catalyst-core 0.0.3-canary.4 → 0.0.3-canary.5

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 (24) hide show
  1. package/README.md +2 -2
  2. package/changelog.md +12 -21
  3. package/dist/native/androidProject/app/build.gradle.kts +126 -5
  4. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -2
  5. package/dist/native/androidProject/app/src/main/assets/webview_config.properties +4 -2
  6. package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/CustomWebview.kt +96 -45
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +162 -0
  8. package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/NativeBridge.kt +122 -61
  9. package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/WebCacheManager.kt +85 -28
  10. package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +1 -2
  11. package/dist/native/androidProject/gradle/libs.versions.toml +1 -1
  12. package/dist/native/androidProject/keystore/Untitled +0 -0
  13. package/dist/native/androidProject/keystore/release-key.jks +0 -0
  14. package/dist/native/buildAppAndroid.js +26 -20
  15. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcuserdata/mayankmahavar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  16. package/dist/native/renameAndroidProject.js +76 -0
  17. package/dist/server/renderer/document/Head.js +1 -1
  18. package/dist/server/renderer/extract.js +3 -3
  19. package/dist/server/renderer/handler.js +9 -9
  20. package/package.json +1 -1
  21. package/transform-config.json +35 -0
  22. package/dist/native/androidProject/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt +0 -24
  23. package/dist/native/androidProject/app/src/main/java/com/example/myapplication/MainActivity.kt +0 -115
  24. package/dist/native/androidProject/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt +0 -17
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  - Overview
11
11
  - Installation
12
-
12
+
13
13
  ## Overview
14
14
 
15
15
  Catalyst offers a comprehensive suite of features designed for modern web development. It includes isomorphic rendering for optimal performance, an extendable server with full-stack capabilities, and configurable state management. The framework employs smart prefetching of data and chunks, allows easy configuration of global styles and layouts, and provides SEO optimization at both global and page levels.
@@ -38,7 +38,7 @@ You will see some prompts to configure your application.
38
38
  cd <name-of-your-project> && npm run start
39
39
  ```
40
40
 
41
- The development server will start at ```http://localhost:3005```
41
+ The development server will start at `http://localhost:3005`
42
42
 
43
43
  ## Documentation
44
44
 
package/changelog.md CHANGED
@@ -1,29 +1,20 @@
1
1
  # Changelog
2
2
 
3
- ## Build Optimizations
3
+
4
+ ## [0.0.3-canary.5] - 12-06-2025
4
5
 
5
6
  ### Features
6
- - Integrated hardware acceleration and thread management for Android
7
- - Added build optimizations with static build option
8
- - Implemented custom caching mechanism for both Android and iOS platforms
9
- - Added buffer size optimization for universal app
7
+ - Production project creation functionality implemented
8
+ - Android App Bundle (.aab) file generation capability
9
+ - Project name replacement functionality
10
10
 
11
11
  ### Fixed
12
- - Fixed null check issue in core functionality
13
- - Resolved catalyst-core issues
14
- - Fixed buffer size problems
15
- - Addressed cache patterns and overwrite issues
16
- - Fixed compatibility issues with older Android OS versions
17
- - Resolved disk memory persistence problems
18
- - Fixed infinite loading issues
19
- - Addressed issues with iOS WebView caching
20
-
21
- ### Other Changes
22
- - Updated build and project configuration files
23
- - Improved developer experience for Android and iOS setup
24
- - Added debugging flags and tools
25
- - Updated documentation and configuration generators
26
- - Cleaned up code and removed redundant elements
12
+ - Production deployment issues resolved
13
+ - Merge conflicts fixed
14
+ - Branch integration improvements
27
15
 
16
+ ### Changes
17
+ - Enhanced production deployment workflow for Android
18
+ - Improved build process for production releases
28
19
  ## Version
29
- - Target version: 0.0.3-canary.4
20
+ - Target version: 0.0.3-canary.5
@@ -3,6 +3,9 @@ import java.io.File
3
3
  import java.util.Properties
4
4
 
5
5
  val configPath: String? by project.properties
