catalyst-core-internal 0.0.1-beta.50 → 0.0.1-beta.52

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 (87) hide show
  1. package/babel.config.js +30 -0
  2. package/changelog.md +7 -1
  3. package/dist/native/androidProject/app/build.gradle.kts +180 -0
  4. package/dist/native/androidProject/app/proguard-rules.pro +21 -0
  5. package/dist/native/androidProject/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt +24 -0
  6. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +28 -0
  7. package/dist/native/androidProject/app/src/main/java/com/example/myapplication/MainActivity.kt +278 -0
  8. package/dist/native/androidProject/app/src/main/java/com/example/myapplication/WebCacheManager.kt +331 -0
  9. package/dist/native/androidProject/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
  10. package/dist/native/androidProject/app/src/main/res/drawable/ic_launcher_foreground.xml +30 -0
  11. package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +22 -0
  12. package/dist/native/androidProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
  13. package/dist/native/androidProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
  14. package/dist/native/androidProject/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  15. package/dist/native/androidProject/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  16. package/dist/native/androidProject/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  17. package/dist/native/androidProject/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  18. package/dist/native/androidProject/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  19. package/dist/native/androidProject/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  20. package/dist/native/androidProject/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  21. package/dist/native/androidProject/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  22. package/dist/native/androidProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  23. package/dist/native/androidProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  24. package/dist/native/androidProject/app/src/main/res/values/colors.xml +10 -0
  25. package/dist/native/androidProject/app/src/main/res/values/strings.xml +3 -0
  26. package/dist/native/androidProject/app/src/main/res/values/themes.xml +15 -0
  27. package/dist/native/androidProject/app/src/main/res/values-night/themes.xml +15 -0
  28. package/dist/native/androidProject/app/src/main/res/xml/backup_rules.xml +13 -0
  29. package/dist/native/androidProject/app/src/main/res/xml/data_extraction_rules.xml +19 -0
  30. package/dist/native/androidProject/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt +17 -0
  31. package/dist/native/androidProject/build.gradle.kts +5 -0
  32. package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
  33. package/dist/native/androidProject/gradle/wrapper/gradle-wrapper.jar +0 -0
  34. package/dist/native/androidProject/gradle/wrapper/gradle-wrapper.properties +6 -0
  35. package/dist/native/androidProject/gradle.properties +23 -0
  36. package/dist/native/androidProject/gradlew +185 -0
  37. package/dist/native/androidProject/gradlew.bat +89 -0
  38. package/dist/native/androidProject/settings.gradle.kts +35 -0
  39. package/dist/native/androidSetup.js +2 -0
  40. package/dist/native/build.swift +31 -0
  41. package/dist/native/buildAppAndroid.js +8 -0
  42. package/dist/native/buildAppIos.js +41 -0
  43. package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  44. package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
  45. package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/Contents.json +6 -0
  46. package/dist/native/iosnativeWebView/iosnativeWebView/CacheManager.swift +249 -0
  47. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +41 -0
  48. package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +13 -0
  49. package/dist/native/iosnativeWebView/iosnativeWebView/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
  50. package/dist/native/iosnativeWebView/iosnativeWebView/ResourceURLProtocol.swift +134 -0
  51. package/dist/native/iosnativeWebView/iosnativeWebView/WebView.swift +79 -0
  52. package/dist/native/iosnativeWebView/iosnativeWebView/WebViewModel.swift +47 -0
  53. package/dist/native/iosnativeWebView/iosnativeWebView/WebViewNavigationDelegate.swift +145 -0
  54. package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +17 -0
  55. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +601 -0
  56. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  57. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  58. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/xcshareddata/xcschemes/iosnativeWebView.xcscheme +112 -0
  59. package/dist/native/setupEmulatorIos.js +19 -0
  60. package/dist/native/terminalProgress.js +11 -0
  61. package/dist/native/utils.js +13 -0
  62. package/dist/scripts/build.js +2 -9
  63. package/dist/scripts/devBuild.js +2 -9
  64. package/dist/scripts/devServe.js +2 -4
  65. package/dist/scripts/loadScriptsBeforeServerStarts.js +2 -2
  66. package/dist/scripts/registerAliases.js +1 -1
  67. package/dist/scripts/scriptUtils.js +2 -2
  68. package/dist/scripts/serve.js +2 -4
  69. package/dist/scripts/start.js +3 -3
  70. package/dist/server/expressServer.js +1 -1
  71. package/dist/server/renderer/document/Body.js +3 -3
  72. package/dist/server/renderer/document/Head.js +2 -2
  73. package/dist/server/renderer/extract.js +1 -1
  74. package/dist/server/renderer/handler.js +10 -6
  75. package/dist/server/renderer/index.js +1 -1
  76. package/dist/server/startServer.js +4 -3
  77. package/dist/server/utils/userAgentUtil.js +1 -1
  78. package/dist/server/utils/validator.js +1 -1
  79. package/dist/webpack/babel.config.client.js +1 -1
  80. package/dist/webpack/babel.config.ssr.js +1 -1
  81. package/dist/webpack/base.babel.js +1 -1
  82. package/dist/webpack/development.client.babel.js +1 -1
  83. package/dist/webpack/production.client.babel.js +1 -1
  84. package/dist/webpack/production.ssr.babel.js +1 -1
  85. package/package.json +5 -1
  86. package/run.sh +2 -2
  87. package/babel.config.json +0 -28
