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.
- package/README.md +2 -2
- package/changelog.md +12 -21
- package/dist/native/androidProject/app/build.gradle.kts +126 -5
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -2
- package/dist/native/androidProject/app/src/main/assets/webview_config.properties +4 -2
- package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/CustomWebview.kt +96 -45
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +162 -0
- package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/NativeBridge.kt +122 -61
- package/dist/native/androidProject/app/src/main/java/{com/example/myapplication → io/yourname/androidproject}/WebCacheManager.kt +85 -28
- package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +1 -2
- package/dist/native/androidProject/gradle/libs.versions.toml +1 -1
- package/dist/native/androidProject/keystore/Untitled +0 -0
- package/dist/native/androidProject/keystore/release-key.jks +0 -0
- package/dist/native/buildAppAndroid.js +26 -20
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcuserdata/mayankmahavar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/dist/native/renameAndroidProject.js +76 -0
- package/dist/server/renderer/document/Head.js +1 -1
- package/dist/server/renderer/extract.js +3 -3
- package/dist/server/renderer/handler.js +9 -9
- package/package.json +1 -1
- package/transform-config.json +35 -0
- package/dist/native/androidProject/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt +0 -24
- package/dist/native/androidProject/app/src/main/java/com/example/myapplication/MainActivity.kt +0 -115
- 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
|
|
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
|
-
|
|
3
|
+
|
|
4
|
+
## [0.0.3-canary.5] - 12-06-2025
|
|
4
5
|
|
|
5
6
|
### Features
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
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
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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.
|
|
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 = "
|
|
24
|
+
namespace = "io.yourname.androidproject"
|
|
22
25
|
compileSdk = 34
|
|
23
26
|
|
|
24
27
|
defaultConfig {
|
|
25
|
-
applicationId = "
|
|
28
|
+
applicationId = "io.yourname.androidproject"
|
|
26
29
|
minSdk = 24
|
|
27
30
|
targetSdk = 34
|
|
28
31
|
versionCode = 1
|
|
29
|
-
versionName = "1.
|
|
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 =
|
|
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
|
-
|
|
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="
|
|
37
|
+
android:authorities="io.yourname.androidproject.release.fileprovider"
|
|
38
38
|
android:exported="false"
|
|
39
39
|
android:grantUriPermissions="true">
|
|
40
40
|
<meta-data
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
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
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
272
|
-
allowUniversalAccessFromFileURLs =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
+
if (BuildConfig.DEBUG) {
|
|
338
|
+
Log.d(TAG, "📦 Attempting to serve from assets: $assetPath")
|
|
339
|
+
}
|
|
320
340
|
val inputStream = context.assets.open(assetPath)
|
|
321
|
-
|
|
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
|
-
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
360
|
-
|
|
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
|
-
|
|
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, "
|
|
368
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
|
403
|
-
Log.e(TAG, "❌ Error loading ${request?.url}: ${
|
|
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
|
-
|
|
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
|
|
473
|
+
// Only enable WebView debugging in debug builds
|
|
474
|
+
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
|
424
475
|
}
|
|
425
476
|
}
|