6
+ val keystorePassword: String? by project.properties // Changed from keyStorePassword to keystorePassword
7
+ val keyAlias: String? by project.properties
8
+ val keyPassword: String? by project.properties
6
9
 
7
10
  fun getLocalIpAddress(): String {
8
11
  return NetworkInterface.getNetworkInterfaces().toList()
@@ -18,35 +21,73 @@ plugins {
18
21
  }
19
22
 
20
23
  android {
21
- namespace = "com.example.androidProject"
24
+ namespace = "io.yourname.androidproject"
22
25
  compileSdk = 34
23
26
 
24
27
  defaultConfig {
25
- applicationId = "com.example.androidProject"
28
+ applicationId = "io.yourname.androidproject"
26
29
  minSdk = 24
27
30
  targetSdk = 34
28
31
  versionCode = 1
29
- versionName = "1.0"
32
+ versionName = "1.1"
30
33
  testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
31
34
  buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
32
35
  }
33
36
 
37
+ // Add signing configuration for app bundle
38
+ signingConfigs {
39
+ create("release") {
40
+ // Make sure the keystore file exists before referencing it
41
+ val keystoreFile = file("../keystore/release-key.jks")
42
+ if (keystoreFile.exists()) {
43
+ storeFile = file("../keystore/release-key.jks")
44
+ storePassword = "test@123"
45
+ keyAlias = "release"
46
+ keyPassword = "test@123"
47
+ } else {
48
+ // Log a warning if the keystore doesn't exist yet
49
+ logger.warn("Keystore file not found at ${keystoreFile.absolutePath}. Run the generateKeystore task first.")
50
+ }
51
+ }
52
+ }
53
+
34
54
  buildTypes {
35
55
  debug {
36
56
  manifestPlaceholders += mapOf("allowCleartextTraffic" to true)
57
+ applicationIdSuffix = ".debug"
58
+ versionNameSuffix = "-debug"
37
59
  isMinifyEnabled = false
38
60
  buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "true")
39
61
  buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
40
62
  }
41
63
  release {
42
64
  manifestPlaceholders += mapOf("allowCleartextTraffic" to false)
43
- isMinifyEnabled = false
65
+ isMinifyEnabled = true
66
+ isShrinkResources = true
44
67
  proguardFiles(
45
68
  getDefaultProguardFile("proguard-android-optimize.txt"),
46
69
  "proguard-rules.pro"
47
70
  )
48
71
  buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "false")
49
72
  buildConfigField("String", "LOCAL_IP", "\"127.0.0.1\"")
73
+
74
+ // Only apply signing config if the keystore exists
75
+ if (file("../keystore/release-key.jks").exists()) {
76
+ signingConfig = signingConfigs.getByName("release")
77
+ }
78
+ }
79
+ }
80
+
81
+ // Configure App Bundle settings
82
+ bundle {
83
+ language {
84
+ enableSplit = true
85
+ }
86
+ density {
87
+ enableSplit = true
88
+ }
89
+ abi {
90
+ enableSplit = true
50
91
  }
51
92
  }
52
93
 
@@ -68,8 +109,16 @@ android {
68
109
  resources {
69
110
  excludes.add("**/route-manifest.json.gz")
70
111
  excludes.add("**/route-manifest.json.br")
112
+ excludes.add("META-INF/LICENSE")
113
+ excludes.add("META-INF/NOTICE")
71
114
  }
72
115
  }
116
+
117
+ // Configure lint options
118
+ lint {
119
+ checkReleaseBuilds = true
120
+ abortOnError = true
121
+ }
73
122
  }
74
123
 
75
124
  dependencies {
@@ -143,7 +192,17 @@ tasks.register("generateWebViewConfig") {
143
192
  val webviewConfigContent = webviewConfigMatch?.groupValues?.get(1)
144
193
 
145
194
  val properties = Properties()
146
- properties.setProperty("LOCAL_IP", getLocalIpAddress())
195
+
196
+ // Set different IP based on build type
197
+ if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
198
+ properties.setProperty("LOCAL_IP", "127.0.0.1") // Production
199
+ properties.setProperty("buildType", "release")
200
+ properties.setProperty("buildOptimisation", "true")
201
+ } else {
202
+ properties.setProperty("LOCAL_IP", getLocalIpAddress()) // Debug
203
+ properties.setProperty("buildType", "debug")
204
+ properties.setProperty("buildOptimisation", "false")
205
+ }
147
206
 
148
207
  // Parse top-level properties
149
208
  val topLevelRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
@@ -170,6 +229,12 @@ tasks.register("generateWebViewConfig") {
170
229
  }
171
230
  }
