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.
- package/babel.config.js +30 -0
- package/changelog.md +7 -1
- package/dist/native/androidProject/app/build.gradle.kts +180 -0
- package/dist/native/androidProject/app/proguard-rules.pro +21 -0
- package/dist/native/androidProject/app/src/androidTest/java/com/example/myapplication/ExampleInstrumentedTest.kt +24 -0
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +28 -0
- package/dist/native/androidProject/app/src/main/java/com/example/myapplication/MainActivity.kt +278 -0
- package/dist/native/androidProject/app/src/main/java/com/example/myapplication/WebCacheManager.kt +331 -0
- package/dist/native/androidProject/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- package/dist/native/androidProject/app/src/main/res/drawable/ic_launcher_foreground.xml +30 -0
- package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +22 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- package/dist/native/androidProject/app/src/main/res/values/colors.xml +10 -0
- package/dist/native/androidProject/app/src/main/res/values/strings.xml +3 -0
- package/dist/native/androidProject/app/src/main/res/values/themes.xml +15 -0
- package/dist/native/androidProject/app/src/main/res/values-night/themes.xml +15 -0
- package/dist/native/androidProject/app/src/main/res/xml/backup_rules.xml +13 -0
- package/dist/native/androidProject/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- package/dist/native/androidProject/app/src/test/java/com/example/myapplication/ExampleUnitTest.kt +17 -0
- package/dist/native/androidProject/build.gradle.kts +5 -0
- package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
- package/dist/native/androidProject/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/dist/native/androidProject/gradle/wrapper/gradle-wrapper.properties +6 -0
- package/dist/native/androidProject/gradle.properties +23 -0
- package/dist/native/androidProject/gradlew +185 -0
- package/dist/native/androidProject/gradlew.bat +89 -0
- package/dist/native/androidProject/settings.gradle.kts +35 -0
- package/dist/native/androidSetup.js +2 -0
- package/dist/native/build.swift +31 -0
- package/dist/native/buildAppAndroid.js +8 -0
- package/dist/native/buildAppIos.js +41 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/Contents.json +6 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/CacheManager.swift +249 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +41 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +13 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Preview Content/Preview Assets.xcassets/Contents.json +6 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/ResourceURLProtocol.swift +134 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/WebView.swift +79 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/WebViewModel.swift +47 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/WebViewNavigationDelegate.swift +145 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +17 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +601 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/xcshareddata/xcschemes/iosnativeWebView.xcscheme +112 -0
- package/dist/native/setupEmulatorIos.js +19 -0
- package/dist/native/terminalProgress.js +11 -0
- package/dist/native/utils.js +13 -0
- package/dist/scripts/build.js +2 -9
- package/dist/scripts/devBuild.js +2 -9
- package/dist/scripts/devServe.js +2 -4
- package/dist/scripts/loadScriptsBeforeServerStarts.js +2 -2
- package/dist/scripts/registerAliases.js +1 -1
- package/dist/scripts/scriptUtils.js +2 -2
- package/dist/scripts/serve.js +2 -4
- package/dist/scripts/start.js +3 -3
- package/dist/server/expressServer.js +1 -1
- package/dist/server/renderer/document/Body.js +3 -3
- package/dist/server/renderer/document/Head.js +2 -2
- package/dist/server/renderer/extract.js +1 -1
- package/dist/server/renderer/handler.js +10 -6
- package/dist/server/renderer/index.js +1 -1
- package/dist/server/startServer.js +4 -3
- package/dist/server/utils/userAgentUtil.js +1 -1
- package/dist/server/utils/validator.js +1 -1
- package/dist/webpack/babel.config.client.js +1 -1
- package/dist/webpack/babel.config.ssr.js +1 -1
- package/dist/webpack/base.babel.js +1 -1
- package/dist/webpack/development.client.babel.js +1 -1
- package/dist/webpack/production.client.babel.js +1 -1
- package/dist/webpack/production.ssr.babel.js +1 -1
- package/package.json +5 -1
- package/run.sh +2 -2
- package/babel.config.json +0 -28
package/babel.config.js
ADDED
|
@@ -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
|
@@ -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>
|
package/dist/native/androidProject/app/src/main/java/com/example/myapplication/MainActivity.kt
ADDED
|
@@ -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
|
+
}
|