pakstr 0.4.0 → 0.6.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.
Files changed (29) hide show
  1. package/android-template/app/build.gradle.kts +71 -4
  2. package/android-template/app/proguard-rules.pro +21 -18
  3. package/android-template/app/src/debug/AndroidManifest.xml +11 -0
  4. package/android-template/app/src/main/AndroidManifest.xml +6 -0
  5. package/android-template/app/src/main/java/com/pakstr/app/ApplicationClass.kt +14 -0
  6. package/android-template/app/src/main/java/com/pakstr/app/MainActivity.kt +55 -5
  7. package/android-template/app/src/main/java/com/pakstr/app/ShellRuntime.kt +1 -1
  8. package/android-template/app/src/main/java/com/pakstr/app/WebViewController.kt +135 -0
  9. package/android-template/app/src/main/java/com/pakstr/app/debug/AppDebugLogger.kt +78 -5
  10. package/android-template/app/src/main/java/com/pakstr/app/debug/CrashHandler.kt +32 -0
  11. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugConfigProvider.kt +8 -3
  12. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugLogStorage.kt +43 -11
  13. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugPreferences.kt +85 -0
  14. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugReportExporter.kt +42 -32
  15. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugRuntimeOverride.kt +30 -4
  16. package/android-template/app/src/main/java/com/pakstr/app/debug/DebugSession.kt +16 -0
  17. package/android-template/app/src/main/java/com/pakstr/app/debug/DeveloperToolsActivity.kt +234 -0
  18. package/android-template/app/src/main/java/com/pakstr/app/debug/DeveloperToolsManager.kt +42 -13
  19. package/android-template/app/src/main/java/com/pakstr/app/debug/WebViewDebugManager.kt +30 -0
  20. package/android-template/app/src/main/java/com/pakstr/app/debug/logging/PakstrLog.kt +16 -0
  21. package/android-template/app/src/main/java/com/pakstr/app/debug/logging/PakstrLogger.kt +154 -0
  22. package/android-template/app/src/main/java/com/pakstr/app/signer/NostrBridge.kt +1 -1
  23. package/android-template/app/src/main/res/layout/activity_developer_tools.xml +308 -0
  24. package/android-template/app/src/main/res/layout/activity_layout.xml +9 -0
  25. package/android-template/app/src/main/res/values/dimes.xml +1 -0
  26. package/android-template/app/src/main/res/values/strings.xml +43 -0
  27. package/android-template/gradle/libs.versions.toml +1 -0
  28. package/android-template/gradle.properties +2 -1
  29. package/package.json +1 -1
@@ -1,12 +1,59 @@
1
+ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2
+ import java.util.Properties
3
+ import java.io.File
4
+
1
5
  plugins {
2
6
  alias(libs.plugins.android.application)
3
7
  alias(libs.plugins.kotlin.compose)
4
8
  }
5
9
 
