catalyst-core 0.2.0-beta.1 → 0.2.0-beta.3
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/dist/native/androidProject/app/build.gradle.kts +26 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +381 -78
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +29 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +143 -17
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +7 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/OfflineCacheService.kt +374 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +162 -26
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +56 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +48 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfDeliveryState.kt +36 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/PerfEventBuffer.kt +289 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/ProfilerNavigationState.kt +13 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CustomWebviewTest.kt +7 -2
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ProfilerLifecycleTest.kt +51 -0
- package/dist/native/bridge/WebBridge.js +2 -2
- package/dist/native/bridge/constants/NativeInterfaces.js +2 -1
- package/dist/native/bridge/perf/collectors/ApiCollector.js +22 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +40 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +43 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +47 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +38 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +25 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +53 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +7 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +40 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +25 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +27 -0
- package/dist/native/bridge/perf/core/constants.js +43 -0
- package/dist/native/bridge/perf/core/measure.js +40 -0
- package/dist/native/bridge/perf/core/session.js +34 -0
- package/dist/native/bridge/perf/core/store.js +1 -0
- package/dist/native/bridge/perf/core/summary.js +1 -0
- package/dist/native/bridge/perf/core/traceExport.js +1 -0
- package/dist/native/bridge/perf/core/waterfalls.js +1 -0
- package/dist/native/bridge/perf/index.js +81 -0
- package/dist/native/buildAndroid/assets.js +2 -2
- package/dist/native/buildIos/config.js +1 -1
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +19 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +4 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +5 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +49 -4
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineCacheService.swift +683 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/OfflineURLSchemeHandler.swift +94 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +19 -6
- package/dist/native/iosnativeWebView/Sources/Core/Utils/SafeAreaUtils.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +35 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +24 -11
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +9 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +141 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +157 -3
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
- package/dist/native/renameAndroidProject.js +2 -2
- package/dist/offline/catalyst-sw.js +1 -0
- package/dist/offline/registerServiceWorker.js +1 -0
- package/dist/router/ClientRouter.js +1 -1
- package/dist/scripts/build.js +1 -1
- package/dist/scripts/devBuild.js +1 -1
- package/dist/scripts/generateOfflineManifest.js +1 -0
- package/dist/server/expressServer.js +2 -1
- package/dist/server/renderer/document/Body.js +1 -1
- package/dist/server/renderer/handler.js +3 -3
- package/dist/webpack/base.babel.js +1 -1
- package/mcp_v2/knowledge-base.json +13 -0
- package/package.json +1 -1
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -136,6 +136,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
136
136
|
"X-Safe-Area-Right" to latestSafeAreaInsets.right.toString(),
|
|
137
137
|
"X-Safe-Area-Bottom" to latestSafeAreaInsets.bottom.toString(),
|
|
138
138
|
"X-Safe-Area-Left" to latestSafeAreaInsets.left.toString(),
|
|
139
|
+
"X-Catalyst-Native-WebView" to "1",
|
|
139
140
|
// Prevent caching of SSR response so updated headers are always used
|
|
140
141
|
"Cache-Control" to "no-cache, no-store, must-revalidate",
|
|
141
142
|
"Pragma" to "no-cache"
|
|
@@ -273,6 +274,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
273
274
|
properties.setProperty("buildType", if (BuildConfig.DEBUG) "debug" else "release")
|
|
274
275
|
properties.setProperty("buildOptimisation", (!BuildConfig.DEBUG).toString())
|
|
275
276
|
}
|
|
277
|
+
io.yourname.androidproject.utils.PerfEventBuffer.configure(
|
|
278
|
+
properties.getProperty("profiler.enabled", "false").toBoolean()
|
|
279
|
+
)
|
|
276
280
|
|
|
277
281
|
configureEdgeToEdge()
|
|
278
282
|
|
|
@@ -285,16 +289,29 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
285
289
|
})
|
|
286
290
|
}
|
|
287
291
|
|
|
288
|
-
// Initialize MetricsMonitor
|
|
292
|
+
// Initialize MetricsMonitor — recordAppStart() resets the cold-start clock for this
|
|
293
|
+
// Activity lifecycle. The singleton may have been constructed in a previous session
|
|
294
|
+
// (hot restart / Activity recreation), so appStartTime would be stale without this.
|
|
289
295
|
metricsMonitor = MetricsMonitor.getInstance(this)
|
|
296
|
+
metricsMonitor.recordAppStart()
|
|
297
|
+
|
|
298
|
+
// Boot timing: record activity onCreate as the earliest boot event
|
|
299
|
+
if (BuildConfig.DEBUG) {
|
|
300
|
+
io.yourname.androidproject.utils.PerfEventBuffer.reset()
|
|
301
|
+
io.yourname.androidproject.utils.PerfEventBuffer.add(org.json.JSONObject().apply {
|
|
302
|
+
put("type", "boot-activity-created")
|
|
303
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
304
|
+
put("thread", Thread.currentThread().name)
|
|
305
|
+
})
|
|
306
|
+
}
|
|
290
307
|
|
|
291
308
|
// Setup UI
|
|
292
309
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
293
310
|
supportActionBar?.hide()
|
|
294
311
|
setContentView(binding.root)
|
|
295
312
|
|
|
296
|
-
// Initialize keyboard utility
|
|
297
|
-
keyboardUtil = KeyboardUtil(this, binding.webviewContainer)
|
|
313
|
+
// Initialize keyboard utility (pass webView so keyboard events reach WebPerfCollector)
|
|
314
|
+
keyboardUtil = KeyboardUtil(this, binding.webviewContainer, binding.webview)
|
|
298
315
|
keyboardUtil.initialize()
|
|
299
316
|
|
|
300
317
|
// Enable hardware acceleration for the window
|
|
@@ -350,6 +367,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
350
367
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
|
351
368
|
override fun handleOnBackPressed() {
|
|
352
369
|
if (customWebView.canGoBack()) {
|
|
370
|
+
if (BuildConfig.DEBUG) {
|
|
371
|
+
BridgeUtils.emitPerfEvent(customWebView.getWebView(), org.json.JSONObject().apply {
|
|
372
|
+
put("type", "navigation-back")
|
|
373
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
374
|
+
})
|
|
375
|
+
}
|
|
353
376
|
customWebView.goBack()
|
|
354
377
|
} else {
|
|
355
378
|
// Disable this callback and let the system handle back press
|
|
@@ -385,12 +408,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
385
408
|
if (BuildConfig.DEBUG) {
|
|
386
409
|
Log.w(TAG, "📴 Device offline on launch, showing offline page")
|
|
387
410
|
}
|
|
388
|
-
customWebView.
|
|
411
|
+
customWebView.showOfflineRouteOrOfflinePage(currentUrl)
|
|
389
412
|
}
|
|
390
413
|
}
|
|
391
|
-
|
|
392
|
-
metricsMonitor.markAppStartComplete()
|
|
393
|
-
|
|
394
414
|
} catch (e: Exception) {
|
|
395
415
|
Log.e(TAG, "Failed to load initial URL: ${e.message}")
|
|
396
416
|
// TODO: Add HTML file workflow - error.html not available yet
|
|
@@ -452,6 +472,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
452
472
|
}
|
|
453
473
|
|
|
454
474
|
override fun onDestroy() {
|
|
475
|
+
io.yourname.androidproject.utils.PerfEventBuffer.reset()
|
|
455
476
|
// Log all performance metrics before destroying
|
|
456
477
|
if (BuildConfig.DEBUG) {
|
|
457
478
|
Log.d(TAG, "🏁 App shutting down - logging final metrics...")
|
|
@@ -533,7 +554,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
533
554
|
loadUrlWithSafeArea(url)
|
|
534
555
|
} else {
|
|
535
556
|
Log.w(TAG, "📴 Offline during notification click, showing offline page")
|
|
536
|
-
customWebView.
|
|
557
|
+
customWebView.showOfflineRouteOrOfflinePage(url)
|
|
537
558
|
}
|
|
538
559
|
|
|
539
560
|
} catch (e: Exception) {
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt
CHANGED
|
@@ -5,6 +5,10 @@ import android.os.Debug
|
|
|
5
5
|
import android.os.SystemClock
|
|
6
6
|
import android.util.Log
|
|
7
7
|
import android.view.Choreographer
|
|
8
|
+
import android.webkit.WebView
|
|
9
|
+
import io.yourname.androidproject.utils.BridgeUtils
|
|
10
|
+
import io.yourname.androidproject.utils.PerfEventBuffer
|
|
11
|
+
import org.json.JSONObject
|
|
8
12
|
import java.util.concurrent.TimeUnit
|
|
9
13
|
import java.util.concurrent.atomic.AtomicInteger
|
|
10
14
|
|
|
@@ -13,11 +17,29 @@ import java.util.concurrent.atomic.AtomicInteger
|
|
|
13
17
|
* Tracks key performance metrics and logs them on app destroy.
|
|
14
18
|
*/
|
|
15
19
|
class MetricsMonitor(private val context: Context) {
|
|
20
|
+
|
|
21
|
+
// WebView reference for emitting perf events — set via attachWebView()
|
|
22
|
+
@Volatile private var webView: WebView? = null
|
|
23
|
+
|
|
24
|
+
fun attachWebView(wv: WebView) {
|
|
25
|
+
webView = wv
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fun detachWebView() {
|
|
29
|
+
// Flush any open fps-drop episode before losing the WebView reference
|
|
30
|
+
if (inFpsDropEpisode) emitFpsDropEpisode(endTime = SystemClock.elapsedRealtime())
|
|
31
|
+
webView = null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private fun emitPerf(payload: JSONObject) {
|
|
35
|
+
webView?.let { BridgeUtils.emitPerfEvent(it, payload) }
|
|
36
|
+
}
|
|
16
37
|
private val TAG = "MetricsMonitor"
|
|
17
38
|
|
|
18
39
|
// App Startup metrics
|
|
19
40
|
private var appStartTime: Long = SystemClock.elapsedRealtime()
|
|
20
41
|
private var coldStartDuration: Long = 0
|
|
42
|
+
private var coldStartCompleted = false
|
|
21
43
|
|
|
22
44
|
// Page Load metrics
|
|
23
45
|
private val pageLoadTimes = mutableMapOf<String, Long>()
|
|
@@ -34,6 +56,15 @@ class MetricsMonitor(private val context: Context) {
|
|
|
34
56
|
private var avgFps = 0.0
|
|
35
57
|
private var fpsReadings = 0
|
|
36
58
|
|
|
59
|
+
// fps-drop episode tracking
|
|
60
|
+
private var inFpsDropEpisode = false
|
|
61
|
+
private var episodeStartNativeTime: Long = 0
|
|
62
|
+
private var episodeMinFps = Double.MAX_VALUE
|
|
63
|
+
private var episodeFpsSum = 0.0
|
|
64
|
+
private var episodeSampleCount = 0
|
|
65
|
+
private val FPS_EPISODE_MAX_MS = 10_000L // force-close after 10s
|
|
66
|
+
private var lastStableFps = 60.0 // fps reading just before episode started — used to compute deltaFps
|
|
67
|
+
|
|
37
68
|
// Memory metrics
|
|
38
69
|
private var initialMemory: Long = getMemoryUsage()
|
|
39
70
|
private var peakMemory: Long = initialMemory
|
|
@@ -74,8 +105,18 @@ class MetricsMonitor(private val context: Context) {
|
|
|
74
105
|
}
|
|
75
106
|
|
|
76
107
|
fun markAppStartComplete() {
|
|
108
|
+
if (coldStartCompleted) return
|
|
109
|
+
coldStartCompleted = true
|
|
77
110
|
coldStartDuration = SystemClock.elapsedRealtime() - appStartTime
|
|
78
111
|
Log.d(TAG, "App cold start completed in: $coldStartDuration ms")
|
|
112
|
+
// Buffer so it is delivered with the post-load batch flush when JS is ready.
|
|
113
|
+
// emitPerf (live path) would be a no-op here — JS may not have loaded yet.
|
|
114
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
115
|
+
put("type", "cold-start")
|
|
116
|
+
put("nativeTime", SystemClock.elapsedRealtime())
|
|
117
|
+
put("durationMs", coldStartDuration)
|
|
118
|
+
put("thread", Thread.currentThread().name)
|
|
119
|
+
})
|
|
79
120
|
}
|
|
80
121
|
|
|
81
122
|
fun trackPageLoadStart(url: String) {
|
|
@@ -144,8 +185,14 @@ class MetricsMonitor(private val context: Context) {
|
|
|
144
185
|
uiBlockingEvents++
|
|
145
186
|
}
|
|
146
187
|
|
|
147
|
-
fun recordLongTask() {
|
|
188
|
+
fun recordLongTask(durationMs: Long = longTaskThreshold) {
|
|
148
189
|
longTasksDetected++
|
|
190
|
+
emitPerf(JSONObject().apply {
|
|
191
|
+
put("type", "long-task")
|
|
192
|
+
put("nativeTime", SystemClock.elapsedRealtime())
|
|
193
|
+
put("durationMs", durationMs)
|
|
194
|
+
put("thread", Thread.currentThread().name)
|
|
195
|
+
})
|
|
149
196
|
}
|
|
150
197
|
|
|
151
198
|
private fun startFrameMonitoring() {
|
|
@@ -173,27 +220,106 @@ class MetricsMonitor(private val context: Context) {
|
|
|
173
220
|
val timeSpan = now - lastFpsCheckTime
|
|
174
221
|
lastFpsCheckTime = now
|
|
175
222
|
|
|
176
|
-
if (timeSpan
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
223
|
+
if (timeSpan <= 0) return
|
|
224
|
+
|
|
225
|
+
currentFps = frames * 1000.0 / timeSpan
|
|
226
|
+
|
|
227
|
+
// Update session-level min/max/avg
|
|
228
|
+
if (minFps == Double.MAX_VALUE) minFps = currentFps
|
|
229
|
+
minFps = minFps.coerceAtMost(currentFps)
|
|
230
|
+
maxFps = maxFps.coerceAtLeast(currentFps)
|
|
231
|
+
val totalFps = avgFps * fpsReadings + currentFps
|
|
232
|
+
fpsReadings++
|
|
233
|
+
avgFps = totalFps / fpsReadings
|
|
234
|
+
|
|
235
|
+
if (currentFps < 55.0) {
|
|
236
|
+
if (!inFpsDropEpisode) {
|
|
237
|
+
// Start new episode — capture the last stable fps as baseline for deltaFps
|
|
238
|
+
inFpsDropEpisode = true
|
|
239
|
+
episodeStartNativeTime = now - timeSpan // start of the measured window
|
|
240
|
+
episodeMinFps = currentFps
|
|
241
|
+
episodeFpsSum = currentFps
|
|
242
|
+
episodeSampleCount = 1
|
|
243
|
+
// lastStableFps already holds the previous reading (set in the else branch below)
|
|
244
|
+
} else {
|
|
245
|
+
// Accumulate into current episode
|
|
246
|
+
episodeMinFps = episodeMinFps.coerceAtMost(currentFps)
|
|
247
|
+
episodeFpsSum += currentFps
|
|
248
|
+
episodeSampleCount++
|
|
249
|
+
|
|
250
|
+
// Force-close if episode has run for >= 10s
|
|
251
|
+
val episodeDuration = now - episodeStartNativeTime
|
|
252
|
+
if (episodeDuration >= FPS_EPISODE_MAX_MS) {
|
|
253
|
+
emitFpsDropEpisode(endTime = now)
|
|
254
|
+
// Immediately start a new episode for the continuing drop
|
|
255
|
+
inFpsDropEpisode = true
|
|
256
|
+
episodeStartNativeTime = now
|
|
257
|
+
episodeMinFps = currentFps
|
|
258
|
+
episodeFpsSum = currentFps
|
|
259
|
+
episodeSampleCount = 1
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
// FPS is good — update lastStableFps and close episode if one was open
|
|
264
|
+
lastStableFps = currentFps
|
|
265
|
+
if (inFpsDropEpisode) {
|
|
266
|
+
emitFpsDropEpisode(endTime = now)
|
|
267
|
+
}
|
|
187
268
|
}
|
|
188
269
|
}
|
|
189
270
|
|
|
271
|
+
private fun emitFpsDropEpisode(endTime: Long) {
|
|
272
|
+
if (!inFpsDropEpisode) return
|
|
273
|
+
val durationMs = endTime - episodeStartNativeTime
|
|
274
|
+
val avgEpisodeFps = if (episodeSampleCount > 0) episodeFpsSum / episodeSampleCount else episodeMinFps
|
|
275
|
+
// deltaFps = how far FPS fell from the last stable reading before the episode
|
|
276
|
+
val deltaFps = lastStableFps - episodeMinFps
|
|
277
|
+
emitPerf(JSONObject().apply {
|
|
278
|
+
put("type", "fps-drop-episode")
|
|
279
|
+
put("startNativeTime", episodeStartNativeTime)
|
|
280
|
+
put("endNativeTime", endTime)
|
|
281
|
+
put("durationMs", durationMs)
|
|
282
|
+
put("minFps", episodeMinFps)
|
|
283
|
+
put("avgFps", avgEpisodeFps)
|
|
284
|
+
put("deltaFps", deltaFps) // drop magnitude from last stable fps
|
|
285
|
+
put("baselineFps", lastStableFps)
|
|
286
|
+
put("thread", Thread.currentThread().name)
|
|
287
|
+
})
|
|
288
|
+
inFpsDropEpisode = false
|
|
289
|
+
episodeMinFps = Double.MAX_VALUE
|
|
290
|
+
episodeFpsSum = 0.0
|
|
291
|
+
episodeSampleCount = 0
|
|
292
|
+
}
|
|
293
|
+
|
|
190
294
|
private fun monitorMemoryPeriodically() {
|
|
191
295
|
Thread {
|
|
192
296
|
while (!Thread.interrupted()) {
|
|
193
297
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
298
|
+
val memInfo = Debug.MemoryInfo()
|
|
299
|
+
Debug.getMemoryInfo(memInfo)
|
|
300
|
+
|
|
301
|
+
// PSS breakdown — all values in KB, convert to MB
|
|
302
|
+
val jvmMb = memInfo.dalvikPss / 1024.0 // Kotlin/Java heap
|
|
303
|
+
val webviewMb = memInfo.nativePss / 1024.0 // V8 + Blink + JNI (WebView proxy)
|
|
304
|
+
val otherMb = memInfo.otherPss / 1024.0 // graphics buffers, code, stack
|
|
305
|
+
val totalMb = memInfo.totalPss / 1024.0 // what Android OOM killer sees
|
|
306
|
+
|
|
307
|
+
currentMemory = memInfo.totalPss.toLong()
|
|
308
|
+
peakMemory = peakMemory.coerceAtLeast(currentMemory)
|
|
309
|
+
|
|
310
|
+
val now = SystemClock.elapsedRealtime()
|
|
311
|
+
emitPerf(JSONObject().apply {
|
|
312
|
+
put("type", "memory-snapshot")
|
|
313
|
+
put("nativeTime", now)
|
|
314
|
+
put("jvmMb", jvmMb)
|
|
315
|
+
put("webviewMb", webviewMb)
|
|
316
|
+
put("otherMb", otherMb)
|
|
317
|
+
put("totalMb", totalMb)
|
|
318
|
+
put("peakMb", peakMemory / 1024.0)
|
|
319
|
+
put("thread", Thread.currentThread().name)
|
|
320
|
+
if (coldStartDuration > 0) put("coldStartMs", coldStartDuration)
|
|
321
|
+
})
|
|
322
|
+
Thread.sleep(5000)
|
|
197
323
|
} catch (e: InterruptedException) {
|
|
198
324
|
break
|
|
199
325
|
}
|
|
@@ -324,7 +450,7 @@ class MetricsMonitor(private val context: Context) {
|
|
|
324
450
|
}
|
|
325
451
|
|
|
326
452
|
fun cleanup() {
|
|
327
|
-
|
|
453
|
+
if (inFpsDropEpisode) emitFpsDropEpisode(endTime = SystemClock.elapsedRealtime())
|
|
328
454
|
Choreographer.getInstance().removeFrameCallback(frameCallback)
|
|
329
455
|
}
|
|
330
456
|
|
|
@@ -338,4 +464,4 @@ class MetricsMonitor(private val context: Context) {
|
|
|
338
464
|
}
|
|
339
465
|
}
|
|
340
466
|
}
|
|
341
|
-
}
|
|
467
|
+
}
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt
CHANGED
|
@@ -213,7 +213,7 @@ class NativeBridge(
|
|
|
213
213
|
.filter { it.isNotEmpty() }
|
|
214
214
|
|
|
215
215
|
accessControlEnabled = properties
|
|
216
|
-
.getProperty("accessControl.enabled", "
|
|
216
|
+
.getProperty("accessControl.enabled", "false")
|
|
217
217
|
.equals("true", ignoreCase = true)
|
|
218
218
|
|
|
219
219
|
if (accessControlEnabled && allowedUrls.isNotEmpty()) {
|
|
@@ -332,8 +332,11 @@ class NativeBridge(
|
|
|
332
332
|
|
|
333
333
|
@JavascriptInterface
|
|
334
334
|
fun openCamera(options: String?) {
|
|
335
|
+
val callId = "openCamera:${android.os.SystemClock.elapsedRealtime()}"
|
|
336
|
+
BridgeUtils.bridgeCallReceived(callId, "openCamera")
|
|
335
337
|
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "open camera") {
|
|
336
338
|
mainActivity.runOnUiThread {
|
|
339
|
+
BridgeUtils.bridgeCallDispatched(callId)
|
|
337
340
|
if (CameraUtils.hasCameraPermission(mainActivity)) {
|
|
338
341
|
launchCamera()
|
|
339
342
|
} else {
|
|
@@ -492,10 +495,13 @@ class NativeBridge(
|
|
|
492
495
|
|
|
493
496
|
@JavascriptInterface
|
|
494
497
|
fun getDeviceInfo(options: String?) {
|
|
498
|
+
val callId = "getDeviceInfo:${android.os.SystemClock.elapsedRealtime()}"
|
|
499
|
+
BridgeUtils.bridgeCallReceived(callId, "getDeviceInfo")
|
|
495
500
|
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_ERROR, "get device info") {
|
|
496
501
|
mainActivity.runOnUiThread {
|
|
497
502
|
val deviceInfo = DeviceInfoUtils.getDeviceInfo(mainActivity, properties)
|
|
498
503
|
BridgeUtils.logDebug(TAG, "Device info retrieved: $deviceInfo")
|
|
504
|
+
BridgeUtils.bridgeCallDispatched(callId)
|
|
499
505
|
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_SUCCESS, deviceInfo.toString())
|
|
500
506
|
}
|
|
501
507
|
}
|