172
231
 
232
+ // Set production-specific properties for release builds
233
+ if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
234
+ properties.setProperty("PRODUCTION_URL", "https://yourwebapp.com") // Replace with your domain
235
+ properties.setProperty("apiBaseUrl", "https://api.yourdomain.com/") // Replace with your API URL
236
+ }
237
+
173
238
  // Create the assets directory if it doesn't exist
174
239
  val assetsDir = File("${project.projectDir}/src/main/assets")
175
240
  if (!assetsDir.exists()) {
@@ -184,4 +249,60 @@ tasks.register("generateWebViewConfig") {
184
249
 
185
250
  println("WebView config generated at ${assetsDir.absolutePath}/webview_config.properties")
186
251
  }
252
+ }
253
+
254
+ // Task to create key store if it doesn't exist
255
+ // Add this task to your build.gradle.kts
256
+ tasks.register("generateKeystore") {
257
+ doLast {
258
+ val keystoreDir = File(project.rootDir, "keystore")
259
+ if (!keystoreDir.exists()) {
260
+ keystoreDir.mkdirs()
261
+ }
262
+
263
+ val keystoreFile = File(keystoreDir, "release-key.jks")
264
+ if (!keystoreFile.exists()) {
265
+ val storePass = project.properties["keystorePassword"] as? String ?: System.getenv("KEYSTORE_PASSWORD") ?: "android"
266
+ val keyPass = project.properties["keyPassword"] as? String ?: System.getenv("KEY_PASSWORD") ?: "android"
267
+ val alias = project.properties["keyAlias"] as? String ?: System.getenv("KEY_ALIAS") ?: "release"
268
+
269
+ exec {
270
+ commandLine = listOf(
271
+ "keytool",
272
+ "-genkey",
273
+ "-v",
274
+ "-keystore", keystoreFile.absolutePath,
275
+ "-alias", alias,
276
+ "-keyalg", "RSA",
277
+ "-keysize", "2048",
278
+ "-validity", "10000",
279
+ "-storepass", storePass,
280
+ "-keypass", keyPass,
281
+ "-dname", "CN=YourCompany, OU=YourDepartment, O=YourOrganization, L=YourCity, ST=YourState, C=US"
282
+ )
283
+ }
284
+ println("Generated keystore at: ${keystoreFile.absolutePath}")
285
+ } else {
286
+ println("Keystore already exists at: ${keystoreFile.absolutePath}")
287
+ }
288
+ }
289
+ }
290
+ // Task to build app bundle
291
+ tasks.register("createAppBundle") {
292
+ dependsOn("generateKeystore", "bundleRelease")
293
+ doLast {
294
+ println("""
295
+ =======================================================
296
+ App Bundle created successfully!
297
+
298
+ Location: ${project.buildDir}/outputs/bundle/release/app-release.aab
299
+
300
+ Next steps:
301
+ 1. Test your bundle with:
302
+ bundletool build-apks --bundle=app/build/outputs/bundle/release/app-release.aab --output=test.apks
303
+
304
+ 2. Upload to Play Console: https://play.google.com/console
305
+ =======================================================
306
+ """.trimIndent())
307
+ }
187
308
  }
@@ -24,7 +24,7 @@
24
24
  android:usesCleartextTraffic="${allowCleartextTraffic}"
25
25
  tools:targetApi="31">
26
26
  <activity
27
- android:name=".MainActivity"
27
+ android:name="io.yourname.androidproject.MainActivity"
28
28
  android:exported="true">
29
29
  <intent-filter>
30
30
  <action android:name="android.intent.action.MAIN" />
@@ -34,7 +34,7 @@
34
34
  </activity>
35
35
  <provider