10
+ val keystoreProperties =
11
+
12
+ Properties()
13
+
14
+ val keystorePath =
15
+ providers.gradleProperty(
16
+ "releaseKeystoreConfig"
17
+ ).orNull
18
+
19
+ val keystorePropertiesFile =
20
+ keystorePath?.let { rootProject.file(it) }
21
+ if (keystorePropertiesFile?.exists() == true) {
22
+
23
+ keystorePropertiesFile.inputStream()
24
+ .use {
25
+ keystoreProperties.load(it)
26
+ }
27
+ }
28
+
6
29
  android {
7
30
  namespace = "com.pakstr.app"
8
31
  compileSdk = 36
9
32
 
33
+ signingConfigs {
34
+
35
+ create("release") {
36
+
37
+ if (keystorePropertiesFile?.exists() == true) {
38
+
39
+ storeFile =
40
+ File(
41
+ keystorePropertiesFile.parentFile,
42
+ keystoreProperties["storeFile"] as String
43
+ )
44
+
45
+ storePassword =
46
+ keystoreProperties["storePassword"] as String
47
+
48
+ keyAlias =
49
+ keystoreProperties["keyAlias"] as String
50
+
51
+ keyPassword =
52
+ keystoreProperties["keyPassword"] as String
53
+ }
54
+ }
55
+ }
56
+
10
57
  defaultConfig {
11
58
  applicationId = "com.pakstr.app"
12
59
  minSdk = 28
@@ -18,23 +65,43 @@ android {
18
65
  }
19
66
 
20
67
  buildTypes {
68
+
69
+ debug {
70
+ applicationIdSuffix = ".debug"
71
+ versionNameSuffix = "-debug"
72
+ }
73
+
21
74
  release {
22
- isMinifyEnabled = false
75
+ signingConfig =
76
+ signingConfigs.getByName("release")
77
+ isMinifyEnabled = true
78
+ isShrinkResources = true
23
79
  proguardFiles(
24
- getDefaultProguardFile("proguard-android-optimize.txt"),
80
+ getDefaultProguardFile(
81
+ "proguard-android-optimize.txt"
82
+ ),
25
83
  "proguard-rules.pro"
26
84
  )
27
85
  }
28
86
  }
29
87
  compileOptions {
30
- sourceCompatibility = JavaVersion.VERSION_11
31
- targetCompatibility = JavaVersion.VERSION_11
88
+ sourceCompatibility = JavaVersion.VERSION_17
89
+ targetCompatibility = JavaVersion.VERSION_17
32
90
  }
91
+
33
92
  buildFeatures {
34
93
  compose = true
35
94
  buildConfig = true
36
95
  }
37
96
  }
97
+ tasks.withType<KotlinCompile>()
98
+ .configureEach {
99
+ compilerOptions {
100
+ jvmTarget.set(
101
+ org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
102
+ )
103
+ }
104
+ }
38
105
 
39
106
  dependencies {
40
107
  implementation(libs.androidx.core.ktx)
@@ -1,21 +1,24 @@
1
- # Add project specific ProGuard rules here.
2
- # You can control the set of applied configuration files using the
3
- # proguardFiles setting in build.gradle.
4
- #
5
- # For more details, see
6
- # http://developer.android.com/guide/developing/tools/proguard.html
1
+ # JavaScript bridge
2
+ -keepclassmembers class * {
3
+ @android.webkit.JavascriptInterface <methods>;
4
+ }
7
5
 
8
- # If your project uses WebView with JS, uncomment the following
9
- # and specify the fully qualified class name to the JavaScript interface
10
- # class:
11
- #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12
- # public *;
13
- #}
6
+ # Nostr signer
7
+ -keep class com.pakstr.app.signer.** { *; }
14
8
 
15
- # Uncomment this to preserve the line number information for
16
- # debugging stack traces.
17
- #-keepattributes SourceFile,LineNumberTable
9
+ # Crypto
10
+ -keep class com.pakstr.app.crypto.** { *; }
18
11
 
19
- # If you keep the line number information, uncomment this to
20
- # hide the original source file name.
21
- #-renamesourcefileattribute SourceFile
12
+ # BouncyCastle
13
+ -keep class org.bouncycastle.crypto.** { *; }
14
+ -keep class org.bouncycastle.math.** { *; }
15
+ -keep class org.bouncycastle.asn1.** { *; }
16
+
17
+ # Ignore unavailable Java SE classes
18
+ -dontwarn javax.naming.**
19
+
20
+ # Keep Application class
21
+ -keep class com.pakstr.app.ApplicationClass { *; }
22
+
23
+ # Keep model classes used through JSON
24
+ -keep class com.pakstr.app.signer.** { *; }
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <application>
4
+ <activity-alias
5
+ android:name=".debug.DeveloperToolsDebugLauncher"
6
+ android:enabled="true"
7
+ android:exported="true"
8
+ android:label="@string/developer_tools_title"
9
+ android:targetActivity=".debug.DeveloperToolsActivity" />
10
+ </application>
11
+ </manifest>
@@ -28,6 +28,12 @@
28
28
  android:supportsRtl="true"
29
29
  android:usesCleartextTraffic="true">
30
30
 
31
+ <activity
32
+ android:name=".debug.DeveloperToolsActivity"
33
+ android:exported="false"
34
+ android:label="@string/developer_tools_title"
35
+ android:theme="@style/Theme.NostrAppShell" />
36
+
31
37
  <activity
32
38
  android:name=".MainActivity"
33
39
  android:exported="true"
@@ -2,6 +2,9 @@ package com.pakstr.app
2
2
 
3
3
  import android.app.Application
4
4
  import com.pakstr.app.crypto.CryptoInitializer
5
+ import com.pakstr.app.debug.AppDebugLogger
6
+ import com.pakstr.app.debug.CrashHandler
7
+ import com.pakstr.app.debug.DebugSession
5
8
  import com.pakstr.app.utils.AppPreferences
6
9
 
7
10
  class ApplicationClass : Application() {
@@ -9,5 +12,16 @@ class ApplicationClass : Application() {
9
12
  super.onCreate()
10
13
  AppPreferences.init(this)
11
14
  CryptoInitializer.init()
15
+ CrashHandler.install(this)
16
+
17
+ AppDebugLogger.log(
18
+
19
+ this,
20
+
21
+ "SESSION",
22
+
23
+ "Session started: ${DebugSession.sessionId}"
24
+
25
+ )
12
26
  }
13
27
  }
@@ -17,10 +17,14 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
17
17
  import androidx.core.view.ViewCompat
18
18
  import androidx.core.view.WindowInsetsCompat
19
19
  import androidx.core.view.updatePadding
20
+ import com.google.android.material.floatingactionbutton.FloatingActionButton
20
21
  import com.pakstr.app.debug.AppDebugLogger
22
+ import com.pakstr.app.debug.CrashHandler
21
23
 
22
24
  import com.pakstr.app.debug.DebugReportShare
25
+ import com.pakstr.app.debug.DeveloperToolsActivity
23
26
  import com.pakstr.app.debug.DeveloperToolsManager
27
+ import com.pakstr.app.debug.WebViewDebugManager
24
28
 
25
29
  import com.pakstr.app.permissions.PermissionManager
26
30
  import com.pakstr.app.permissions.interfaces.PermissionHandler
@@ -41,6 +45,8 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
41
45
  private lateinit var amberLauncher:
42
46
  ActivityResultLauncher<Intent>
43
47
 
48
+ private lateinit var fab: FloatingActionButton
49
+
44
50
  override fun onCreate(
45
51
 
46
52
  savedInstanceState: Bundle?
@@ -52,21 +58,32 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
52
58
  enableEdgeToEdge()
53
59
 
54
60
  super.onCreate(savedInstanceState)
61
+ if (BuildConfig.DEBUG) {
62
+
63
+ DeveloperToolsManager.enableDebug(this)
64
+
65
+ }
66
+
67
+ WebViewDebugManager.enable(this)
68
+
55
69
  AppDebugLogger.log(
56
70
 
57
71
  this,
58
72
 
59
- "SESSION",
73
+ "TEST",
60
74
 
61
- "========== APP START =========="
75
+ "Developer debug enabled"
62
76
 
63
77
  )
78
+ AppDebugLogger.log(
64
79
 
65
- if (BuildConfig.DEBUG) {
80
+ this,
66
81
 
67
- WebView.setWebContentsDebuggingEnabled(true)
82
+ "SESSION",
68
83
 
69
- }
84
+ "========== APP START =========="
85
+
86
+ )
70
87
 
71
88
  setContentView(
72
89
 
@@ -75,6 +92,7 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
75
92
  )
76
93
 
77
94
  initViews()
95
+ showDebugFab()
78
96
 
79
97
  amberLauncher =
80
98
 
@@ -132,6 +150,34 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
132
150
 
133
151
  }
134
152
 
153
+ private fun showDebugFab() {
154
+ if (BuildConfig.DEBUG || RuntimeConfig.debugEnabled(this)) {
155
+
156
+ fab.visibility = View.VISIBLE
157
+
158
+ fab.setOnClickListener {
159
+
160
+ startActivity(
161
+
162
+ Intent(
163
+
164
+ this,
165
+
166
+ DeveloperToolsActivity::class.java
167
+
168
+ )
169
+
170
+ )
171
+
172
+ }
173
+
174
+ } else {
175
+
176
+ fab.visibility = View.GONE
177
+
178
+ }
179
+ }
180
+
135
181
  private fun setupBackNavigation() {
136
182
  onBackPressedDispatcher.addCallback(
137
183
  this,
@@ -184,6 +230,10 @@ class MainActivity : AppCompatActivity(), PermissionHandler {
184
230
  permissionManager =
185
231
  PermissionManager(this)
186
232
 
233
+ fab = findViewById(
234
+
235
+ R.id.developerToolsFab)
236
+
187
237
  }
188
238
 
189
239
  private fun handleSignerSetup() {
@@ -97,7 +97,7 @@ class ShellRuntime(
97
97
  }
98
98
 
99
99
  }
100
-
100
+ AppDebugLogger.load(context)
101
101
  }
102
102
 
103
103
  private fun startServer(): Boolean {
@@ -95,6 +95,7 @@ class WebViewController(
95
95
  nip07Injected = injectNip07()
96
96
 
97
97
  }
98
+ injectDebugLogger()
98
99
  }
99
100
 
100
101
  override fun onReceivedHttpError(
@@ -343,4 +344,138 @@ class WebViewController(
343
344
  false
344
345
  }
345
346
  }
347
+
348
+ private fun injectDebugLogger() {
349
+
350
+ val js = """
351
+ (function(){
352
+
353
+ if (!window.AndroidBridge) {
354
+ return;
355
+ }
356
+
357
+
358
+ const oldLog = console.log;
359
+ const oldWarn = console.warn;
360
+ const oldError = console.error;
361
+
362
+
363
+ function send(level, args){
364
+
365
+ try {
366
+
367
+ const message =
368
+ Array.from(args)
369
+ .map(item => {
370
+
371
+ if(typeof item === 'object'){
372
+ return JSON.stringify(item);
373
+ }
374
+
375
+ return String(item);
376
+
377
+ })
378
+ .join(" ");
379
+
380
+ window.AndroidBridge.debugLog(
381
+ level,
382
+ message
383
+ );
384
+
385
+ } catch(e){
386
+
387
+ }
388
+ }
389
+
390
+
391
+ console.log = function(){
392
+
393
+ send(
394
+ "LOG",
395
+ arguments
396
+ );
397
+
398
+ oldLog.apply(
399
+ console,
400
+ arguments
401
+ );
402
+ };
403
+
404
+
405
+ console.warn = function(){
406
+
407
+ send(
408
+ "WARN",
409
+ arguments
410
+ );
411
+
412
+ oldWarn.apply(
413
+ console,
414
+ arguments
415
+ );
416
+ };
417
+
418
+
419
+ console.error = function(){
420
+
421
+ send(
422
+ "ERROR",
423
+ arguments
424
+ );
425
+
426
+ oldError.apply(
427
+ console,
428
+ arguments
429
+ );
430
+ };
431
+
432
+
433
+ window.onerror =
434
+ function(
435
+ message,
436
+ source,
437
+ line,
438
+ column,
439
+ error
440
+ ){
441
+
442
+ send(
443
+ "ERROR",
444
+ [
445
+ "JS ERROR:",
446
+ message,
447
+ source,
448
+ line,
449
+ column,
450
+ error?.stack
451
+ ]
452
+ );
453
+
454
+ return false;
455
+ };
456
+
457
+
458
+ window.onunhandledrejection =
459
+ function(event){
460
+
461
+ send(
462
+ "ERROR",
463
+ [
464
+ "PROMISE ERROR:",
465
+ event.reason
466
+ ]
467
+ );
468
+
469
+ };
470
+
471
+
472
+ })();
473
+ """.trimIndent()
474
+
475
+
476
+ webView.evaluateJavascript(
477
+ js,
478
+ null
479
+ )
480
+ }
346
481
  }
@@ -5,6 +5,7 @@ import android.util.Log
5
5
  import com.pakstr.app.BuildConfig
6
6
  import com.pakstr.app.RuntimeConfig
7
7
  import java.text.SimpleDateFormat
8
+ import java.util.Collections
8
9
  import java.util.Date
9
10
  import java.util.Locale
10
11
 
@@ -15,7 +16,12 @@ object AppDebugLogger {
15
16
  private const val MAX_LOGS = 500
16
17
 
17
18
  private val logs =
18
- mutableListOf<String>()
19
+
20
+ Collections.synchronizedList(
21
+
22
+ mutableListOf<String>()
23
+
24
+ )
19
25
 
20
26
  fun log(
21
27
  context: Context,
@@ -108,10 +114,14 @@ object AppDebugLogger {
108
114
 
109
115
  }
110
116
 
111
- fun clear() {
117
+ fun clear(
118
+ context: Context
119
+ ) {
112
120
 
113
121
  logs.clear()
114
122
 
123
+ DebugLogStorage.clear(context)
124
+
115
125
  }
116
126
 
117
127
  private fun isDebugEnabled(
@@ -280,14 +290,77 @@ object AppDebugLogger {
280
290
  }
281
291
 
282
292
  private fun config(
293
+ context: Context
294
+ ): DebugConfig {
295
+
296
+
297
+ val runtime =
298
+ DebugRuntimeOverride.get(context)
299
+
300
+
301
+ if (runtime != null) {
302
+ return runtime
303
+ }
304
+
305
+
306
+ val stored =
307
+ DebugPreferences.load(
308
+ context
309
+ )
310
+
311
+
312
+ if (stored.enabled) {
313
+ return stored
314
+ }
315
+
316
+
317
+ return RuntimeConfig.debugConfig(
318
+ context
319
+ )
320
+
321
+ }
283
322
 
323
+ fun exportLogs(
284
324
  context: Context
325
+ ): String {
285
326
 
286
- ): DebugConfig {
327
+ return buildString {
328
+
329
+ appendLine(
330
+ "===== PAKSTR DEBUG LOG ====="
331
+ )
332
+
333
+ appendLine(
334
+ "Version: ${BuildConfig.VERSION_NAME}"
335
+ )
336
+
337
+ appendLine(
338
+ "Debug: ${isDebugEnabled(context)}"
339
+ )
340
+
341
+ appendLine()
342
+
343
+
344
+ getLogs().forEach {
345
+ appendLine(it)
346
+ }
287
347
 
288
- return DebugRuntimeOverride.get()
348
+ }
349
+ }
350
+
351
+ fun load(
352
+
353
+ context: Context
354
+
355
+ ) {
356
+
357
+ logs.clear()
289
358
 
290
- ?: RuntimeConfig.debugConfig(context)
359
+ logs.addAll(
360
+
361
+ DebugLogStorage.read(context)
362
+
363
+ )
291
364
 
292
365
  }
293
366
  }
@@ -0,0 +1,32 @@
1
+ package com.pakstr.app.debug
2
+
3
+ import android.content.Context
4
+
5
+ object CrashHandler {
6
+
7
+ fun install(
8
+ context: Context
9
+ ) {
10
+
11
+ val defaultHandler =
12
+ Thread.getDefaultUncaughtExceptionHandler()
13
+
14
+
15
+ Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
16
+
17
+
18
+ AppDebugLogger.error(
19
+ context,
20
+ "CRASH",
21
+ "Unhandled exception in ${thread.name}",
22
+ throwable
23
+ )
24
+
25
+
26
+ defaultHandler?.uncaughtException(
27
+ thread,
28
+ throwable
29
+ )
30
+ }
31
+ }
32
+ }
@@ -1,10 +1,13 @@
1
1
  package com.pakstr.app.debug
2
2
 
3
+ import android.content.Context
4
+
3
5
  object DebugConfigProvider {
4
6
 
5
7
  private var manifestConfig =
6
8
  DebugConfig()
7
9
 
10
+
8
11
  fun initialize(
9
12
  config: DebugConfig
10
13
  ) {
@@ -13,10 +16,12 @@ object DebugConfigProvider {
13
16
 
14
17
  }
15
18
 
16
- fun current():
17
- DebugConfig {
18
19
 
19
- return DebugRuntimeOverride.get()
20
+ fun current(
21
+ context: Context
22
+ ): DebugConfig {
23
+
24
+ return DebugRuntimeOverride.get(context)
20
25
  ?: manifestConfig
21
26
 
22
27
  }