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,331 @@
1
+ package com.example.myapplication
2
+
3
+ import android.content.Context
4
+ import android.util.Log
5
+ import android.util.LruCache
6
+ import android.webkit.WebResourceResponse
7
+ import kotlinx.coroutines.*
8
+ import java.io.*
9
+ import java.net.HttpURLConnection
10
+ import java.net.URL
11
+ import java.security.MessageDigest
12
+ import java.util.concurrent.TimeUnit
13
+
14
+ class WebCacheManager(private val context: Context) {
15
+ private val TAG = "WebCacheManager"
16
+ private val cacheDir = File(context.cacheDir, "webview_cache")
17
+ private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
18
+
19
+ // Cache timing configurations
20
+ private val maxAge = TimeUnit.HOURS.toMillis(24) // Time until content becomes stale
21
+ private val staleWhileRevalidate = TimeUnit.HOURS.toMillis(1) // Additional time content can be served while revalidating
22
+
23
+ // Track ongoing revalidations to prevent duplicate requests
24
+ private val ongoingRevalidations = mutableSetOf<String>()
25
+
26
+ // Memory cache
27
+ private val memoryCache: LruCache<String, CacheEntry> = LruCache<String, CacheEntry>(
28
+ (Runtime.getRuntime().maxMemory() / 1024 / 8).toInt()
29
+ )
30
+
31
+ init {
32
+ cacheDir.mkdirs()
33
+ }
34
+
35
+ data class CacheEntry(
36
+ val response: WebResourceResponse,
37
+ val timestamp: Long = System.currentTimeMillis(),
38
+ val eTag: String? = null,
39
+ val lastModified: String? = null
40
+ )
41
+
42
+ suspend fun getCachedResponse(url: String, headers: Map<String, String>): WebResourceResponse? =
43
+ withContext(Dispatchers.IO) {
44
+ try {
45
+ val cacheKey = generateCacheKey(url)
46
+ val currentTime = System.currentTimeMillis()
47
+
48
+ // Check memory cache first
49
+ val memoryCacheEntry = memoryCache.get(cacheKey)
50
+ if (memoryCacheEntry != null) {
51
+ val age = currentTime - memoryCacheEntry.timestamp
52
+
53
+ when {
54
+ age <= maxAge -> {
55
+ // Fresh content
56
+ Log.d(TAG, "Serving fresh content from memory cache: $url")
57
+ return@withContext memoryCacheEntry.response
58
+ }
59
+ age <= maxAge + staleWhileRevalidate -> {
60
+ // Stale content, but within revalidate window
61
+ Log.d(TAG, "Serving stale content while revalidating: $url")
62
+ revalidateInBackground(url, headers, cacheKey, memoryCacheEntry)
63
+ return@withContext memoryCacheEntry.response
64
+ }
65
+ }
66
+ }
67
+
68
+ // Check disk cache
69
+ val cacheFile = File(cacheDir, cacheKey)
70
+ if (cacheFile.exists()) {
71
+ val fileAge = currentTime - cacheFile.lastModified()
72
+ val metadata = loadMetadata(cacheKey)
73
+
74
+ when {
75
+ fileAge <= maxAge -> {
76
+ // Fresh content from disk
77
+ val response = createResponseFromCache(cacheFile, metadata)
78
+ memoryCache.put(cacheKey, CacheEntry(response))
79
+ return@withContext response
80
+ }
81
+ fileAge <= maxAge + staleWhileRevalidate -> {
82
+ // Stale content from disk, revalidate in background
83
+ val response = createResponseFromCache(cacheFile, metadata)
84
+ val cacheEntry = CacheEntry(
85
+ response = response,
86
+ timestamp = cacheFile.lastModified(),
87
+ eTag = metadata.eTag,
88
+ lastModified = metadata.lastModified
89
+ )
90
+ revalidateInBackground(url, headers, cacheKey, cacheEntry)
91
+ return@withContext response
92
+ }
93
+ }
94
+ }
95
+
96
+ // No cache or cache too old, fetch fresh content
97
+ Log.d(TAG, "❌ Cache miss, fetching fresh content: $url")
98
+ fetchAndCacheResource(url, headers, cacheKey)
99
+ } catch (e: Exception) {
100
+ Log.e(TAG, "Error in getCachedResponse for URL: $url", e)
101
+ null
102
+ }
103
+ }
104
+
105
+ suspend fun cleanup() {
106
+ withContext(Dispatchers.IO) {
107
+ try {
108
+ val currentTime = System.currentTimeMillis()
109
+ val maxCacheSize = 100 * 1024 * 1024 // 100MB max cache size
110
+ var totalSize = 0L
111
+
112
+ // Get all cache files sorted by last modified time (oldest first)
113
+ val cacheFiles = cacheDir.listFiles()?.sortedBy { it.lastModified() } ?: return@withContext
114
+
115
+ for (file in cacheFiles) {
116
+ val age = currentTime - file.lastModified()
117
+
118
+ // Delete if expired (older than maxAge + staleWhileRevalidate)
119
+ if (age > maxAge + staleWhileRevalidate) {
120
+ file.delete()
121
+ // Also delete corresponding metadata file if it exists
122
+ val metaFile = File(cacheDir, "${file.name}.meta")
123
+ if (metaFile.exists()) {
124
+ metaFile.delete()
125
+ }
126
+ continue
127
+ }
128
+
129
+ totalSize += file.length()
130
+ }
131
+
132
+ // If cache is too large, remove oldest files until under limit
133
+ if (totalSize > maxCacheSize) {
134
+ val sortedFiles = cacheDir.listFiles()?.sortedBy { it.lastModified() } ?: return@withContext
135
+ for (file in sortedFiles) {
136
+ if (totalSize <= maxCacheSize) break
137
+
138
+ totalSize -= file.length()
139
+ file.delete()
140
+ // Delete corresponding metadata file
141
+ val metaFile = File(cacheDir, "${file.name}.meta")
142
+ if (metaFile.exists()) {
143
+ metaFile.delete()
144
+ }
145
+ }
146
+ }
147
+
148
+ Log.d(TAG, "Cache cleanup completed. Current size: ${totalSize / 1024}KB")
149
+ } catch (e: Exception) {
150
+ Log.e(TAG, "Error during cache cleanup", e)
151
+ }
152
+ }
153
+ }
154
+
155
+ private fun revalidateInBackground(
156
+ url: String,
157
+ headers: Map<String, String>,
158
+ cacheKey: String,
159
+ cacheEntry: CacheEntry
160
+ ) {
161
+ if (!ongoingRevalidations.add(cacheKey)) {
162
+ // Revalidation already in progress
163
+ return
164
+ }
165
+
166
+ scope.launch {
167
+ try {
168
+ val revalidationHeaders = headers.toMutableMap()
169
+ cacheEntry.eTag?.let { revalidationHeaders["If-None-Match"] = it }
170
+ cacheEntry.lastModified?.let { revalidationHeaders["If-Modified-Since"] = it }
171
+
172
+ val connection = URL(url).openConnection() as HttpURLConnection
173
+ revalidationHeaders.forEach { (key, value) ->
174
+ connection.setRequestProperty(key, value)
175
+ }
176
+
177
+ when (connection.responseCode) {
178
+ HttpURLConnection.HTTP_NOT_MODIFIED -> {
179
+ // Content still valid, update timestamp
180
+ val updatedEntry = cacheEntry.copy(timestamp = System.currentTimeMillis())
181
+ memoryCache.put(cacheKey, updatedEntry)
182
+ updateCacheFileTimestamp(cacheKey)
183
+ Log.d(TAG, "✅ Content revalidated, not modified: $url")
184
+ }
185
+ HttpURLConnection.HTTP_OK -> {
186
+ // Content changed, update cache
187
+ Log.d(TAG, "⚡ Content changed, updating cache: $url")
188
+ fetchAndCacheResource(url, headers, cacheKey)
189
+ }
190
+ }
191
+ } catch (e: Exception) {
192
+ Log.e(TAG, "Error during revalidation for URL: $url", e)
193
+ } finally {
194
+ ongoingRevalidations.remove(cacheKey)
195
+ }
196
+ }
197
+ }
198
+
199
+ private fun updateCacheFileTimestamp(cacheKey: String) {
200
+ val cacheFile = File(cacheDir, cacheKey)
201
+ if (cacheFile.exists()) {
202
+ cacheFile.setLastModified(System.currentTimeMillis())
203
+ File(cacheDir, "${cacheKey}.meta").setLastModified(System.currentTimeMillis())
204
+ }
205
+ }
206
+
207
+ private suspend fun fetchAndCacheResource(
208
+ url: String,
209
+ headers: Map<String, String>,
210
+ cacheKey: String
211
+ ): WebResourceResponse? = withContext(Dispatchers.IO) {
212
+ var connection: HttpURLConnection? = null
213
+ try {
214
+ connection = URL(url).openConnection() as HttpURLConnection
215
+ headers.forEach { (key, value) ->
216
+ connection.setRequestProperty(key, value)
217
+ }
218
+
219
+ connection.connectTimeout = 15000
220
+ connection.readTimeout = 15000
221
+ connection.connect()
222
+
223
+ if (connection.responseCode == HttpURLConnection.HTTP_OK) {
224
+ val mimeType = connection.contentType ?: "application/octet-stream"
225
+ val encoding = connection.contentEncoding ?: "utf-8"
226
+ val eTag = connection.getHeaderField("ETag")
227
+ val lastModified = connection.getHeaderField("Last-Modified")
228
+
229
+ val responseBytes = connection.inputStream.use { it.readBytes() }
230
+ if (!isValidResponse(mimeType, responseBytes)) {
231
+ return@withContext null
232
+ }
233
+
234
+ // Create response for immediate use
235
+ val response = WebResourceResponse(
236
+ mimeType,
237
+ encoding,
238
+ ByteArrayInputStream(responseBytes)
239
+ )
240
+
241
+ // Cache the response
242
+ val cacheEntry = CacheEntry(
243
+ response = WebResourceResponse(
244
+ mimeType,
245
+ encoding,
246
+ ByteArrayInputStream(responseBytes.clone())
247
+ ),
248
+ eTag = eTag,
249
+ lastModified = lastModified
250
+ )
251
+ memoryCache.put(cacheKey, cacheEntry)
252
+
253
+ // Save to disk cache
254
+ scope.launch {
255
+ try {
256
+ val cacheFile = File(cacheDir, cacheKey)
257
+ FileOutputStream(cacheFile).use { it.write(responseBytes) }
258
+ saveMetadata(cacheKey, CacheMetadata(
259
+ mimeType = mimeType,
260
+ encoding = encoding,
261
+ eTag = eTag,
262
+ lastModified = lastModified
263
+ ))
264
+ Log.d(TAG, "✅ Successfully cached response for: $url")
265
+ } catch (e: Exception) {
266
+ Log.e(TAG, "Error saving to disk cache for URL: $url", e)
267
+ }
268
+ }
269
+
270
+ return@withContext response
271
+ }
272
+ return@withContext null
273
+ } finally {
274
+ connection?.disconnect()
275
+ }
276
+ }
277
+
278
+ private data class CacheMetadata(
279
+ val mimeType: String,
280
+ val encoding: String,
281
+ val eTag: String? = null,
282
+ val lastModified: String? = null
283
+ ) : Serializable
284
+
285
+ private fun createResponseFromCache(cacheFile: File, metadata: CacheMetadata): WebResourceResponse {
286
+ return WebResourceResponse(
287
+ metadata.mimeType,
288
+ metadata.encoding,
289
+ BufferedInputStream(FileInputStream(cacheFile))
290
+ )
291
+ }
292
+
293
+ private fun generateCacheKey(url: String): String {
294
+ val md = MessageDigest.getInstance("MD5")
295
+ val digest = md.digest(url.toByteArray())
296
+ return digest.joinToString("") { "%02x".format(it) }
297
+ }
298
+
299
+ private fun isValidResponse(mimeType: String, responseBytes: ByteArray): Boolean {
300
+ return try {
301
+ if (responseBytes.isEmpty()) return false
302
+ if (mimeType.isEmpty()) return false
303
+
304
+ if (mimeType.startsWith("text/") ||
305
+ mimeType.contains("json") ||
306
+ mimeType.contains("javascript")
307
+ ) {
308
+ val content = String(responseBytes)
309
+ if (content.isBlank()) return false
310
+ }
311
+ true
312
+ } catch (e: Exception) {
313
+ Log.e(TAG, "Error validating response", e)
314
+ false
315
+ }
316
+ }
317
+
318
+ private fun saveMetadata(cacheKey: String, metadata: CacheMetadata) {
319
+ val metadataFile = File(cacheDir, "${cacheKey}.meta")
320
+ ObjectOutputStream(FileOutputStream(metadataFile)).use {
321
+ it.writeObject(metadata)
322
+ }
323
+ }
324
+
325
+ private fun loadMetadata(cacheKey: String): CacheMetadata {
326
+ val metadataFile = File(cacheDir, "${cacheKey}.meta")
327
+ return ObjectInputStream(FileInputStream(metadataFile)).use {
328
+ it.readObject() as CacheMetadata
329
+ }
330
+ }
331
+ }
@@ -0,0 +1,170 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:width="108dp"
4
+ android:height="108dp"
5
+ android:viewportWidth="108"
6
+ android:viewportHeight="108">
7
+ <path
8
+ android:fillColor="#3DDC84"
9
+ android:pathData="M0,0h108v108h-108z" />
10
+ <path
11
+ android:fillColor="#00000000"
12
+ android:pathData="M9,0L9,108"
13
+ android:strokeWidth="0.8"
14
+ android:strokeColor="#33FFFFFF" />
15
+ <path
16
+ android:fillColor="#00000000"
17
+ android:pathData="M19,0L19,108"
18
+ android:strokeWidth="0.8"
19
+ android:strokeColor="#33FFFFFF" />
20
+ <path
21
+ android:fillColor="#00000000"
22
+ android:pathData="M29,0L29,108"
23
+ android:strokeWidth="0.8"
24
+ android:strokeColor="#33FFFFFF" />
25
+ <path
26
+ android:fillColor="#00000000"
27
+ android:pathData="M39,0L39,108"
28
+ android:strokeWidth="0.8"
29
+ android:strokeColor="#33FFFFFF" />
30
+ <path
31
+ android:fillColor="#00000000"
32
+ android:pathData="M49,0L49,108"
33
+ android:strokeWidth="0.8"
34
+ android:strokeColor="#33FFFFFF" />
35
+ <path
36
+ android:fillColor="#00000000"
37
+ android:pathData="M59,0L59,108"
38
+ android:strokeWidth="0.8"
39
+ android:strokeColor="#33FFFFFF" />
40
+ <path
41
+ android:fillColor="#00000000"
42
+ android:pathData="M69,0L69,108"
43
+ android:strokeWidth="0.8"
44
+ android:strokeColor="#33FFFFFF" />
45
+ <path
46
+ android:fillColor="#00000000"
47
+ android:pathData="M79,0L79,108"
48
+ android:strokeWidth="0.8"
49
+ android:strokeColor="#33FFFFFF" />
50
+ <path
51
+ android:fillColor="#00000000"
52
+ android:pathData="M89,0L89,108"
53
+ android:strokeWidth="0.8"
54
+ android:strokeColor="#33FFFFFF" />
55
+ <path
56
+ android:fillColor="#00000000"
57
+ android:pathData="M99,0L99,108"
58
+ android:strokeWidth="0.8"
59
+ android:strokeColor="#33FFFFFF" />
60
+ <path
61
+ android:fillColor="#00000000"
62
+ android:pathData="M0,9L108,9"
63
+ android:strokeWidth="0.8"
64
+ android:strokeColor="#33FFFFFF" />
65
+ <path
66
+ android:fillColor="#00000000"
67
+ android:pathData="M0,19L108,19"
68
+ android:strokeWidth="0.8"
69
+ android:strokeColor="#33FFFFFF" />
70
+ <path
71
+ android:fillColor="#00000000"
72
+ android:pathData="M0,29L108,29"
73
+ android:strokeWidth="0.8"
74
+ android:strokeColor="#33FFFFFF" />
75
+ <path
76
+ android:fillColor="#00000000"
77
+ android:pathData="M0,39L108,39"
78
+ android:strokeWidth="0.8"
79
+ android:strokeColor="#33FFFFFF" />
80
+ <path
81
+ android:fillColor="#00000000"
82
+ android:pathData="M0,49L108,49"
83
+ android:strokeWidth="0.8"
84
+ android:strokeColor="#33FFFFFF" />
85
+ <path
86
+ android:fillColor="#00000000"
87
+ android:pathData="M0,59L108,59"
88
+ android:strokeWidth="0.8"
89
+ android:strokeColor="#33FFFFFF" />
90
+ <path
91
+ android:fillColor="#00000000"
92
+ android:pathData="M0,69L108,69"
93
+ android:strokeWidth="0.8"
94
+ android:strokeColor="#33FFFFFF" />
95
+ <path
96
+ android:fillColor="#00000000"
97
+ android:pathData="M0,79L108,79"
98
+ android:strokeWidth="0.8"
99
+ android:strokeColor="#33FFFFFF" />
100
+ <path
101
+ android:fillColor="#00000000"
102
+ android:pathData="M0,89L108,89"
103
+ android:strokeWidth="0.8"
104
+ android:strokeColor="#33FFFFFF" />
105
+ <path
106
+ android:fillColor="#00000000"
107
+ android:pathData="M0,99L108,99"
108
+ android:strokeWidth="0.8"
109
+ android:strokeColor="#33FFFFFF" />
110
+ <path
111
+ android:fillColor="#00000000"
112
+ android:pathData="M19,29L89,29"
113
+ android:strokeWidth="0.8"
114
+ android:strokeColor="#33FFFFFF" />
115
+ <path
116
+ android:fillColor="#00000000"
117
+ android:pathData="M19,39L89,39"
118
+ android:strokeWidth="0.8"
119
+ android:strokeColor="#33FFFFFF" />
120
+ <path
121
+ android:fillColor="#00000000"
122
+ android:pathData="M19,49L89,49"
123
+ android:strokeWidth="0.8"
124
+ android:strokeColor="#33FFFFFF" />
125
+ <path
126
+ android:fillColor="#00000000"
127
+ android:pathData="M19,59L89,59"
128
+ android:strokeWidth="0.8"
129
+ android:strokeColor="#33FFFFFF" />
130
+ <path
131
+ android:fillColor="#00000000"
132
+ android:pathData="M19,69L89,69"
133
+ android:strokeWidth="0.8"
134
+ android:strokeColor="#33FFFFFF" />
135
+ <path
136
+ android:fillColor="#00000000"
137
+ android:pathData="M19,79L89,79"
138
+ android:strokeWidth="0.8"
139
+ android:strokeColor="#33FFFFFF" />
140
+ <path
141
+ android:fillColor="#00000000"
142
+ android:pathData="M29,19L29,89"
143
+ android:strokeWidth="0.8"
144
+ android:strokeColor="#33FFFFFF" />
145
+ <path
146
+ android:fillColor="#00000000"
147
+ android:pathData="M39,19L39,89"
148
+ android:strokeWidth="0.8"
149
+ android:strokeColor="#33FFFFFF" />
150
+ <path
151
+ android:fillColor="#00000000"
152
+ android:pathData="M49,19L49,89"
153
+ android:strokeWidth="0.8"
154
+ android:strokeColor="#33FFFFFF" />
155
+ <path
156
+ android:fillColor="#00000000"
157
+ android:pathData="M59,19L59,89"
158
+ android:strokeWidth="0.8"
159
+ android:strokeColor="#33FFFFFF" />
160
+ <path
161
+ android:fillColor="#00000000"
162
+ android:pathData="M69,19L69,89"
163
+ android:strokeWidth="0.8"
164
+ android:strokeColor="#33FFFFFF" />
165
+ <path
166
+ android:fillColor="#00000000"
167
+ android:pathData="M79,19L79,89"
168
+ android:strokeWidth="0.8"
169
+ android:strokeColor="#33FFFFFF" />
170
+ </vector>
@@ -0,0 +1,30 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ xmlns:aapt="http://schemas.android.com/aapt"
3
+ android:width="108dp"
4
+ android:height="108dp"
5
+ android:viewportWidth="108"
6
+ android:viewportHeight="108">
7
+ <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
8
+ <aapt:attr name="android:fillColor">
9
+ <gradient
10
+ android:endX="85.84757"
11
+ android:endY="92.4963"
12
+ android:startX="42.9492"
13
+ android:startY="49.59793"
14
+ android:type="linear">
15
+ <item
16
+ android:color="#44000000"
17
+ android:offset="0.0" />
18
+ <item
19
+ android:color="#00000000"
20
+ android:offset="1.0" />
21
+ </gradient>
22
+ </aapt:attr>
23
+ </path>
24
+ <path
25
+ android:fillColor="#FFFFFF"
26
+ android:fillType="nonZero"
27
+ android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
28
+ android:strokeWidth="1"
29
+ android:strokeColor="#00000000" />
30
+ </vector>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:layout_width="match_parent"
4
+ android:layout_height="match_parent">
5
+ <RelativeLayout
6
+ android:layout_height="match_parent"
7
+ android:layout_width="match_parent">
8
+
9
+ <WebView
10
+ android:id="@+id/webview"
11
+ android:layout_height="match_parent"
12
+ android:layout_width="match_parent" />
13
+
14
+ <ProgressBar
15
+ android:id="@+id/progress"
16
+ android:layout_centerInParent="true"
17
+ android:layout_height="wrap_content"
18
+ android:layout_width="wrap_content"
19
+ android:visibility="gone" />
20
+ </RelativeLayout>
21
+
22
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <background android:drawable="@drawable/ic_launcher_background" />
4
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
5
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
6
+ </adaptive-icon>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+ <background android:drawable="@drawable/ic_launcher_background" />
4
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
5
+ <monochrome android:drawable="@drawable/ic_launcher_foreground" />
6
+ </adaptive-icon>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <color name="purple_200">#FFBB86FC</color>
4
+ <color name="purple_500">#FF6200EE</color>
5
+ <color name="purple_700">#FF3700B3</color>
6
+ <color name="teal_200">#FF03DAC5</color>
7
+ <color name="teal_700">#FF018786</color>
8
+ <color name="black">#FF000000</color>
9
+ <color name="white">#FFFFFFFF</color>
10
+ </resources>
@@ -0,0 +1,3 @@
1
+ <resources>
2
+ <string name="app_name">My Application</string>
3
+ </resources>
@@ -0,0 +1,15 @@
1
+ <resources xmlns:tools="http://schemas.android.com/tools">
2
+ <!-- Base application theme. -->
3
+ <style name="Theme.AndroidProject" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
4
+ <!-- Primary brand color. -->
5
+ <item name="colorPrimary">@color/purple_500</item>
6
+ <item name="colorPrimaryVariant">@color/purple_700</item>
7
+ <item name="colorOnPrimary">@color/white</item>
8
+ <!-- Secondary brand color. -->
9
+ <item name="colorSecondary">@color/teal_200</item>
10
+ <item name="colorSecondaryVariant">@color/teal_700</item>
11
+ <item name="colorOnSecondary">@color/black</item>
12
+ <!-- Status bar color. -->
13
+ <!-- Customize your theme here. -->
14
+ </style>
15
+ </resources>
@@ -0,0 +1,15 @@
1
+ <resources xmlns:tools="http://schemas.android.com/tools">
2
+ <!-- Base application theme. -->
3
+ <style name="Theme.AndroidProject" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
4
+ <!-- Primary brand color. -->
5
+ <item name="colorPrimary">@color/purple_500</item>
6
+ <item name="colorPrimaryVariant">@color/purple_700</item>
7
+ <item name="colorOnPrimary">@color/white</item>
8
+ <!-- Secondary brand color. -->
9
+ <item name="colorSecondary">@color/teal_200</item>
10
+ <item name="colorSecondaryVariant">@color/teal_700</item>
11
+ <item name="colorOnSecondary">@color/black</item>
12
+ <!-- Status bar color. -->
13
+ <!-- Customize your theme here. -->
14
+ </style>
15
+ </resources>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!--
2
+ Sample backup rules file; uncomment and customize as necessary.
3
+ See https://developer.android.com/guide/topics/data/autobackup
4
+ for details.
5
+ Note: This file is ignored for devices older that API 31
6
+ See https://developer.android.com/about/versions/12/backup-restore
7
+ -->
8
+ <full-backup-content>
9
+ <!--
10
+ <include domain="sharedpref" path="."/>
11
+ <exclude domain="sharedpref" path="device.xml"/>
12
+ -->
13
+ </full-backup-content>