36
36
  android:name="androidx.core.content.FileProvider"
37
- android:authorities="com.example.androidProject.fileprovider"
37
+ android:authorities="io.yourname.androidproject.release.fileprovider"
38
38
  android:exported="false"
39
39
  android:grantUriPermissions="true">
40
40
  <meta-data
@@ -1,6 +1,8 @@
1
- LOCAL_IP=192.168.0.17
2
- buildType=Debug
1
+ LOCAL_IP=192.168.3.209
2
+ buildType=Release
3
3
  emulatorName=testMediumPhone
4
4
  port=3005
5
5
  sdkPath=/Users/mayankmahavar/Android
6
6
  cachePattern=*.css
7
+ PRODUCTION_URL=https://catalyst.1mg.com/public_docs/
8
+
@@ -1,4 +1,4 @@
1
- package com.example.androidProject
1
+ package io.yourname.androidproject
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
@@ -10,7 +10,7 @@ import android.view.View
10
10
  import android.webkit.*
11
11
  import android.widget.ProgressBar
12
12
  import androidx.webkit.WebViewAssetLoader
13
- import com.example.myapplication.WebCacheManager
13
+ import io.yourname.androidproject.WebCacheManager
14
14
  import kotlinx.coroutines.*
15
15
  import java.util.Properties
16
16
 
@@ -46,7 +46,6 @@ class CustomWebView(
46
46
  }
47
47
 
