pakstr 0.0.4 → 0.1.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/build.gradle.kts +3 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/ExampleInstrumentedTest.kt +8 -3
- package/android-template/app/src/androidTest/java/com/pakstr/app/signer/NostrEventBuilderTest.kt +67 -0
- package/android-template/app/src/androidTest/java/com/pakstr/app/signer/NostrSignatureVerifierTest.kt +43 -0
- package/android-template/app/src/main/AndroidManifest.xml +15 -3
- package/android-template/app/src/main/assets/nip07.js +47 -7
- package/android-template/app/src/main/assets/www/index.html +220 -0
- package/android-template/app/src/main/java/com/pakstr/app/ApplicationClass.kt +2 -0
- package/android-template/app/src/main/java/com/pakstr/app/LocalServer.kt +35 -10
- package/android-template/app/src/main/java/com/pakstr/app/MainActivity.kt +102 -103
- package/android-template/app/src/main/java/com/pakstr/app/ShellRuntime.kt +5 -6
- package/android-template/app/src/main/java/com/pakstr/app/WebViewController.kt +65 -18
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Bip340Verifier.kt +318 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/CryptoInitializer.kt +34 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/NostrEventHasher.kt +100 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/NostrEventVerifier.kt +54 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/NostrSignatureVerifier.kt +43 -0
- package/android-template/app/src/main/java/com/pakstr/app/crypto/Secp256k1.kt +63 -0
- package/android-template/app/src/main/java/com/pakstr/app/debug/AppDebugLogger.kt +21 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/AmberSigner.kt +142 -100
- package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +115 -39
- package/android-template/app/src/main/java/com/pakstr/app/signer/NostrEventBuilder.kt +39 -0
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerManager.kt +83 -75
- package/android-template/app/src/main/java/com/pakstr/app/signer/SignerType.kt +0 -1
- package/android-template/app/src/main/java/com/pakstr/app/signer/exceptions/SignerUnavailableException.kt +5 -0
- package/android-template/app/src/main/res/values/strings.xml +1 -1
- package/package.json +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
package com.pakstr.app
|
|
2
2
|
|
|
3
3
|
import android.Manifest
|
|
4
|
+
import android.content.Intent
|
|
4
5
|
import android.os.Bundle
|
|
5
|
-
|
|
6
|
+
|
|
6
7
|
import android.view.View
|
|
7
8
|
import android.webkit.PermissionRequest
|
|
8
9
|
import android.webkit.WebView
|
|
9
10
|
import android.widget.FrameLayout
|
|
10
11
|
import androidx.activity.OnBackPressedCallback
|
|
11
12
|
import androidx.activity.enableEdgeToEdge
|
|
13
|
+
import androidx.activity.result.ActivityResultLauncher
|
|
12
14
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
13
15
|
import androidx.appcompat.app.AppCompatActivity
|
|
14
16
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|
@@ -17,8 +19,8 @@ import androidx.core.view.WindowInsetsCompat
|
|
|
17
19
|
import androidx.core.view.updatePadding
|
|
18
20
|
import com.pakstr.app.permissions.PermissionManager
|
|
19
21
|
import com.pakstr.app.permissions.interfaces.PermissionHandler
|
|
22
|
+
import com.pakstr.app.crypto.CryptoInitializer
|
|
20
23
|
import com.pakstr.app.signer.SignerManager
|
|
21
|
-
import com.pakstr.app.signer.interfaces.NostrSigner
|
|
22
24
|
import com.pakstr.app.utils.AppPreferences
|
|
23
25
|
import com.pakstr.app.utils.SignerPickerBottomSheet
|
|
24
26
|
|
|
@@ -27,127 +29,111 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
27
29
|
private lateinit var runtime: ShellRuntime
|
|
28
30
|
private lateinit var container: FrameLayout
|
|
29
31
|
private lateinit var webView: WebView
|
|
30
|
-
|
|
31
32
|
private lateinit var loader: View
|
|
32
|
-
|
|
33
33
|
private lateinit var signerManager: SignerManager
|
|
34
|
-
|
|
35
|
-
private var activeSigner: NostrSigner? = null
|
|
36
|
-
|
|
37
|
-
private var pendingPermissionRequest: PermissionRequest? = null
|
|
38
|
-
|
|
39
34
|
private lateinit var permissionManager: PermissionManager
|
|
35
|
+
private lateinit var amberLauncher:
|
|
36
|
+
ActivityResultLauncher<Intent>
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ActivityResultContracts.StartActivityForResult()
|
|
46
|
-
|
|
47
|
-
) { result ->
|
|
48
|
-
|
|
49
|
-
signerManager.handleAmberResult(result)
|
|
50
|
-
|
|
51
|
-
}
|
|
38
|
+
override fun onCreate(
|
|
39
|
+
savedInstanceState: Bundle?
|
|
40
|
+
) {
|
|
52
41
|
|
|
53
|
-
|
|
54
|
-
val splashScreen = installSplashScreen()
|
|
42
|
+
installSplashScreen()
|
|
55
43
|
enableEdgeToEdge()
|
|
56
44
|
|
|
57
45
|
super.onCreate(savedInstanceState)
|
|
58
|
-
setContentView(R.layout.activity_layout)
|
|
59
|
-
|
|
60
|
-
permissionManager = PermissionManager(this)
|
|
61
46
|
|
|
62
|
-
|
|
47
|
+
setContentView(
|
|
48
|
+
R.layout.activity_layout
|
|
49
|
+
)
|
|
63
50
|
|
|
64
|
-
|
|
51
|
+
initViews()
|
|
65
52
|
|
|
66
|
-
|
|
53
|
+
amberLauncher =
|
|
54
|
+
registerForActivityResult(
|
|
55
|
+
ActivityResultContracts.StartActivityForResult()
|
|
56
|
+
) { result ->
|
|
57
|
+
signerManager.handleAmberResult(result)
|
|
58
|
+
}
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
initSigner()
|
|
69
61
|
|
|
62
|
+
if (AppPreferences.getBoolean("isFirstRun", true)) {
|
|
63
|
+
showSignerPicker()
|
|
70
64
|
}
|
|
71
65
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//showTwoOptionsPicker(prefs)
|
|
76
|
-
showSignerPicker()
|
|
77
|
-
//testPush()
|
|
78
|
-
}
|
|
66
|
+
ViewCompat.setOnApplyWindowInsetsListener(
|
|
67
|
+
container
|
|
68
|
+
) { view, insets ->
|
|
79
69
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
val bars =
|
|
71
|
+
insets.getInsets(
|
|
72
|
+
WindowInsetsCompat.Type.systemBars()
|
|
73
|
+
)
|
|
84
74
|
|
|
85
75
|
view.updatePadding(
|
|
86
|
-
top = bars.top,
|
|
76
|
+
top = bars.top,
|
|
77
|
+
bottom = bars.bottom
|
|
87
78
|
)
|
|
88
|
-
|
|
89
79
|
insets
|
|
90
80
|
}
|
|
91
81
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
signerManager,
|
|
97
|
-
permissionHandler = this
|
|
82
|
+
setupRuntime()
|
|
83
|
+
setupBackNavigation()
|
|
84
|
+
showLoader(true)
|
|
85
|
+
runtime.start()
|
|
98
86
|
|
|
99
|
-
|
|
87
|
+
}
|
|
100
88
|
|
|
101
|
-
|
|
89
|
+
private fun setupBackNavigation() {
|
|
90
|
+
onBackPressedDispatcher.addCallback(
|
|
91
|
+
this,
|
|
92
|
+
object : OnBackPressedCallback(true) {
|
|
102
93
|
|
|
103
|
-
|
|
94
|
+
override fun handleOnBackPressed() {
|
|
104
95
|
|
|
105
|
-
|
|
96
|
+
val currentWebView = runtime.getWebview()
|
|
106
97
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
if (currentWebView.canGoBack()) {
|
|
99
|
+
currentWebView.goBack()
|
|
100
|
+
} else {
|
|
101
|
+
finish()
|
|
102
|
+
}
|
|
111
103
|
}
|
|
112
104
|
}
|
|
113
|
-
|
|
114
|
-
})
|
|
115
|
-
loader.visibility = View.VISIBLE
|
|
116
|
-
runtime.start()
|
|
105
|
+
)
|
|
117
106
|
}
|
|
118
107
|
|
|
119
|
-
private fun
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
108
|
+
private fun setupRuntime() {
|
|
109
|
+
runtime =
|
|
110
|
+
ShellRuntime(
|
|
111
|
+
this,
|
|
112
|
+
webView,
|
|
113
|
+
signerManager,
|
|
114
|
+
permissionHandler = this
|
|
115
|
+
)
|
|
123
116
|
}
|
|
124
117
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
118
|
+
private fun initSigner() {
|
|
119
|
+
signerManager =
|
|
120
|
+
SignerManager(
|
|
121
|
+
this,
|
|
122
|
+
amberLauncher
|
|
123
|
+
)
|
|
128
124
|
}
|
|
129
125
|
|
|
126
|
+
private fun initViews() {
|
|
130
127
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
activeSigner = signerManager.getSigner()
|
|
138
|
-
|
|
139
|
-
handleSignerSetup()
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
picker.show(
|
|
144
|
-
|
|
145
|
-
supportFragmentManager,
|
|
146
|
-
|
|
147
|
-
"SignerPicker"
|
|
148
|
-
|
|
149
|
-
)
|
|
128
|
+
container =
|
|
129
|
+
findViewById(R.id.mainContainer)
|
|
130
|
+
webView =
|
|
131
|
+
findViewById(R.id.webView)
|
|
132
|
+
loader =
|
|
133
|
+
findViewById(R.id.loader)
|
|
150
134
|
|
|
135
|
+
permissionManager =
|
|
136
|
+
PermissionManager(this)
|
|
151
137
|
}
|
|
152
138
|
|
|
153
139
|
private fun handleSignerSetup() {
|
|
@@ -157,11 +143,17 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
157
143
|
)
|
|
158
144
|
}
|
|
159
145
|
|
|
146
|
+
override fun onDestroy() {
|
|
147
|
+
runtime.stop()
|
|
148
|
+
super.onDestroy()
|
|
149
|
+
}
|
|
150
|
+
|
|
160
151
|
override fun onRequestPermissionsResult(
|
|
161
152
|
requestCode: Int,
|
|
162
153
|
permissions: Array<out String>,
|
|
163
154
|
grantResults: IntArray
|
|
164
155
|
) {
|
|
156
|
+
|
|
165
157
|
super.onRequestPermissionsResult(
|
|
166
158
|
requestCode,
|
|
167
159
|
permissions,
|
|
@@ -176,33 +168,40 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
|
|
|
176
168
|
override fun onPermissionRequest(
|
|
177
169
|
request: PermissionRequest
|
|
178
170
|
) {
|
|
171
|
+
|
|
179
172
|
permissionManager.request(
|
|
173
|
+
|
|
180
174
|
Manifest.permission.CAMERA
|
|
175
|
+
|
|
181
176
|
) { granted ->
|
|
177
|
+
|
|
182
178
|
if (granted) {
|
|
183
|
-
|
|
179
|
+
|
|
180
|
+
request.grant(
|
|
181
|
+
request.resources
|
|
182
|
+
)
|
|
184
183
|
} else {
|
|
185
184
|
request.deny()
|
|
186
185
|
}
|
|
187
186
|
}
|
|
188
187
|
}
|
|
189
188
|
|
|
190
|
-
private fun
|
|
191
|
-
permissionManager.requestNotificationPermission { granted ->
|
|
192
|
-
|
|
193
|
-
if (granted) {
|
|
194
|
-
Log.d(
|
|
195
|
-
"PERMISSION",
|
|
196
|
-
"Notifications granted"
|
|
197
|
-
)
|
|
198
|
-
} else {
|
|
199
|
-
Log.d(
|
|
200
|
-
"PERMISSION",
|
|
201
|
-
"Notifications denied"
|
|
202
|
-
)
|
|
203
|
-
}
|
|
189
|
+
private fun showSignerPicker() {
|
|
204
190
|
|
|
205
|
-
|
|
191
|
+
SignerPickerBottomSheet { selectedType ->
|
|
192
|
+
signerManager.setActiveType(selectedType)
|
|
193
|
+
handleSignerSetup()
|
|
194
|
+
}.show(
|
|
195
|
+
supportFragmentManager,
|
|
196
|
+
"SignerPicker"
|
|
197
|
+
)
|
|
198
|
+
}
|
|
206
199
|
|
|
200
|
+
private fun showLoader(
|
|
201
|
+
show: Boolean
|
|
202
|
+
) {
|
|
203
|
+
loader.visibility =
|
|
204
|
+
if (show) View.VISIBLE
|
|
205
|
+
else View.GONE
|
|
207
206
|
}
|
|
208
|
-
}
|
|
207
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
package com.pakstr.app
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import
|
|
4
|
+
import com.pakstr.app.debug.AppDebugLogger
|
|
5
5
|
import android.webkit.WebView
|
|
6
6
|
import com.pakstr.app.permissions.interfaces.PermissionHandler
|
|
7
7
|
import com.pakstr.app.signer.SignerManager
|
|
@@ -43,7 +43,7 @@ class ShellRuntime(
|
|
|
43
43
|
|
|
44
44
|
if (!ok) {
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
AppDebugLogger.log(
|
|
47
47
|
"RUNTIME",
|
|
48
48
|
"Server failed"
|
|
49
49
|
)
|
|
@@ -84,7 +84,7 @@ class ShellRuntime(
|
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
AppDebugLogger.log(
|
|
88
88
|
"SERVER",
|
|
89
89
|
"Started OK"
|
|
90
90
|
)
|
|
@@ -96,10 +96,9 @@ class ShellRuntime(
|
|
|
96
96
|
} catch (e: Exception) {
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
AppDebugLogger.log(
|
|
100
100
|
"SERVER",
|
|
101
|
-
"
|
|
102
|
-
e
|
|
101
|
+
"Failed to start: ${e.message}"
|
|
103
102
|
)
|
|
104
103
|
|
|
105
104
|
|
|
@@ -2,7 +2,6 @@ package com.pakstr.app
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.content.Context
|
|
5
|
-
import android.util.Log
|
|
6
5
|
import android.view.View
|
|
7
6
|
import android.webkit.CookieManager
|
|
8
7
|
import android.webkit.PermissionRequest
|
|
@@ -11,11 +10,22 @@ import android.webkit.WebResourceRequest
|
|
|
11
10
|
import android.webkit.WebResourceResponse
|
|
12
11
|
import android.webkit.WebView
|
|
13
12
|
import android.webkit.WebViewClient
|
|
14
|
-
import com.pakstr.app.
|
|
13
|
+
import com.pakstr.app.debug.AppDebugLogger
|
|
15
14
|
import com.pakstr.app.permissions.interfaces.PermissionHandler
|
|
16
15
|
import com.pakstr.app.signer.NostrBridge
|
|
17
16
|
import com.pakstr.app.signer.SignerManager
|
|
18
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Controls WebView configuration and lifecycle.
|
|
20
|
+
*
|
|
21
|
+
* Responsibilities:
|
|
22
|
+
* - Configure secure WebView settings
|
|
23
|
+
* - Load local web application from NanoHTTPD server
|
|
24
|
+
* - Inject NIP-07 JavaScript provider
|
|
25
|
+
* - Expose Android signer bridge to JavaScript
|
|
26
|
+
* - Handle Web permissions
|
|
27
|
+
* - Cleanup WebView resources
|
|
28
|
+
*/
|
|
19
29
|
class WebViewController(
|
|
20
30
|
|
|
21
31
|
private val context: Context,
|
|
@@ -35,6 +45,8 @@ class WebViewController(
|
|
|
35
45
|
//webView.loadUrl("https://sigit.io")
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
private var nip07Injected = false
|
|
49
|
+
|
|
38
50
|
fun setup() {
|
|
39
51
|
showLoader()
|
|
40
52
|
setupWebView()
|
|
@@ -54,21 +66,30 @@ class WebViewController(
|
|
|
54
66
|
|
|
55
67
|
allowFileAccess = false
|
|
56
68
|
allowContentAccess = false
|
|
69
|
+
|
|
70
|
+
mediaPlaybackRequiresUserGesture = false
|
|
71
|
+
builtInZoomControls = false
|
|
72
|
+
displayZoomControls = false
|
|
73
|
+
|
|
74
|
+
mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_NEVER_ALLOW
|
|
57
75
|
}
|
|
58
76
|
|
|
59
77
|
// Standard clients are enough since overriding URL loading isn't required
|
|
60
78
|
webViewClient = object : WebViewClient() {
|
|
61
79
|
|
|
62
|
-
override fun onPageFinished(
|
|
80
|
+
override fun onPageFinished(
|
|
81
|
+
view: WebView?,
|
|
82
|
+
url: String?
|
|
83
|
+
) {
|
|
63
84
|
super.onPageFinished(view, url)
|
|
64
|
-
hideLoader()
|
|
65
|
-
val js = context.assets.open("nip07.js")
|
|
66
85
|
|
|
67
|
-
|
|
86
|
+
hideLoader()
|
|
68
87
|
|
|
69
|
-
|
|
88
|
+
if (!nip07Injected) {
|
|
89
|
+
nip07Injected =
|
|
90
|
+
injectNip07()
|
|
70
91
|
|
|
71
|
-
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
94
|
|
|
74
95
|
override fun shouldInterceptRequest(
|
|
@@ -79,12 +100,16 @@ class WebViewController(
|
|
|
79
100
|
|
|
80
101
|
): WebResourceResponse? {
|
|
81
102
|
|
|
82
|
-
val url = request?.url.toString()
|
|
83
|
-
|
|
84
|
-
Log.d("WEB_ASSET", "REQUEST: $url")
|
|
85
|
-
|
|
86
103
|
return super.shouldInterceptRequest(view, request)
|
|
87
104
|
}
|
|
105
|
+
|
|
106
|
+
override fun shouldOverrideUrlLoading(
|
|
107
|
+
view: WebView?,
|
|
108
|
+
request: WebResourceRequest?
|
|
109
|
+
): Boolean {
|
|
110
|
+
val url = request?.url ?: return true
|
|
111
|
+
return url.host != "127.0.0.1"
|
|
112
|
+
}
|
|
88
113
|
}
|
|
89
114
|
webChromeClient = object : WebChromeClient() {
|
|
90
115
|
|
|
@@ -96,6 +121,8 @@ class WebViewController(
|
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
|
|
124
|
+
// Exposes native Nostr signer APIs to the WebView.
|
|
125
|
+
// Available in JavaScript as window.AndroidBridge.
|
|
99
126
|
webView.addJavascriptInterface(
|
|
100
127
|
|
|
101
128
|
NostrBridge(
|
|
@@ -130,13 +157,10 @@ class WebViewController(
|
|
|
130
157
|
fun destroy() {
|
|
131
158
|
webView.stopLoading()
|
|
132
159
|
|
|
133
|
-
webView.clearCache(true)
|
|
134
|
-
|
|
135
160
|
webView.clearHistory()
|
|
136
|
-
|
|
137
|
-
CookieManager.getInstance()
|
|
138
|
-
|
|
139
|
-
CookieManager.getInstance().flush()
|
|
161
|
+
webView.clearCache(false)
|
|
162
|
+
CookieManager.getInstance()
|
|
163
|
+
.flush()
|
|
140
164
|
|
|
141
165
|
webView.loadUrl("about:blank")
|
|
142
166
|
webView.removeCallbacks(loadRunnable)
|
|
@@ -145,4 +169,27 @@ class WebViewController(
|
|
|
145
169
|
|
|
146
170
|
webView.destroy()
|
|
147
171
|
}
|
|
172
|
+
|
|
173
|
+
private fun injectNip07(): Boolean {
|
|
174
|
+
|
|
175
|
+
return try {
|
|
176
|
+
val js =
|
|
177
|
+
context.assets
|
|
178
|
+
.open("nip07.js")
|
|
179
|
+
.bufferedReader()
|
|
180
|
+
.readText()
|
|
181
|
+
|
|
182
|
+
webView.evaluateJavascript(
|
|
183
|
+
js,
|
|
184
|
+
null
|
|
185
|
+
)
|
|
186
|
+
true
|
|
187
|
+
} catch (e: Exception) {
|
|
188
|
+
AppDebugLogger.log(
|
|
189
|
+
"WEBVIEW",
|
|
190
|
+
"Failed loading nip07.js: ${e.message}"
|
|
191
|
+
)
|
|
192
|
+
false
|
|
193
|
+
}
|
|
194
|
+
}
|
|
148
195
|
}
|