@@ -0,0 +1,30 @@
1
+ module.exports = (api) => {
2
+ api.cache(true)
3
+
4
+ const EXPERIMENTS = JSON.parse(process.env.EXPERIMENTS || "{}")
5
+
6
+ return {
7
+ presets: [
8
+ [
9
+ "@babel/preset-env",
10
+ {
11
+ targets: {
12
+ node: "current",
13
+ },
14
+ },
15
+ ],
16
+ "@babel/preset-react",
17
+ ],
18
+ compact: true,
19
+ plugins: [
20
+ ...(EXPERIMENTS?.ENABLE_COMPILER ? [["babel-plugin-react-compiler", { target: "18" }]] : []),
21
+ "@loadable/babel-plugin",
22
+ ],
23
+ env: {
24
+ test: {
25
+ presets: ["@babel/preset-react"],
26
+ },
27
+ },
28
+ ignore: ["__TEST__"],
29
+ }
30
+ }
package/changelog.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Changelog
2
2
 
3
- ## [0.0.1-canary.0] - 02-07-2024
3
+ ## [0.0.1-beta.9] - 13-12-2024
4
+
5
+ ### Changes
6
+
7
+ - Update @tata1mg/router's version to 0.0.1-beta.6.
8
+
9
+ ## [0.0.1-beta.8] - 09-12-2024
4
10
 
5
11
  ### Changes
6
12
 
