catalyst-core 0.2.0-beta.2 → 0.3.0-beta.1
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.native.config.cjs +8 -0
- package/bin/catalyst.js +49 -105
- package/changelog.md +8 -0
- package/dist/caching.js +1 -1
- package/dist/index.jsx +8 -0
- package/dist/logger.js +74 -3
- package/dist/native/androidProject/app/build.gradle.kts +1 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +143 -4
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +26 -6
- 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 +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +26 -0
- 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/ProfilerLifecycleTest.kt +51 -0
- package/dist/native/androidSetup.js +300 -8
- package/dist/native/bridge/WebBridge.js +481 -59
- package/dist/native/bridge/constants/NativeInterfaces.js +290 -44
- package/dist/native/bridge/errors.js +250 -26
- package/dist/native/bridge/hooks/index.js +125 -1
- package/dist/native/bridge/hooks/useCamera.js +347 -9
- package/dist/native/bridge/hooks/useDataProtection.js +200 -2
- package/dist/native/bridge/hooks/useDeviceInfo.js +71 -2
- package/dist/native/bridge/hooks/useFilePicker.js +429 -2
- package/dist/native/bridge/hooks/useGoogleSignIn.js +81 -1
- package/dist/native/bridge/hooks/useHapticFeedback.js +196 -1
- package/dist/native/bridge/hooks/useIntent.js +95 -1
- package/dist/native/bridge/hooks/useNativeTransition.js +202 -27
- package/dist/native/bridge/hooks/useNetworkStatus.js +84 -3
- package/dist/native/bridge/hooks/useNotification.js +318 -2
- package/dist/native/bridge/hooks/useSafeArea.js +84 -2
- package/dist/native/bridge/hooks/useVideoStream.js +434 -6
- package/dist/native/bridge/hooks/utils.js +27 -1
- package/dist/native/bridge/hooks.js +16 -1
- package/dist/native/bridge/perf/collectors/ApiCollector.js +85 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +215 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +165 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +505 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +273 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +124 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +294 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +183 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +284 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +111 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +111 -0
- package/dist/native/bridge/perf/core/constants.js +126 -0
- package/dist/native/bridge/perf/core/measure.js +189 -0
- package/dist/native/bridge/perf/core/session.js +100 -0
- package/dist/native/bridge/perf/core/store.js +554 -0
- package/dist/native/bridge/perf/core/summary.js +174 -0
- package/dist/native/bridge/perf/core/traceExport.js +240 -0
- package/dist/native/bridge/perf/core/waterfalls.js +146 -0
- package/dist/native/bridge/perf/index.js +402 -0
- package/dist/native/bridge/safeArea.js +53 -5
- package/dist/native/bridge/standardizedHooks.js +595 -82
- package/dist/native/bridge/useBaseHook.js +274 -38
- package/dist/native/bridge/utils/CameraUtils.js +201 -56
- package/dist/native/bridge/utils/FileObjectConverter.js +129 -18
- package/dist/native/bridge/utils/NativeBridge.js +425 -141
- package/dist/native/buildAndroid/assets.js +510 -13
- package/dist/native/buildAndroid/build.js +296 -19
- package/dist/native/buildAndroid/config.js +87 -1
- package/dist/native/buildAndroid/index.js +374 -1
- package/dist/native/buildAppAndroid.js +34 -2
- package/dist/native/buildAppIos.js +62 -2
- package/dist/native/buildIos/assets.js +609 -8
- package/dist/native/buildIos/build.js +639 -20
- package/dist/native/buildIos/config.js +294 -12
- package/dist/native/buildIos/index.js +343 -6
- package/dist/native/buildIos/plugins.js +343 -16
- package/dist/native/internal-plugins/device-info-plugin/android/DeviceInfoPlugin.kt +43 -0
- package/dist/native/internal-plugins/device-info-plugin/ios/DeviceInfoPlugin.swift +28 -0
- package/dist/native/internal-plugins/device-info-plugin/manifest.json +19 -0
- package/dist/native/internalPluginUtils.js +232 -7
- 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 +10 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DisplayLinkPerfMonitor.swift +89 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +373 -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 +126 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +75 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +13 -0
- package/dist/native/package.json +3 -0
- package/dist/native/plugin-bridge/PluginBridge.js +227 -1
- package/dist/native/pluginComposerAndroid.js +407 -23
- package/dist/native/pluginComposerIos.js +435 -11
- package/dist/native/renameAndroidProject.js +1094 -88
- package/dist/native/setupEmulatorIos.js +289 -22
- package/dist/native/setupServer.js +160 -19
- package/dist/native/terminalProgress.js +199 -11
- package/dist/native/utils.js +193 -15
- package/dist/offline/catalyst-sw.js +145 -1
- package/dist/offline/registerServiceWorker.js +33 -1
- package/dist/otel.js +375 -46
- package/dist/router/ClientRouter.js +14 -2
- package/dist/router/ServerRouter.js +9 -1
- package/dist/router.js +1 -1
- package/dist/scripts/build.js +112 -3
- package/dist/scripts/checkVersion.js +3 -1
- package/dist/scripts/cleanCache.js +30 -1
- package/dist/scripts/generateOfflineManifest.js +116 -1
- package/dist/scripts/loadEnvironmentVariables.js +32 -6
- package/dist/scripts/plugins.js +309 -1
- package/dist/scripts/preServerInit.js +13 -0
- package/dist/scripts/scriptUtils.js +54 -3
- package/dist/scripts/serve.js +51 -4
- package/dist/scripts/start.js +47 -10
- package/dist/scripts/validator.js +149 -1
- package/dist/sentry.cjs +127 -0
- package/dist/sentry.js +125 -5
- package/dist/server/expressServer.js +250 -5
- package/dist/server/manifestCache.js +36 -0
- package/dist/server/renderer/ChunkExtractor.js +144 -0
- package/dist/server/renderer/document/Body.jsx +59 -0
- package/dist/server/renderer/document/FastRefresh.jsx +20 -0
- package/dist/server/renderer/document/Head.jsx +60 -0
- package/dist/server/renderer/document/index.jsx +2 -0
- package/dist/server/renderer/extract.js +149 -31
- package/dist/server/renderer/handler.jsx +433 -0
- package/dist/server/renderer/index.js +8 -1
- package/dist/server/renderer/render.js +47 -0
- package/dist/server/utils/botDetectionMiddleware.js +29 -0
- package/dist/server/utils/cjsRequire.js +10 -0
- package/dist/server/utils/userAgentUtil.js +87 -9
- package/dist/server/utils/validator.js +164 -1
- package/dist/vite/FastRefresh.jsx +20 -0
- package/dist/vite/inject-cache-key-plugin.js +276 -0
- package/dist/vite/loadCustomViteConfig.js +22 -0
- package/dist/vite/manifest-categorization-plugin.js +455 -0
- package/dist/vite/node-loader.mjs +458 -0
- package/dist/vite/scssParams.js +21 -0
- package/dist/vite/vite.config.client.js +130 -0
- package/dist/vite/vite.config.js +422 -0
- package/dist/vite/vite.config.server.js +84 -0
- package/dist/web-router/components/MetaTag.jsx +34 -0
- package/dist/web-router/components/RouterDataProvider.jsx +332 -0
- package/dist/web-router/components/Split.jsx +200 -0
- package/dist/web-router/components/SplitInview.jsx +106 -0
- package/dist/web-router/components/SsrRequestContext.jsx +14 -0
- package/dist/web-router/context.jsx +12 -0
- package/dist/web-router/hooks.jsx +72 -0
- package/dist/web-router/types/components/MetaTag.d.ts +2 -0
- package/dist/web-router/types/components/RouterDataProvider.d.ts +95 -0
- package/dist/web-router/types/context.d.ts +14 -0
- package/dist/web-router/types/hooks.d.ts +1 -0
- package/dist/web-router/types/index.d.ts +3 -0
- package/dist/web-router/types/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +119 -0
- package/mcp_v2/conversion-tasks.json +35 -17
- package/mcp_v2/knowledge-base.json +124 -13
- package/mcp_v2/lib/helpers.js +10 -0
- package/mcp_v2/mcp.js +4 -4
- package/mcp_v2/package.json +1 -0
- package/mcp_v2/tools/build.js +116 -20
- package/mcp_v2/tools/conversion.js +106 -5
- package/mcp_v2/tools/tasks.js +18 -14
- package/package.json +35 -68
- package/babel.config.js +0 -32
- package/dist/index.js +0 -1
- package/dist/scripts/devBuild.js +0 -3
- package/dist/scripts/devServe.js +0 -3
- package/dist/scripts/loadScriptsBeforeServerStarts.js +0 -4
- package/dist/scripts/registerAliases.js +0 -6
- package/dist/server/renderer/document/Body.js +0 -11
- package/dist/server/renderer/document/Head.js +0 -9
- package/dist/server/renderer/document/index.js +0 -1
- package/dist/server/renderer/handler.js +0 -47
- package/dist/server/startServer.js +0 -57
- package/dist/webpack/babel-plugins/remove-client.plugin.js +0 -5
- package/dist/webpack/babel-plugins/remove-ssr.plugin.js +0 -5
- package/dist/webpack/babel.config.client.js +0 -1
- package/dist/webpack/babel.config.ssr.js +0 -1
- package/dist/webpack/base.babel.js +0 -7
- package/dist/webpack/development.client.babel.js +0 -10
- package/dist/webpack/production.client.babel.js +0 -4
- package/dist/webpack/production.ssr.babel.js +0 -8
- package/dist/webpack/scssParams.js +0 -9
package/bin/catalyst.js
CHANGED
|
@@ -1,128 +1,72 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict"
|
|
3
|
-
process.on("unhandledRejection", (err) => {
|
|
4
|
-
throw err
|
|
5
|
-
})
|
|
6
|
-
const { spawnSync } = require("node:child_process")
|
|
7
|
-
const args = process.argv.slice(2)
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"plugin",
|
|
17
|
-
"plugins",
|
|
18
|
-
"buildApp",
|
|
19
|
-
"buildApp:ios",
|
|
20
|
-
"buildApp:android",
|
|
21
|
-
"setupEmulator",
|
|
22
|
-
"setupEmulator:ios",
|
|
23
|
-
"setupEmulator:android",
|
|
24
|
-
]
|
|
3
|
+
import { spawnSync } from "node:child_process"
|
|
4
|
+
import { dirname, resolve } from "node:path"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
|
|
7
|
+
process.on("unhandledRejection", (error) => {
|
|
8
|
+
throw error
|
|
9
|
+
})
|
|
25
10
|
|
|
26
|
-
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const args = process.argv.slice(2)
|
|
13
|
+
const webCommands = new Set(["build", "start", "serve", "serve:inspect", "plugin", "plugins"])
|
|
27
14
|
const platformScripts = {
|
|
28
15
|
"setupEmulator:ios": "../dist/native/setupEmulatorIos.js",
|
|
29
16
|
"setupEmulator:android": "../dist/native/androidSetup.js",
|
|
30
17
|
"buildApp:ios": "../dist/native/buildAppIos.js",
|
|
31
18
|
"buildApp:android": "../dist/native/buildAppAndroid.js",
|
|
32
19
|
}
|
|
20
|
+
const validCommands = new Set([...webCommands, "buildApp", "setupEmulator", ...Object.keys(platformScripts)])
|
|
21
|
+
|
|
22
|
+
const scriptIndex = args.findIndex((arg) => validCommands.has(arg))
|
|
23
|
+
const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
|
|
24
|
+
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
|
|
25
|
+
const scriptArgs = args.slice(scriptIndex + 1)
|
|
33
26
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
function handleProcessResult(result) {
|
|
28
|
+
if (result.signal) {
|
|
29
|
+
if (result.signal === "SIGKILL") {
|
|
30
|
+
console.error("The Catalyst process was terminated with SIGKILL.")
|
|
31
|
+
} else if (result.signal === "SIGTERM") {
|
|
32
|
+
console.error("The Catalyst process was terminated with SIGTERM.")
|
|
33
|
+
}
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
process.exit(result.status ?? 1)
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const command = `${baseCommand}:${platform}`
|
|
44
|
-
const result = spawnSync(
|
|
39
|
+
function runScript(relativePath, extraArgs = []) {
|
|
40
|
+
return spawnSync(
|
|
45
41
|
process.execPath,
|
|
46
|
-
nodeArgs
|
|
47
|
-
{
|
|
42
|
+
[...nodeArgs, resolve(__dirname, relativePath), ...extraArgs, ...scriptArgs],
|
|
43
|
+
{
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
}
|
|
48
46
|
)
|
|
49
|
-
return result
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
49
|
+
function runPlatformCommand(command) {
|
|
50
|
+
return runScript(platformScripts[command])
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (script === "buildApp" || script === "setupEmulator") {
|
|
54
|
+
for (const platform of ["ios", "android"]) {
|
|
55
|
+
const result = runPlatformCommand(`${script}:${platform}`)
|
|
56
|
+
if (result.status !== 0 || result.signal) handleProcessResult(result)
|
|
61
57
|
}
|
|
62
58
|
process.exit(0)
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
|
|
66
|
-
(
|
|
67
|
-
x === "build" ||
|
|
68
|
-
x === "start" ||
|
|
69
|
-
x === "serve" ||
|
|
70
|
-
x === "devBuild" ||
|
|
71
|
-
x === "devServe" ||
|
|
72
|
-
x === "plugin" ||
|
|
73
|
-
x === "plugins" ||
|
|
74
|
-
isPlatformCommand(x, "buildApp") ||
|
|
75
|
-
isPlatformCommand(x, "setupEmulator")
|
|
76
|
-
)
|
|
77
|
-
const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
|
|
78
|
-
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
|
|
79
|
-
const resolvedScript = script === "plugin" ? "plugins" : script
|
|
80
|
-
|
|
81
|
-
if (validCommands.includes(script)) {
|
|
82
|
-
// Handle platform-specific or combined commands
|
|
83
|
-
if (script === "buildApp" || script === "setupEmulator") {
|
|
84
|
-
// Run for all platforms if no specific platform is specified
|
|
85
|
-
runAllPlatforms(script)
|
|
86
|
-
} else if (script in platformScripts) {
|
|
87
|
-
// Run for specific platform
|
|
88
|
-
const result = spawnSync(
|
|
89
|
-
process.execPath,
|
|
90
|
-
nodeArgs.concat(require.resolve(platformScripts[script])).concat(args.slice(scriptIndex + 1)),
|
|
91
|
-
{ stdio: "inherit" }
|
|
92
|
-
)
|
|
93
|
-
handleProcessResult(result)
|
|
94
|
-
} else {
|
|
95
|
-
// Original commands
|
|
96
|
-
const result = spawnSync(
|
|
97
|
-
process.execPath,
|
|
98
|
-
nodeArgs
|
|
99
|
-
.concat(require.resolve("../dist/scripts/" + resolvedScript))
|
|
100
|
-
.concat(args.slice(scriptIndex + 1)),
|
|
101
|
-
{ stdio: "inherit" }
|
|
102
|
-
)
|
|
103
|
-
handleProcessResult(result)
|
|
104
|
-
}
|
|
105
|
-
} else {
|
|
106
|
-
console.log('Unknown script "' + script + '".')
|
|
61
|
+
if (platformScripts[script]) {
|
|
62
|
+
handleProcessResult(runPlatformCommand(script))
|
|
107
63
|
}
|
|
108
64
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log(
|
|
114
|
-
"The build failed because the process exited too early. " +
|
|
115
|
-
"This probably means the system ran out of memory or someone called " +
|
|
116
|
-
"`kill -9` on the process."
|
|
117
|
-
)
|
|
118
|
-
} else if (result.signal === "SIGTERM") {
|
|
119
|
-
console.log(
|
|
120
|
-
"The build failed because the process exited too early. " +
|
|
121
|
-
"Someone might have called `kill` or `killall`, or the system could " +
|
|
122
|
-
"be shutting down."
|
|
123
|
-
)
|
|
124
|
-
}
|
|
125
|
-
process.exit(1)
|
|
126
|
-
}
|
|
127
|
-
process.exit(result.status)
|
|
65
|
+
if (webCommands.has(script)) {
|
|
66
|
+
const scriptName = script === "plugin" ? "plugins" : script === "serve:inspect" ? "serve" : script
|
|
67
|
+
const extraArgs = script === "serve:inspect" ? ["--inspect"] : []
|
|
68
|
+
handleProcessResult(runScript(`../dist/scripts/${scriptName}.js`, extraArgs))
|
|
128
69
|
}
|
|
70
|
+
|
|
71
|
+
console.error(`Unknown script "${script}".`)
|
|
72
|
+
process.exit(1)
|
package/changelog.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# 1.0.0-beta.1
|
|
2
|
+
|
|
3
|
+
- Replaced the Webpack build and SSR pipeline with Vite and ESM.
|
|
4
|
+
- Added React 19 Suspense-aware routing, split components, and manifest-based asset loading.
|
|
5
|
+
- Added opt-in request, compression, flush, and bot-aware OpenTelemetry spans.
|
|
6
|
+
- Preserved native application builds, internal plugins, Sentry exports, and safe-area SSR hydration.
|
|
7
|
+
- Removed the legacy `devBuild` and `devServe` commands.
|
|
8
|
+
|
|
1
9
|
# Changelog
|
|
2
10
|
|
|
3
11
|
## [0.1.0-beta.2] - 2026-05-06
|
package/dist/caching.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export * from "@tata1mg/api-caching"
|
package/dist/index.jsx
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "react-router-dom"
|
|
2
|
+
export * from "./web-router/components/RouterDataProvider.jsx"
|
|
3
|
+
export * from "./web-router/components/MetaTag.jsx"
|
|
4
|
+
export { split, hydrationReady } from "./web-router/components/Split.jsx"
|
|
5
|
+
export { split as default } from "./web-router/components/Split.jsx"
|
|
6
|
+
export * from "./web-router/hooks.jsx"
|
|
7
|
+
export * from "./web-router/utils/metaDataUtils.jsx"
|
|
8
|
+
export * from "./server/renderer/document/index.jsx"
|
package/dist/logger.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import pc from "picocolors"
|
|
2
|
+
import winston from "winston"
|
|
3
|
+
import DailyRotateFile from "winston-daily-rotate-file"
|
|
4
|
+
|
|
5
|
+
const { createLogger, format, transports } = winston
|
|
6
|
+
|
|
7
|
+
/**
|
|
2
8
|
* @description Logger library with rotational strategy. Creates a logs folder in root.
|
|
3
9
|
* With debug, error and info log directories with their respective log files.
|
|
4
10
|
*
|
|
@@ -6,5 +12,70 @@
|
|
|
6
12
|
* @param config { @enableDebugLogs: Bool // default: true }
|
|
7
13
|
* @returns loggerInstance
|
|
8
14
|
*
|
|
9
|
-
*/
|
|
10
|
-
|
|
15
|
+
*/
|
|
16
|
+
const configureLogger = (config = {}) => {
|
|
17
|
+
const { enableDebugLogs = true, enableFileLogging = true, enableConsoleLogging = true } = config
|
|
18
|
+
|
|
19
|
+
const consoleTransport = new transports.Console({ level: "debug" })
|
|
20
|
+
|
|
21
|
+
const fileTransport = (type = "info") => {
|
|
22
|
+
return new DailyRotateFile({
|
|
23
|
+
filename: `${process.env.src_path}/logs/${type}/%DATE%.${type}.log`,
|
|
24
|
+
datePattern: "YYYY-MM-DD",
|
|
25
|
+
maxFiles: "3d", // Logs will be removed after 2days,
|
|
26
|
+
loglevel: type,
|
|
27
|
+
level: type,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const infoLogger = createLogger({
|
|
32
|
+
format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
|
|
33
|
+
defaultMeta: { loglevel: "info" },
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const debugLogger = createLogger({
|
|
37
|
+
format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
|
|
38
|
+
defaultMeta: { loglevel: "debug" },
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const errorLogger = createLogger({
|
|
42
|
+
format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.json()),
|
|
43
|
+
defaultMeta: { loglevel: "error" },
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
if (enableConsoleLogging && JSON.parse(enableConsoleLogging)) {
|
|
47
|
+
infoLogger.add(consoleTransport)
|
|
48
|
+
debugLogger.add(consoleTransport)
|
|
49
|
+
errorLogger.add(consoleTransport)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (enableFileLogging && JSON.parse(enableFileLogging)) {
|
|
53
|
+
infoLogger.add(fileTransport("info"))
|
|
54
|
+
debugLogger.add(fileTransport("debug"))
|
|
55
|
+
errorLogger.add(fileTransport("error"))
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const Logger = {
|
|
59
|
+
debug: () => {},
|
|
60
|
+
error: (msg) => {
|
|
61
|
+
console.log(pc.red(pc.bold("ERROR: " + JSON.stringify(msg))))
|
|
62
|
+
errorLogger.error(msg)
|
|
63
|
+
},
|
|
64
|
+
info: (msg) => {
|
|
65
|
+
console.log(pc.green(pc.bold("INFO: " + JSON.stringify(msg))))
|
|
66
|
+
infoLogger.info(msg)
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (enableDebugLogs && JSON.parse(enableDebugLogs)) {
|
|
71
|
+
Logger.debug = (msg) => {
|
|
72
|
+
console.log(pc.yellow(pc.bold("DEBUG: " + JSON.stringify(msg))))
|
|
73
|
+
debugLogger.debug(msg)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (global) global.logger = Logger
|
|
78
|
+
return Logger
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { configureLogger }
|
|
@@ -110,6 +110,7 @@ android {
|
|
|
110
110
|
applicationIdSuffix = ".debug"
|
|
111
111
|
versionNameSuffix = "-debug"
|
|
112
112
|
isMinifyEnabled = false
|
|
113
|
+
enableUnitTestCoverage = true
|
|
113
114
|
buildConfigField("Boolean", "ALLOW_MIXED_CONTENT", "true")
|
|
114
115
|
buildConfigField("String", "LOCAL_IP", "\"${getLocalIpAddress()}\"")
|
|
115
116
|
}
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt
CHANGED
|
@@ -7,18 +7,26 @@ import android.graphics.Bitmap
|
|
|
7
7
|
import android.net.Uri
|
|
8
8
|
import android.os.Bundle
|
|
9
9
|
import android.util.Log
|
|
10
|
+
import android.view.GestureDetector
|
|
11
|
+
import android.view.MotionEvent
|
|
10
12
|
import android.view.View
|
|
11
13
|
import android.webkit.*
|
|
12
14
|
import androidx.webkit.ServiceWorkerClientCompat
|
|
13
15
|
import androidx.webkit.ServiceWorkerControllerCompat
|
|
16
|
+
import androidx.webkit.WebViewCompat
|
|
17
|
+
import androidx.webkit.WebViewFeature
|
|
14
18
|
import androidx.webkit.WebResourceRequestCompat
|
|
15
19
|
import android.widget.ProgressBar
|
|
16
20
|
import androidx.webkit.WebViewAssetLoader
|
|
17
21
|
import io.yourname.androidproject.WebCacheManager
|
|
18
22
|
import io.yourname.androidproject.URLWhitelistManager
|
|
19
23
|
import io.yourname.androidproject.matchesCachePattern
|
|
24
|
+
import io.yourname.androidproject.utils.BridgeUtils
|
|
20
25
|
import io.yourname.androidproject.utils.CameraUtils
|
|
21
26
|
import io.yourname.androidproject.utils.NetworkUtils
|
|
27
|
+
import io.yourname.androidproject.utils.PerfEventBuffer
|
|
28
|
+
import io.yourname.androidproject.utils.ProfilerNavigationState
|
|
29
|
+
import org.json.JSONObject
|
|
22
30
|
import kotlinx.coroutines.*
|
|
23
31
|
import java.io.FileNotFoundException
|
|
24
32
|
import java.util.Properties
|
|
@@ -45,11 +53,15 @@ class CustomWebView(
|
|
|
45
53
|
private var isInitialApiCalled: Boolean = false // Flag to track initial page load
|
|
46
54
|
private var isInitialPageLoaded: Boolean = false
|
|
47
55
|
private var buildOptimisation: Boolean = false // Added property for build optimization
|
|
56
|
+
private var profilerEnabled: Boolean = false
|
|
57
|
+
private val profilerNavigationState = ProfilerNavigationState()
|
|
48
58
|
private lateinit var assetLoader: WebViewAssetLoader
|
|
49
59
|
private var allowedUrls: List<String> = emptyList()
|
|
50
60
|
private var accessControlEnabled: Boolean = false
|
|
51
61
|
private val offlineAssetPath = "offline/offline.html"
|
|
52
62
|
private val offlineAssetUrl = "file:///android_asset/$offlineAssetPath"
|
|
63
|
+
private var scrollSessionOpen = false
|
|
64
|
+
private lateinit var scrollGestureDetector: GestureDetector
|
|
53
65
|
private var offlinePageVisible = false
|
|
54
66
|
private var lastTargetUrl: String? = null
|
|
55
67
|
private var activeOfflineRouteOrigin: String? = null
|
|
@@ -62,8 +74,16 @@ class CustomWebView(
|
|
|
62
74
|
private var assetLoadFailures = 0
|
|
63
75
|
|
|
64
76
|
init {
|
|
77
|
+
if (BuildConfig.DEBUG) {
|
|
78
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
79
|
+
put("type", "boot-webview-constructed")
|
|
80
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
81
|
+
put("thread", Thread.currentThread().name)
|
|
82
|
+
})
|
|
83
|
+
}
|
|
65
84
|
setupFromProperties()
|
|
66
85
|
cacheManager = WebCacheManager(context, properties)
|
|
86
|
+
if (profilerEnabled) metricsMonitor.attachWebView(webView)
|
|
67
87
|
offlineCacheService = OfflineCacheService(context)
|
|
68
88
|
setupWebView()
|
|
69
89
|
}
|
|
@@ -90,6 +110,7 @@ class CustomWebView(
|
|
|
90
110
|
|
|
91
111
|
// Parse buildOptimisation property
|
|
92
112
|
buildOptimisation = properties.getProperty("buildOptimisation", "false").toBoolean()
|
|
113
|
+
profilerEnabled = PerfEventBuffer.isEnabled()
|
|
93
114
|
|
|
94
115
|
// Load allowed URLs from properties
|
|
95
116
|
allowedUrls = properties.getProperty("accessControl.allowedUrls", "")
|
|
@@ -162,12 +183,26 @@ class CustomWebView(
|
|
|
162
183
|
}
|
|
163
184
|
}
|
|
164
185
|
|
|
186
|
+
private fun loadTopLevelUrl(url: String) {
|
|
187
|
+
if (BuildConfig.DEBUG) {
|
|
188
|
+
val navigation = profilerNavigationState.begin(url)
|
|
189
|
+
if (navigation.shouldReset) PerfEventBuffer.reset()
|
|
190
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
191
|
+
put("type", "boot-load-url")
|
|
192
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
193
|
+
put("url", navigation.url)
|
|
194
|
+
put("thread", Thread.currentThread().name)
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
loadUrlInternal(url)
|
|
198
|
+
}
|
|
199
|
+
|
|
165
200
|
fun loadUrl(url: String) {
|
|
166
201
|
lastTargetUrl = url
|
|
167
202
|
offlinePageVisible = false
|
|
168
203
|
visibleOfflineSnapshotUrl = null
|
|
169
204
|
activeOfflineRouteOrigin = null
|
|
170
|
-
|
|
205
|
+
loadTopLevelUrl(url)
|
|
171
206
|
}
|
|
172
207
|
|
|
173
208
|
fun updateLastTargetUrl(url: String) {
|
|
@@ -185,7 +220,7 @@ class CustomWebView(
|
|
|
185
220
|
offlinePageVisible = true
|
|
186
221
|
visibleOfflineSnapshotUrl = null
|
|
187
222
|
activeOfflineRouteOrigin = null
|
|
188
|
-
|
|
223
|
+
loadTopLevelUrl(offlineAssetUrl)
|
|
189
224
|
if (BuildConfig.DEBUG) {
|
|
190
225
|
Log.d(TAG, "📴 Showing offline page from assets: $offlineAssetPath")
|
|
191
226
|
}
|
|
@@ -228,7 +263,7 @@ class CustomWebView(
|
|
|
228
263
|
offlinePageVisible = false
|
|
229
264
|
visibleOfflineSnapshotUrl = snapshotUrl
|
|
230
265
|
updateActiveOfflineRoute(url, true)
|
|
231
|
-
|
|
266
|
+
loadTopLevelUrl(url)
|
|
232
267
|
if (BuildConfig.DEBUG) {
|
|
233
268
|
Log.d(TAG, "📴 Loading cached offline route through request interceptor: $url")
|
|
234
269
|
}
|
|
@@ -304,6 +339,7 @@ class CustomWebView(
|
|
|
304
339
|
|
|
305
340
|
fun destroy() {
|
|
306
341
|
job.cancel()
|
|
342
|
+
metricsMonitor.detachWebView()
|
|
307
343
|
webView.destroy()
|
|
308
344
|
}
|
|
309
345
|
|
|
@@ -335,6 +371,13 @@ class CustomWebView(
|
|
|
335
371
|
if (BuildConfig.DEBUG) {
|
|
336
372
|
Log.d(TAG, "🚀 Hardware acceleration enabled - Thread: ${Thread.currentThread().name}")
|
|
337
373
|
}
|
|
374
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply {
|
|
375
|
+
put("type", "hw-accel-change")
|
|
376
|
+
put("state", "on")
|
|
377
|
+
put("trigger", "cleanup")
|
|
378
|
+
put("thread", Thread.currentThread().name)
|
|
379
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
380
|
+
})
|
|
338
381
|
}
|
|
339
382
|
}
|
|
340
383
|
|
|
@@ -345,6 +388,13 @@ class CustomWebView(
|
|
|
345
388
|
if (BuildConfig.DEBUG) {
|
|
346
389
|
Log.d(TAG, "⚫ Hardware acceleration disabled - Thread: ${Thread.currentThread().name}")
|
|
347
390
|
}
|
|
391
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply {
|
|
392
|
+
put("type", "hw-accel-change")
|
|
393
|
+
put("state", "off")
|
|
394
|
+
put("trigger", "cache-serve")
|
|
395
|
+
put("thread", Thread.currentThread().name)
|
|
396
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
397
|
+
})
|
|
348
398
|
}
|
|
349
399
|
}
|
|
350
400
|
|
|
@@ -472,7 +522,7 @@ class CustomWebView(
|
|
|
472
522
|
}
|
|
473
523
|
offlinePageVisible = false
|
|
474
524
|
lastTargetUrl = target
|
|
475
|
-
|
|
525
|
+
loadTopLevelUrl(target)
|
|
476
526
|
} else if (BuildConfig.DEBUG) {
|
|
477
527
|
Log.w(TAG, "🔄 Retry requested but no target URL is known")
|
|
478
528
|
}
|
|
@@ -718,6 +768,13 @@ class CustomWebView(
|
|
|
718
768
|
|
|
719
769
|
@SuppressLint("SetJavaScriptEnabled")
|
|
720
770
|
private fun setupWebView() {
|
|
771
|
+
if (profilerEnabled && WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
|
|
772
|
+
WebViewCompat.addDocumentStartJavaScript(
|
|
773
|
+
webView,
|
|
774
|
+
"window.__CATALYST_PROFILER_ENABLED = true;",
|
|
775
|
+
setOf("*")
|
|
776
|
+
)
|
|
777
|
+
}
|
|
721
778
|
if (BuildConfig.DEBUG) {
|
|
722
779
|
Log.d(TAG, "🌐 Setting up WebView on thread: ${Thread.currentThread().name}")
|
|
723
780
|
}
|
|
@@ -1057,14 +1114,53 @@ class CustomWebView(
|
|
|
1057
1114
|
Log.d(TAG, "⏳ Page load started for: $url - Hardware Acceleration: $isHardwareAccelerationEnabled")
|
|
1058
1115
|
Log.d(FLOW_TAG, "PAGE started url=$url online=${NetworkUtils.getCurrentStatus(context).isOnline} offlinePageVisible=$offlinePageVisible visibleSnapshot=${visibleOfflineSnapshotUrl ?: "none"} activeOrigin=${activeOfflineRouteOrigin ?: "none"}")
|
|
1059
1116
|
}
|
|
1117
|
+
|
|
1118
|
+
if (profilerEnabled) {
|
|
1119
|
+
val nativeNow = android.os.SystemClock.elapsedRealtime()
|
|
1120
|
+
view?.evaluateJavascript(
|
|
1121
|
+
"window.__CATALYST_PROFILER_ENABLED = true; window.__NATIVE_TIME_OFFSET = $nativeNow - performance.now();",
|
|
1122
|
+
null
|
|
1123
|
+
)
|
|
1124
|
+
}
|
|
1125
|
+
if (profilerEnabled && url != null) {
|
|
1126
|
+
val nativeNow = android.os.SystemClock.elapsedRealtime()
|
|
1127
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
1128
|
+
put("type", "boot-page-started")
|
|
1129
|
+
put("nativeTime", nativeNow)
|
|
1130
|
+
put("url", url)
|
|
1131
|
+
})
|
|
1132
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
1133
|
+
put("type", "page-load-start")
|
|
1134
|
+
put("nativeTime", nativeNow)
|
|
1135
|
+
put("url", url)
|
|
1136
|
+
})
|
|
1137
|
+
}
|
|
1060
1138
|
}
|
|
1061
1139
|
|
|
1062
1140
|
override fun onPageFinished(view: WebView?, url: String?) {
|
|
1063
1141
|
progressBar.visibility = View.GONE
|
|
1064
1142
|
if(!isInitialPageLoaded){
|
|
1065
1143
|
isInitialPageLoaded = true
|
|
1144
|
+
metricsMonitor.markAppStartComplete()
|
|
1066
1145
|
}
|
|
1067
1146
|
url?.let { metricsMonitor.trackPageLoadEnd(it) }
|
|
1147
|
+
|
|
1148
|
+
val pageFinishedNative = android.os.SystemClock.elapsedRealtime()
|
|
1149
|
+
if (profilerEnabled && url != null) {
|
|
1150
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
1151
|
+
put("type", "boot-page-finished")
|
|
1152
|
+
put("nativeTime", pageFinishedNative)
|
|
1153
|
+
put("url", url)
|
|
1154
|
+
})
|
|
1155
|
+
val pageStart = view?.tag as? Long
|
|
1156
|
+
PerfEventBuffer.add(JSONObject().apply {
|
|
1157
|
+
put("type", "page-load-end")
|
|
1158
|
+
put("nativeTime", pageFinishedNative)
|
|
1159
|
+
put("url", url)
|
|
1160
|
+
put("durationMs", if (pageStart != null) System.currentTimeMillis() - pageStart else 0L)
|
|
1161
|
+
})
|
|
1162
|
+
if (view != null) PerfEventBuffer.scheduleFlush(view)
|
|
1163
|
+
}
|
|
1068
1164
|
if (BuildConfig.DEBUG) {
|
|
1069
1165
|
val startTime = view?.tag as? Long ?: return
|
|
1070
1166
|
val loadTime = System.currentTimeMillis() - startTime
|
|
@@ -1095,6 +1191,15 @@ class CustomWebView(
|
|
|
1095
1191
|
if (BuildConfig.DEBUG) {
|
|
1096
1192
|
Log.e(FLOW_TAG, "PAGE resource-error mainFrame=${request?.isForMainFrame} method=${request?.method} code=${error.errorCode} description=${error.description} url=${request?.url}")
|
|
1097
1193
|
}
|
|
1194
|
+
if (view != null && request?.isForMainFrame == true) {
|
|
1195
|
+
BridgeUtils.emitPerfEvent(view, JSONObject().apply {
|
|
1196
|
+
put("type", "page-load-error")
|
|
1197
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
1198
|
+
put("url", request.url?.toString() ?: "")
|
|
1199
|
+
put("errorCode", error.errorCode)
|
|
1200
|
+
put("description", error.description?.toString() ?: "")
|
|
1201
|
+
})
|
|
1202
|
+
}
|
|
1098
1203
|
}
|
|
1099
1204
|
|
|
1100
1205
|
val failedUrl = request?.url?.toString()
|
|
@@ -1204,5 +1309,39 @@ class CustomWebView(
|
|
|
1204
1309
|
|
|
1205
1310
|
// Only enable WebView debugging in debug builds
|
|
1206
1311
|
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG)
|
|
1312
|
+
|
|
1313
|
+
if (profilerEnabled) {
|
|
1314
|
+
scrollGestureDetector = GestureDetector(context,
|
|
1315
|
+
object : GestureDetector.SimpleOnGestureListener() {
|
|
1316
|
+
override fun onScroll(
|
|
1317
|
+
e1: MotionEvent?,
|
|
1318
|
+
e2: MotionEvent,
|
|
1319
|
+
distanceX: Float,
|
|
1320
|
+
distanceY: Float
|
|
1321
|
+
): Boolean {
|
|
1322
|
+
if (!scrollSessionOpen) {
|
|
1323
|
+
scrollSessionOpen = true
|
|
1324
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply {
|
|
1325
|
+
put("type", "scroll-start")
|
|
1326
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
1327
|
+
})
|
|
1328
|
+
}
|
|
1329
|
+
return false
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
)
|
|
1333
|
+
webView.setOnTouchListener { _, event ->
|
|
1334
|
+
scrollGestureDetector.onTouchEvent(event)
|
|
1335
|
+
if (scrollSessionOpen &&
|
|
1336
|
+
(event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL)) {
|
|
1337
|
+
scrollSessionOpen = false
|
|
1338
|
+
BridgeUtils.emitPerfEvent(webView, JSONObject().apply {
|
|
1339
|
+
put("type", "scroll-end")
|
|
1340
|
+
put("nativeTime", android.os.SystemClock.elapsedRealtime())
|
|
1341
|
+
})
|
|
1342
|
+
}
|
|
1343
|
+
false
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1207
1346
|
}
|
|
1208
1347
|
}
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -274,6 +274,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
274
274
|
properties.setProperty("buildType", if (BuildConfig.DEBUG) "debug" else "release")
|
|
275
275
|
properties.setProperty("buildOptimisation", (!BuildConfig.DEBUG).toString())
|
|
276
276
|
}
|
|
277
|
+
io.yourname.androidproject.utils.PerfEventBuffer.configure(
|
|
278
|
+
properties.getProperty("profiler.enabled", "false").toBoolean()
|
|
279
|
+
)
|
|
277
280
|
|
|
278
281
|
configureEdgeToEdge()
|
|
279
282
|
|
|
@@ -286,16 +289,29 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
286
289
|
})
|
|
287
290
|
}
|
|
288
291
|
|
|
289
|
-
// 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.
|
|
290
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
|
+
}
|
|
291
307
|
|
|
292
308
|
// Setup UI
|
|
293
309
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
294
310
|
supportActionBar?.hide()
|
|
295
311
|
setContentView(binding.root)
|
|
296
312
|
|
|
297
|
-
// Initialize keyboard utility
|
|
298
|
-
keyboardUtil = KeyboardUtil(this, binding.webviewContainer)
|
|
313
|
+
// Initialize keyboard utility (pass webView so keyboard events reach WebPerfCollector)
|
|
314
|
+
keyboardUtil = KeyboardUtil(this, binding.webviewContainer, binding.webview)
|
|
299
315
|
keyboardUtil.initialize()
|
|
300
316
|
|
|
301
317
|
// Enable hardware acceleration for the window
|
|
@@ -351,6 +367,12 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
351
367
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
|
352
368
|
override fun handleOnBackPressed() {
|
|
353
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
|
+
}
|
|
354
376
|
customWebView.goBack()
|
|
355
377
|
} else {
|
|
356
378
|
// Disable this callback and let the system handle back press
|
|
@@ -389,9 +411,6 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
389
411
|
customWebView.showOfflineRouteOrOfflinePage(currentUrl)
|
|
390
412
|
}
|
|
391
413
|
}
|
|
392
|
-
|
|
393
|
-
metricsMonitor.markAppStartComplete()
|
|
394
|
-
|
|
395
414
|
} catch (e: Exception) {
|
|
396
415
|
Log.e(TAG, "Failed to load initial URL: ${e.message}")
|
|
397
416
|
// TODO: Add HTML file workflow - error.html not available yet
|
|
@@ -453,6 +472,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
453
472
|
}
|
|
454
473
|
|
|
455
474
|
override fun onDestroy() {
|
|
475
|
+
io.yourname.androidproject.utils.PerfEventBuffer.reset()
|
|
456
476
|
// Log all performance metrics before destroying
|
|
457
477
|
if (BuildConfig.DEBUG) {
|
|
458
478
|
Log.d(TAG, "🏁 App shutting down - logging final metrics...")
|