48
48
  private fun setupFromProperties() {
49
-
50
49
  buildType = properties.getProperty("buildType", "debug")
51
50
  apiBaseUrl = properties.getProperty("apiBaseUrl", "")
52
51
  cachePatterns = properties.getProperty("cachePattern", "")
@@ -66,12 +65,15 @@ class CustomWebView(
66
65
  isInitialPageLoaded = false // Default value
67
66
  }
68
67
 
69
- Log.d(TAG, "Build type: $buildType")
70
- Log.d(TAG, "Cache Pattern: $cachePatterns")
71
- Log.d(TAG, "API Base URL: $apiBaseUrl")
72
- Log.d(TAG, "Build Optimisation: $buildOptimisation")
73
- Log.d(TAG, "Initial API Called: $isInitialApiCalled")
74
- Log.d(TAG, "Initial Page Loaded: $isInitialPageLoaded")
68
+ // Only log detailed info in debug mode
69
+ if (BuildConfig.DEBUG) {
70
+ Log.d(TAG, "Build type: $buildType")
71
+ Log.d(TAG, "Cache Pattern: $cachePatterns")
72
+ Log.d(TAG, "API Base URL: $apiBaseUrl")
73
+ Log.d(TAG, "Build Optimisation: $buildOptimisation")
74
+ Log.d(TAG, "Initial API Called: $isInitialApiCalled")
75
+ Log.d(TAG, "Initial Page Loaded: $isInitialPageLoaded")
76
+ }
75
77
 
76
78
  // Setup WebView Asset Loader for serving local files
77
79
  assetLoader = WebViewAssetLoader.Builder()
@@ -97,7 +99,6 @@ class CustomWebView(
97
99
  webView.clearHistory()
98
100
  }
99
101
 
100
-
101
102
  fun canGoBack(): Boolean = webView.canGoBack()
102
103
 
103
104
  fun goBack() {
@@ -131,7 +132,9 @@ class CustomWebView(
131
132
  }
132
133
 
133
134
  webView.addJavascriptInterface(obj, name)
134
- Log.d(TAG, "🔗 Added JavaScript interface: $name")
135
+ if (BuildConfig.DEBUG) {
136
+ Log.d(TAG, "🔗 Added JavaScript interface: $name")
137
+ }
135
138
  }
136
139
 
137
140
  fun destroy() {
@@ -141,13 +144,19 @@ class CustomWebView(
141
144
 
142
145
  fun cleanupCache() {
143
146
  launch(Dispatchers.IO) {
144
- Log.d(TAG, "📝 Starting cache cleanup on thread: ${Thread.currentThread().name}")
147
+ if (BuildConfig.DEBUG) {
148
+ Log.d(TAG, "📝 Starting cache cleanup on thread: ${Thread.currentThread().name}")
149
+ }
145
150
  val startTime = System.currentTimeMillis()
146
151
  disableHardwareAcceleration()
147
152
  try {
148
153
  cacheManager.cleanup()
149
- val duration = System.currentTimeMillis() - startTime
150
- Log.d(TAG, "⏱️ Cache cleanup completed in ${duration}ms on thread: ${Thread.currentThread().name}")
154
+ if (BuildConfig.DEBUG) {
155
+ val duration = System.currentTimeMillis() - startTime
156
+ Log.d(TAG, "⏱️ Cache cleanup completed in ${duration}ms on thread: ${Thread.currentThread().name}")
157
+ }
158
+ } catch (e: Exception) {
159
+ Log.e(TAG, "Cache cleanup error: ${e.message}")
151
160
  } finally {
152
161
  enableHardwareAcceleration()
153
162
  }
@@ -158,7 +167,9 @@ class CustomWebView(
158
167
  if (!isHardwareAccelerationEnabled) {
159
168
  webView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
160
169
  isHardwareAccelerationEnabled = true
161
- Log.d(TAG, "🚀 Hardware acceleration enabled - Thread: ${Thread.currentThread().name}")
170
+ if (BuildConfig.DEBUG) {
171
+ Log.d(TAG, "🚀 Hardware acceleration enabled - Thread: ${Thread.currentThread().name}")
172
+ }
162
173
  }
163
174
  }
164
175
 
@@ -166,7 +177,9 @@ class CustomWebView(
166
177
  if (isHardwareAccelerationEnabled) {
167
178
  webView.setLayerType(View.LAYER_TYPE_NONE, null)
168
179
  isHardwareAccelerationEnabled = false
169
- Log.d(TAG, "⚫ Hardware acceleration disabled - Thread: ${Thread.currentThread().name}")
180
+ if (BuildConfig.DEBUG) {
181
+ Log.d(TAG, "⚫ Hardware acceleration disabled - Thread: ${Thread.currentThread().name}")
182
+ }
170
183
  }
171
184
  }
172
185
 
@@ -248,7 +261,9 @@ class CustomWebView(
248
261
 
249
262
  @SuppressLint("SetJavaScriptEnabled")
250
263
  private fun setupWebView() {
251
- Log.d(TAG, "🌐 Setting up WebView on thread: ${Thread.currentThread().name}")
264
+ if (BuildConfig.DEBUG) {
265
+ Log.d(TAG, "🌐 Setting up WebView on thread: ${Thread.currentThread().name}")
266
+ }
252
267
 
253
268
  webView.settings.apply {
254
269
  javaScriptEnabled = true
@@ -267,15 +282,14 @@ class CustomWebView(
267
282
  allowContentAccess = true
268
283
  setEnableSmoothTransition(true)
269
284
 
270
- // Allow access to file URLs and JavaScript interfaces
271
- allowFileAccessFromFileURLs = true
272
- allowUniversalAccessFromFileURLs = true
285
+ // Allow access to file URLs and JavaScript interfaces - restrict in production if possible
286
+ allowFileAccessFromFileURLs = BuildConfig.DEBUG
287
+ allowUniversalAccessFromFileURLs = BuildConfig.DEBUG
273
288
  }
274
289
 
275
290
  webView.webViewClient = object : WebViewClient() {
276
291
  override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
277
292
  request?.url?.let { url ->
278
-
279
293
  // Let WebView handle loading non-API HTTP/HTTPS URLs
280
294
  if (url.scheme in listOf("http", "https")) {
281
295
  return false
@@ -289,12 +303,16 @@ class CustomWebView(
289
303
  request: WebResourceRequest
290
304
  ): WebResourceResponse? {
291
305
  val url = request.url.toString()
292
- Log.d(TAG, "🔄 Intercepting request for: $url on thread: ${Thread.currentThread().name}")
306
+ if (BuildConfig.DEBUG) {
307
+ Log.d(TAG, "🔄 Intercepting request for: $url on thread: ${Thread.currentThread().name}")
308
+ }
293
309
 
294
310
  // Handle the initial route request - intercept first request regardless of host
295
311
  if (!isInitialApiCalled && request.method == "GET") {
296
312
  isInitialApiCalled = true
297
- Log.d(TAG, "📄 Serving initial index.html from assets for route: $url")
313
+ if (BuildConfig.DEBUG) {
314
+ Log.d(TAG, "📄 Serving initial index.html from assets for route: $url")
315
+ }
298
316
 
299
317
  try {
300
318
  val indexHtml = context.assets.open("build/public/index.html")
@@ -316,14 +334,20 @@ class CustomWebView(
316
334
 
317
335
  try {
318
336
  val mimeType = getMimeType(assetPath)
319
- Log.d(TAG, "📦 Attempting to serve from assets: $assetPath")
337
+ if (BuildConfig.DEBUG) {
338
+ Log.d(TAG, "📦 Attempting to serve from assets: $assetPath")
339
+ }
320
340
  val inputStream = context.assets.open(assetPath)
321
- Log.d(TAG, "✅ Successfully loaded from assets: $assetPath")
341
+ if (BuildConfig.DEBUG) {
342
+ Log.d(TAG, "✅ Successfully loaded from assets: $assetPath")
343
+ }
322
344
  return WebResourceResponse(mimeType, "utf-8", inputStream)
323
345
  } catch (e: Exception) {
324
346
  assetLoadFailures++
325
- Log.e(TAG, "❌ Error loading asset: $assetPath", e)
326
- Log.d(TAG, "⚠️ Falling back to network for: $url")
347
+ if (BuildConfig.DEBUG) {
348
+ Log.e(TAG, "❌ Error loading asset: $assetPath", e)
349
+ Log.d(TAG, "⚠️ Falling back to network for: $url")
350
+ }
327
351
  // Return null to let the WebView load from network
328
352
  return null
329
353
  }
@@ -331,16 +355,19 @@ class CustomWebView(
331
355
 
332
356
  // Let API calls go through normally
333
357
  if (isApiCall(url)) {
334
- Log.d(TAG, "🌐 API call detected, letting it go through network: $url")
358
+ if (BuildConfig.DEBUG) {
359
+ Log.d(TAG, "🌐 API call detected, letting it go through network: $url")
360
+ }
335
361
  return null
336
362
  }
337
363
  }
338
364
 
339
-
340
365
  // For non-API HTTP requests that match cache patterns, use cache system
341
366
  if (request.method == "GET" && shouldCacheUrl(url)) {
342
367
  return runBlocking {
343
- Log.d(TAG, "⚙️ Processing cacheable request in coroutine on thread: ${Thread.currentThread().name}")
368
+ if (BuildConfig.DEBUG) {
369
+ Log.d(TAG, "⚙️ Processing cacheable request in coroutine on thread: ${Thread.currentThread().name}")
370
+ }
344
371
  val startTime = System.currentTimeMillis()
345
372
  disableHardwareAcceleration()
346
373
  try {
@@ -356,23 +383,31 @@ class CustomWebView(
356
383
  var response = cacheManager.getCachedResponse(url, headers)
357
384
 
358
385
  if (response != null) {
359
- val duration = System.currentTimeMillis() - startTime
360
- Log.d(TAG, "✅ Served from cache in ${duration}ms: $url")
386
+ if (BuildConfig.DEBUG) {
387
+ val duration = System.currentTimeMillis() - startTime
388
+ Log.d(TAG, "✅ Served from cache in ${duration}ms: $url")
389
+ }
361
390
  response
362
391
  } else {
363
- Log.d(TAG, "❌ Cache miss for: $url")
392
+ if (BuildConfig.DEBUG) {
393
+ Log.d(TAG, "❌ Cache miss for: $url")
394
+ }
364
395
  null
365
396
  }
366
397
  } catch (e: Exception) {
367
- Log.e(TAG, "❌ Error processing request for URL: $url", e)
368
- e.printStackTrace()
398
+ Log.e(TAG, "Error processing request for URL: $url: ${e.message}")
399
+ if (BuildConfig.DEBUG) {
400
+ e.printStackTrace()
401
+ }
369
402
  null
370
403
  } finally {
371
404
  enableHardwareAcceleration()
372
405
  }
373
406
  }
374
407
  } else {
375
- Log.d(TAG, "⏭️ URL doesn't match cache criteria, skipping cache: $url")
408
+ if (BuildConfig.DEBUG) {
409
+ Log.d(TAG, "⏭️ URL doesn't match cache criteria, skipping cache: $url")
410
+ }
376
411
  }
377
412
 
378
413
  return null
@@ -383,7 +418,9 @@ class CustomWebView(
383
418
  progressBar.visibility = View.VISIBLE
384
419
  val startTime = System.currentTimeMillis()
385
420
  view?.tag = startTime // Store start time for performance tracking
386
- Log.d(TAG, "⏳ Page load started for: $url - Hardware Acceleration: $isHardwareAccelerationEnabled")
421
+ if (BuildConfig.DEBUG) {
422
+ Log.d(TAG, "⏳ Page load started for: $url - Hardware Acceleration: $isHardwareAccelerationEnabled")
423
+ }
387
424
  }
388
425
 
389
426
  override fun onPageFinished(view: WebView?, url: String?) {
@@ -391,16 +428,27 @@ class CustomWebView(
391
428
  if(!isInitialPageLoaded){
392
429
  isInitialPageLoaded = true
393
430
  }
394
- val startTime = view?.tag as? Long ?: return
395
- val loadTime = System.currentTimeMillis() - startTime
396
- Log.d(TAG, "✅ Page load finished for: $url - Load time: ${loadTime}ms - Hardware Acceleration: $isHardwareAccelerationEnabled")
397
- Log.d(TAG, "📊 Asset loading stats: Attempted: $assetLoadAttempts, Failed: $assetLoadFailures (${String.format("%.1f", assetLoadFailures * 100.0 / assetLoadAttempts.coerceAtLeast(1))}%)")
431
+ if (BuildConfig.DEBUG) {
432
+ val startTime = view?.tag as? Long ?: return
433
+ val loadTime = System.currentTimeMillis() - startTime
434
+ Log.d(TAG, "✅ Page load finished for: $url - Load time: ${loadTime}ms - Hardware Acceleration: $isHardwareAccelerationEnabled")
435
+ Log.d(TAG, "📊 Asset loading stats: Attempted: $assetLoadAttempts, Failed: $assetLoadFailures (${String.format("%.1f", assetLoadFailures * 100.0 / assetLoadAttempts.coerceAtLeast(1))}%)")
436
+ }
398
437
  super.onPageFinished(view, url)
399
438
  }
400
439
 
401
440
  override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
402
- error?.let {
403
- Log.e(TAG, "❌ Error loading ${request?.url}: ${it.errorCode} ${it.description}")
441
+ if (error != null) {
442
+ Log.e(TAG, "❌ Error loading ${request?.url}: ${error.errorCode} ${error.description}")
443
+
444
+ // Only show a fallback page for main frame errors in production
445
+ if (request?.isForMainFrame == true && !BuildConfig.DEBUG) {
446
+ try {
447
+ view?.loadUrl("file:///android_asset/build/public/error.html")
448
+ } catch (e: Exception) {
449
+ Log.e(TAG, "Failed to load error page: ${e.message}")
450
+ }
451
+ }
404
452
  }
405
453
  super.onReceivedError(view, request, error)
406
454
  }
@@ -415,11 +463,14 @@ class CustomWebView(
415
463
  }
416
464
 
417
465
  override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
418
- Log.d(TAG, "Console: ${consoleMessage?.message()} -- From line ${consoleMessage?.lineNumber()} of ${consoleMessage?.sourceId()}")
466
+ if (BuildConfig.DEBUG) {
467
+ Log.d(TAG, "Console: ${consoleMessage?.message()} -- From line ${consoleMessage?.lineNumber()} of ${consoleMessage?.sourceId()}")
468
+ }
419
469
  return true
420
470
  }
421
471
  }
422
472
 
423
- WebView.setWebContentsDebuggingEnabled(true)
473
+ // Only enable WebView debugging in debug builds
474
+ WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
424
475
  }
425
476
  }