@@ -0,0 +1,180 @@
1
+ import java.net.NetworkInterface
2
+ import java.io.File
3
+ import java.util.Properties
4
+
5
+ val configPath: String? by project.properties
6
+
7
+ fun getLocalIpAddress(): String {
8
+ return NetworkInterface.getNetworkInterfaces().toList()
9
+ .flatMap { it.inetAddresses.toList() }
10
+ .filter { !it.isLoopbackAddress && it.hostAddress.indexOf(':') == -1 }
11
+ .map { it.hostAddress }
12
+ .firstOrNull() ?: "127.0.0.1"
13
+ }
14
+
15
+ plugins {
16
+ alias(libs.plugins.android.application)
17
+ alias(libs.plugins.jetbrains.kotlin.android)
18
+ }
19
+
20
+ android {
21
+ namespace = "com.example.androidProject"
22
+ compileSdk = 34
23
+
24
+ defaultConfig {
25
+ applicationId = "com.example.androidProject"
26
+ minSdk = 24
27
+ targetSdk = 34
28
+ versionCode = 1
29
+ versionName = "1.0"
30
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
31
+ buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
32
+ }
33
+
34
+ buildTypes {
35
+ debug {
36
+ manifestPlaceholders += mapOf("allowCleartextTraffic" to true)
37
+ isMinifyEnabled = false
38
+ buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "true")
39
+ buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
40
+ }
41
+ release {
42
+ manifestPlaceholders += mapOf("allowCleartextTraffic" to false)
43
+ isMinifyEnabled = false
44
+ proguardFiles(
45
+ getDefaultProguardFile("proguard-android-optimize.txt"),
46
+ "proguard-rules.pro"
47
+ )
48
+ buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "false")
49
+ buildConfigField("String", "LOCAL_IP", "\"127.0.0.1\"")
50
+ }
51
+ }
52
+
53
+ compileOptions {
54
+ sourceCompatibility = JavaVersion.VERSION_1_8
55
+ targetCompatibility = JavaVersion.VERSION_1_8
56
+ }
57
+
58
+ kotlinOptions {
59
+ jvmTarget = "1.8"
60
+ }
61
+
62
+ buildFeatures {
63
+ viewBinding = true
64
+ buildConfig = true
65
+ }
66
+ }
67
+
68
+ dependencies {
69
+ implementation(libs.androidx.core.ktx)
70
+ implementation(libs.androidx.appcompat)
71
+ implementation(libs.material)
72
+ implementation(libs.androidx.constraintlayout)
73
+ testImplementation(libs.junit)
74
+ androidTestImplementation(libs.androidx.junit)
75
+ androidTestImplementation(libs.androidx.espresso.core)
76
+ implementation("androidx.webkit:webkit:1.12.1")
77
+ implementation("org.json:json:20231013")
78
+ }
79
+
80
+ // Task to verify local IP
81
+ tasks.register("printLocalIp") {
82
+ doLast {
83
+ println("Local IP Address: ${getLocalIpAddress()}")
84
+ }
85
+ }
86
+
87
+ tasks.register("updateSdkPath") {
88
+ doLast {
89
+ val sdkPath: String? by project.properties
90
+
91
+ if (sdkPath == null) {
92
+ throw GradleException(
93
+ """
94
+ SDK path not provided!
95
+ Please provide the SDK path using -PsdkPath=/path/to/sdk
96
+ Example: ./gradlew updateSdkPath -PsdkPath=/path/to/android/sdk
97
+ """.trimIndent()
98
+ )
99
+ }
100
+
101
+ val localProperties = File(project.rootDir, "local.properties")
102
+ val properties = Properties()
103
+
104
+ if (localProperties.exists()) {
105
+ properties.load(localProperties.inputStream())
106
+ }
107
+
108
+ properties.setProperty("sdk.dir", sdkPath)
109
+ properties.store(localProperties.outputStream(), "Updated SDK Path")
110
+
111
+ println("Updated SDK path to: $sdkPath")
112
+ }
113
+ }
114
+
115
+ // Task to generate WebView config
116
+ tasks.register("generateWebViewConfig") {
117
+ doLast {
118
+ val configJsonPath = configPath ?: throw GradleException(
119
+ """
120
+ Config path not provided!
121
+ Please provide the config path using -PconfigPath=/path/to/your/config.json
122
+ Example: ./gradlew generateWebViewConfig -PconfigPath=/path/to/your/config.json
123
+ """.trimIndent()
124
+ )
125
+
126
+ val configJsonFile = File(configJsonPath)
127
+ if (!configJsonFile.exists()) {
128
+ throw GradleException("Config file not found at: $configJsonPath")
129
+ }
130
+
131
+ val configContent = configJsonFile.readText()
132
+
133
+ // Extract WEBVIEW_CONFIG section
134
+ val webviewConfigRegex = """"WEBVIEW_CONFIG"\s*:\s*\{([^}]*)\}""".toRegex()
135
+ val webviewConfigMatch = webviewConfigRegex.find(configContent)
136
+ val webviewConfigContent = webviewConfigMatch?.groupValues?.get(1)
137
+
138
+ val properties = Properties()
139
+ properties.setProperty("LOCAL_IP", getLocalIpAddress())
140
+
141
+ // Parse top-level properties
142
+ val topLevelRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
143
+ webviewConfigContent?.let {
144
+ topLevelRegex.findAll(it).forEach { matchResult ->
145
+ val (key, value) = matchResult.destructured
146
+ if (key != "android") {
147
+ properties.setProperty(key, value)
148
+ }
149
+ }
150
+ }
151
+
152
+ // Parse android object specifically
153
+ val androidRegex = """"android"\s*:\s*\{([^}]*)\}""".toRegex()
154
+ val androidMatch = androidRegex.find(webviewConfigContent ?: "")
155
+ val androidContent = androidMatch?.groupValues?.get(1)
156
+
157
+ // Parse android object properties
158
+ val androidPropsRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
159
+ androidContent?.let {
160
+ androidPropsRegex.findAll(it).forEach { matchResult ->
161
+ val (key, value) = matchResult.destructured
162
+ properties.setProperty("android.$key", value)
163
+ }
164
+ }
165
+
166
+ // Create the assets directory if it doesn't exist
167
+ val assetsDir = File("${project.projectDir}/src/main/assets")
168
+ if (!assetsDir.exists()) {
169
+ assetsDir.mkdirs()
170
+ println("Created assets directory at ${assetsDir.absolutePath}")
171
+ }
172
+
173
+ // Write to properties file
174
+ File(assetsDir, "webview_config.properties").outputStream().use {
175
+ properties.store(it, "WebView Configuration")
176
+ }
177
+
178
+ println("WebView config generated at ${assetsDir.absolutePath}/webview_config.properties")
179
+ }
180
+ }
@@ -0,0 +1,21 @@
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
7
+
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
+ #}
14
+
15
+ # Uncomment this to preserve the line number information for
16
+ # debugging stack traces.
17
+ #-keepattributes SourceFile,LineNumberTable
18
+
19
+ # If you keep the line number information, uncomment this to
20
+ # hide the original source file name.
21
+ #-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
1
+ package com.example.myapplication
2
+
3
+ import androidx.test.platform.app.InstrumentationRegistry
4
+ import androidx.test.ext.junit.runners.AndroidJUnit4
5
+
6
+ import org.junit.Test
7
+ import org.junit.runner.RunWith
8
+
9
+ import org.junit.Assert.*
10
+
11
+ /**
12
+ * Instrumented test, which will execute on an Android device.
13
+ *
14
+ * See [testing documentation](http://d.android.com/tools/testing).
15
+ */
16
+ @RunWith(AndroidJUnit4::class)
17
+ class ExampleInstrumentedTest {
18
+ @Test
19
+ fun useAppContext() {
20
+ // Context of the app under test.
21
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22
+ assertEquals("com.example.myapplication", appContext.packageName)
23
+ }
24
+ }
@@ -0,0 +1,28 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ xmlns:tools="http://schemas.android.com/tools">
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
6
+ <application
7
+ android:usesCleartextTraffic="${allowCleartextTraffic}"
8
+ android:allowBackup="true"
9
+ android:dataExtractionRules="@xml/data_extraction_rules"
10
+ android:fullBackupContent="@xml/backup_rules"
11
+ android:icon="@mipmap/ic_launcher"
12
+ android:label="@string/app_name"
13
+ android:roundIcon="@mipmap/ic_launcher_round"
14
+ android:supportsRtl="true"
15
+ android:theme="@style/Theme.AndroidProject"
16
+ tools:targetApi="31">
17
+ <activity
18
+ android:name=".MainActivity"
19
+ android:exported="true">
20
+ <intent-filter>
21
+ <action android:name="android.intent.action.MAIN" />
22
+
23
+ <category android:name="android.intent.category.LAUNCHER" />
24
+ </intent-filter>
25
+ </activity>
26
+ </application>
27
+
28
+ </manifest>
@@ -0,0 +1,278 @@
1
+ package com.example.androidProject
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.graphics.Bitmap
5
+ import android.os.Bundle
6
+ import android.util.Log
7
+ import android.view.View
8
+ import android.webkit.*
9
+ import androidx.appcompat.app.AppCompatActivity
10
+ import androidx.webkit.WebViewAssetLoader
11
+ import java.util.Properties
12
+ import com.example.androidProject.databinding.ActivityMainBinding
13
+ import com.example.myapplication.WebCacheManager
14
+ import kotlinx.coroutines.CoroutineScope
15
+ import kotlinx.coroutines.Dispatchers
16
+ import kotlinx.coroutines.launch
17
+ import kotlinx.coroutines.MainScope
18
+ import kotlinx.coroutines.runBlocking
19
+ import org.json.JSONObject
20
+ import java.net.URL
21
+ import java.net.HttpURLConnection
22
+
23
+ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
24
+
25
+ private val TAG = "WebViewDebug"
26
+ private lateinit var binding: ActivityMainBinding
27
+ private lateinit var myWebView: WebView
28
+ private lateinit var cacheManager: WebCacheManager
29
+ private var buildType: String = "debug" // Default to debug
30
+ private var cachePatterns: List<String> = emptyList()
31
+
32
+ data class AndroidConfig(
33
+ val buildType: String = "debug",
34
+ val cachePattern: String = "",
35
+ val emulatorName: String = "",
36
+ val sdkPath: String = ""
37
+ )
38
+
39
+ override fun onCreate(savedInstanceState: Bundle?) {
40
+ super.onCreate(savedInstanceState)
41
+
42
+ val properties = Properties()
43
+ assets.open("webview_config.properties").use {
44
+ properties.load(it)
45
+ }
46
+
47
+ // Parse android config from JSON string
48
+ val androidConfigJson = properties.getProperty("android", "{}")
49
+ val androidConfig = try {
50
+ val jsonObject = JSONObject(androidConfigJson)
51
+ AndroidConfig(
52
+ buildType = jsonObject.optString("buildType", "debug"),
53
+ cachePattern = jsonObject.optString("cachePattern", ""),
54
+ emulatorName = jsonObject.optString("emulatorName", ""),
55
+ sdkPath = jsonObject.optString("sdkPath", "")
56
+ )
57
+ } catch (e: Exception) {
58
+ Log.e(TAG, "Error parsing android config", e)
59
+ AndroidConfig()
60
+ }
61
+ Log.d(TAG, "android config parsed: $androidConfig")
62
+ buildType = androidConfig.buildType
63
+ cachePatterns = androidConfig.cachePattern
64
+ .split(",")
65
+ .map { it.trim() }
66
+ .filter { it.isNotEmpty() }
67
+
68
+ Log.d(TAG, "Build type: $buildType")
69
+ Log.d(TAG, "Cache Pattern: $cachePatterns ")
70
+
71
+ binding = ActivityMainBinding.inflate(layoutInflater)
72
+ supportActionBar?.hide()
73
+ setContentView(binding.root)
74
+
75
+ cacheManager = WebCacheManager(applicationContext)
76
+
77
+ // Clean up expired cache entries
78
+ launch(Dispatchers.IO) {
79
+ cacheManager.cleanup()
80
+ }
81
+
82
+ myWebView = binding.webview
83
+
84
+ // Clear any existing WebView data if this is a fresh start
85
+ if (savedInstanceState == null) {
86
+ myWebView.clearCache(false) // false means we keep files marked as "cache-control: no-cache"
87
+ myWebView.clearHistory()
88
+ } else {
89
+ myWebView.restoreState(savedInstanceState)
90
+ }
91
+
92
+ setupWebView(properties)
93
+
94
+ val local_ip = properties.getProperty("LOCAL_IP" , "localhost")
95
+ val port = properties.getProperty("port" , "3005")
96
+ val loadUrl = "http://$local_ip:$port"
97
+ makeRequest(loadUrl)
98
+ }
99
+
100
+ private fun shouldCacheUrl(url: String): Boolean {
101
+ // If no patterns specified, don't cache anything
102
+ if (cachePatterns.isEmpty()) return false
103
+
104
+ // Convert the wildcard pattern to a regex pattern
105
+ fun String.wildcardToRegex(): String {
106
+ return this.replace(".", "\\.") // Escape dots
107
+ .replace("*", ".*") // Convert * to .*
108
+ .let { "^$it$" } // Anchor pattern
109
+ }
110
+
111
+ // Check if URL matches any of the patterns
112
+ return cachePatterns.any { pattern ->
113
+ val regex = pattern.wildcardToRegex().toRegex(RegexOption.IGNORE_CASE)
114
+ regex.matches(url) || url.endsWith(pattern.removePrefix("*"))
115
+ }
116
+ }
117
+
118
+ override fun onSaveInstanceState(outState: Bundle) {
119
+ super.onSaveInstanceState(outState)
120
+ myWebView.saveState(outState)
121
+ }
122
+
123
+ @SuppressLint("SetJavaScriptEnabled")
124
+ private fun setupWebView(properties: Properties) {
125
+ myWebView.settings.apply {
126
+ javaScriptEnabled = true
127
+ loadsImagesAutomatically = true
128
+ mixedContentMode = if (BuildConfig.ALLOW_MIXED_CONTENT) {
129
+ WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
130
+ } else {
131
+ WebSettings.MIXED_CONTENT_NEVER_ALLOW
132
+ }
133
+
134
+ // Enable standard caching
135
+ cacheMode = WebSettings.LOAD_DEFAULT // This will use both RAM and disk cache
136
+ databaseEnabled = true
137
+ domStorageEnabled = true
138
+
139
+ // Enable file access
140
+ allowFileAccess = true
141
+ allowContentAccess = true
142
+ }
143
+
144
+ myWebView.webViewClient = object : WebViewClient() {
145
+
146
+ override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
147
+ request?.url?.let { url ->
148
+ if (url.scheme in listOf("http", "https")) {
149
+ view?.loadUrl(url.toString())
150
+ return true
151
+ }
152
+ }
153
+ return false
154
+ }
155
+
156
+ override fun shouldInterceptRequest(
157
+ view: WebView,
158
+ request: WebResourceRequest
159
+ ): WebResourceResponse? {
160
+ val originalUrl = request.url.toString()
161
+ Log.d(TAG, "Intercepting request for: $originalUrl")
162
+
163
+ if ( buildType == "debug" || request.method != "GET") {
164
+ return null
165
+ }
166
+
167
+ // Check if URL matches patterns
168
+ if (!shouldCacheUrl(originalUrl)) {
169
+ Log.d(TAG, "URL doesn't match cache patterns, skipping cache: $originalUrl")
170
+ return null
171
+ }
172
+
173
+ return runBlocking {
174
+ try {
175
+ // Add cache control headers
176
+ val headers = request.requestHeaders.toMutableMap().apply {
177
+ if (!containsKey("Cache-Control")) {
178
+ put("Cache-Control", "max-age=86400") // 24 hours
179
+ }
180
+ if (!containsKey("Pragma")) {
181
+ put("Pragma", "cache")
182
+ }
183
+ }
184
+
185
+ // Try to get from cache first
186
+ var response = cacheManager.getCachedResponse(originalUrl, headers)
187
+
188
+ if (response != null) {
189
+ Log.d(TAG, "📱 Serving from cache: $originalUrl")
190
+ response
191
+ } else {
192
+ Log.d(TAG, "Cache Manager unable to return response : $originalUrl")
193
+ null
194
+ }
195
+ } catch (e: Exception) {
196
+ Log.e(TAG, "❌ Error processing request for URL: $originalUrl", e)
197
+ e.printStackTrace()
198
+ null
199
+ }
200
+ }
201
+ }
202
+
203
+ override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
204
+ super.onPageStarted(view, url, favicon)
205
+ binding.progress.visibility = View.VISIBLE
206
+ Log.d(TAG, "⏳ Page started loading: $url")
207
+ }
208
+
209
+ override fun onPageFinished(view: WebView?, url: String?) {
210
+ binding.progress.visibility = View.GONE
211
+ Log.d(TAG, "✅ Page finished loading: $url")
212
+ // Save to WebView's back/forward list
213
+ view?.clearHistory()
214
+ view?.saveState(Bundle())
215
+ super.onPageFinished(view, url)
216
+ }
217
+
218
+ override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
219
+ error?.let {
220
+ Log.e(TAG, "❌ Error loading ${request?.url}: ${it.errorCode} ${it.description}")
221
+ }
222
+ super.onReceivedError(view, request, error)
223
+ }
224
+ }
225
+
226
+ myWebView.webChromeClient = object : WebChromeClient() {
227
+ override fun onProgressChanged(view: WebView, progress: Int) {
228
+ binding.progress.progress = progress
229
+ if (progress == 100) {
230
+ binding.progress.visibility = View.GONE
231
+ }
232
+ }
233
+
234
+ override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
235
+ Log.d(TAG, "Console: ${consoleMessage?.message()} -- From line ${consoleMessage?.lineNumber()} of ${consoleMessage?.sourceId()}")
236
+ return true
237
+ }
238
+ }
239
+
240
+ WebView.setWebContentsDebuggingEnabled(true)
241
+ }
242
+
243
+ private fun makeRequest(url: String?) {
244
+ binding.webview.scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
245
+ url?.let { myWebView.loadUrl(it) }
246
+ }
247
+
248
+ override fun onBackPressed() {
249
+ if (myWebView.canGoBack()) {
250
+ myWebView.goBack()
251
+ } else {
252
+ super.onBackPressed()
253
+ }
254
+ }
255
+
256
+ override fun onPause() {
257
+ super.onPause()
258
+ myWebView.onPause()
259
+ }
260
+
261
+ override fun onResume() {
262
+ super.onResume()
263
+ myWebView.onResume()
264
+ }
265
+
266
+ override fun onDestroy() {
267
+ super.onDestroy()
268
+ myWebView.destroy()
269
+ }
270
+
271
+ // external fun stringFromJNI(): String
272
+
273
+ // companion object {
274
+ // init {
275
+ // System.loadLibrary("native-lib")
276
+ // }
277
+ // }
278
+ }