catalyst-core 0.0.3-canary.9 → 0.1.0-beta.2
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/README.md +4 -5
- package/bin/catalyst.js +28 -15
- package/changelog.md +139 -30
- package/dist/native/androidProject/app/build.gradle.kts +125 -15
- package/dist/native/androidProject/app/proguard-rules.pro +85 -14
- package/dist/native/androidProject/app/src/debug/res/xml/network_security_config.xml +8 -0
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +10 -3
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +552 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CatalystConstants.kt +109 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +605 -124
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +405 -47
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt +341 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +954 -71
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashActivity.kt +111 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/URLWhitelistManager.kt +231 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/Utils.kt +49 -55
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +81 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/design/DesignTokens.kt +307 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/EmulatorDetector.kt +203 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +187 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/SecurityAlertHandler.kt +170 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/SecurityAlertUI.kt +366 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/SecurityBottomSheet.kt +138 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/SecurityCheckManager.kt +511 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/SecurityCheckScheduler.kt +68 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +47 -11
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/DeviceInfoUtils.kt +38 -13
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/DownloadUtils.kt +36 -14
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FileUtils.kt +3 -2
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +57 -8
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/IntentUtils.kt +31 -6
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +82 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/NetworkUtils.kt +105 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/NotificationConstants.kt +28 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/NotificationUtils.kt +639 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/SafeAreaUtils.kt +62 -0
- package/dist/native/androidProject/app/src/main/res/drawable/splashscreen_fallback.png +0 -0
- package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +1 -0
- package/dist/native/androidProject/app/src/main/res/layout/activity_splash.xml +20 -0
- package/dist/native/androidProject/app/src/main/res/values/themes.xml +23 -0
- package/dist/native/androidProject/app/src/main/res/values-night/themes.xml +11 -0
- package/dist/native/androidProject/app/src/main/res/xml/network_security_config.xml +12 -0
- package/dist/native/androidProject/app/src/noFcm/java/io/yourname/androidproject/utils/NotificationManager.kt +166 -0
- package/dist/native/androidProject/app/src/noFcm/java/io/yourname/androidproject/utils/PushNotificationUtils.kt +94 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeMessageValidatorTest.kt +267 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +286 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +296 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/ConfigMappingTest.kt +594 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CustomWebviewTest.kt +466 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +187 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +304 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +424 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +469 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/SecurityBridgeTest.kt +199 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/URLWhitelistManagerTest.kt +462 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +391 -0
- package/dist/native/androidProject/app/src/withFcm/java/io/yourname/androidproject/utils/NotificationManager.kt +224 -0
- package/dist/native/androidProject/app/src/withFcm/java/io/yourname/androidproject/utils/PushNotificationUtils.kt +399 -0
- package/dist/native/androidProject/build.gradle.kts +6 -0
- package/dist/native/androidProject/gradle/libs.versions.toml +8 -2
- package/dist/native/androidSetup.js +4 -3
- package/dist/native/assets/catalyst.png +0 -0
- package/dist/native/bridge/WebBridge.js +16 -3
- package/dist/native/bridge/constants/NativeInterfaces.js +28 -10
- package/dist/native/bridge/errors.js +1 -1
- package/dist/native/bridge/hooks.js +76 -16
- package/dist/native/bridge/safeArea.js +5 -0
- package/dist/native/bridge/standardizedHooks.js +23 -5
- package/dist/native/bridge/useBaseHook.js +7 -9
- package/dist/native/bridge/utils/CameraUtils.js +58 -0
- package/dist/native/bridge/utils/FileObjectConverter.js +40 -0
- package/dist/native/bridge/utils/NativeBridge.js +57 -8
- package/dist/native/buildAppAndroid.js +115 -43
- package/dist/native/buildAppIos.js +291 -26
- package/dist/native/iosnativeWebView/Configurations/Debug.xcconfig +21 -0
- package/dist/native/iosnativeWebView/Configurations/Release.xcconfig +24 -0
- package/dist/native/iosnativeWebView/Configurations/Shared.xcconfig +50 -0
- package/dist/native/iosnativeWebView/Package.resolved +32 -0
- package/dist/native/iosnativeWebView/Package.swift +37 -0
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +293 -0
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +299 -0
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +199 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +125 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/ConfigConstants.swift +70 -0
- package/dist/native/iosnativeWebView/Sources/Core/Notifications/NotificationModels.swift +156 -0
- package/dist/native/iosnativeWebView/Sources/Core/Protocols/NotificationHandlerProtocol.swift +35 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/BootTimingUtility.swift +26 -0
- package/dist/native/iosnativeWebView/{iosnativeWebView → Sources/Core/Utils}/CacheManager.swift +69 -36
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +363 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +67 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +1053 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/FrameworkServerUtils.swift +901 -0
- package/dist/native/iosnativeWebView/{iosnativeWebView → Sources/Core/Utils}/ImageHandler.swift +206 -20
- package/dist/native/iosnativeWebView/Sources/Core/Utils/NetworkMonitor.swift +112 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/NotificationHandlerProvider.swift +20 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/NullNotificationHandler.swift +85 -0
- package/dist/native/iosnativeWebView/{iosnativeWebView → Sources/Core/Utils}/ResourceURLProtocol.swift +60 -25
- package/dist/native/iosnativeWebView/Sources/Core/Utils/SafeAreaUtils.swift +132 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ScreenSecureManager.swift +131 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/URLWhitelistManager.swift +224 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +43 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +991 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +166 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +340 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +149 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +611 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +334 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +95 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +86 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +181 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +182 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +395 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +84 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AccentColor.colorset/Contents.json +8 -8
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/AppIcon.appiconset/Contents.json +10 -10
- package/dist/native/iosnativeWebView/iosnativeWebView/Assets.xcassets/Contents.json +4 -4
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +65 -10
- package/dist/native/iosnativeWebView/iosnativeWebView/Info-Release.plist +21 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +13 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/Preview Content/Preview Assets.xcassets/Contents.json +4 -4
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebView.entitlements +12 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +80 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/localhost.p12 +0 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +118 -100
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +69 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/xcshareddata/xcschemes/iosnativeWebView.xcscheme +9 -4
- package/dist/native/iosnativeWebView/iosnativeWebView.xctestplan +47 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BootTimingUtilityTests.swift +236 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift +212 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +418 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +390 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/ConfigMappingTests.swift +299 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +383 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FrameworkServerUtilsTests.swift +391 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +581 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/NotificationHandlerTests.swift +430 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/ScreenSecureManagerTests.swift +121 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/URLWhitelistManagerTests.swift +367 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +440 -0
- package/dist/native/renameAndroidProject.js +80 -70
- package/dist/native/setupEmulatorIos.js +22 -5
- package/dist/native/setupServer.js +11 -10
- package/dist/native/terminalProgress.js +10 -10
- package/dist/native/utils.js +4 -5
- package/dist/otel.js +4 -0
- package/dist/scripts/build.js +2 -2
- package/dist/scripts/devBuild.js +2 -2
- package/dist/scripts/loadEnvironmentVariables.js +3 -1
- package/dist/scripts/scriptUtils.js +2 -2
- package/dist/sentry.js +4 -1
- package/dist/server/renderer/document/Body.js +4 -4
- package/dist/server/renderer/handler.js +7 -5
- package/dist/webpack/development.client.babel.js +1 -1
- package/mcp_v2/conversion-tasks.json +326 -0
- package/mcp_v2/knowledge-base.json +1068 -0
- package/mcp_v2/lib/helpers.js +165 -0
- package/mcp_v2/mcp.js +563 -0
- package/mcp_v2/package.json +13 -0
- package/mcp_v2/schema.sql +88 -0
- package/mcp_v2/setup.js +278 -0
- package/mcp_v2/tools/build.js +686 -0
- package/mcp_v2/tools/config.js +453 -0
- package/mcp_v2/tools/conversion.js +799 -0
- package/mcp_v2/tools/debug.js +113 -0
- package/mcp_v2/tools/knowledge.js +219 -0
- package/mcp_v2/tools/sync.js +23 -0
- package/mcp_v2/tools/tasks.js +945 -0
- package/package.json +11 -15
- package/dist/native/androidProject/.idea/.name +0 -1
- package/dist/native/androidProject/.idea/AndroidProjectSystem.xml +0 -6
- package/dist/native/androidProject/.idea/compiler.xml +0 -6
- package/dist/native/androidProject/.idea/deploymentTargetSelector.xml +0 -10
- package/dist/native/androidProject/.idea/gradle.xml +0 -18
- package/dist/native/androidProject/.idea/migrations.xml +0 -10
- package/dist/native/androidProject/.idea/misc.xml +0 -10
- package/dist/native/androidProject/.idea/runConfigurations.xml +0 -17
- package/dist/native/androidProject/.idea/vcs.xml +0 -6
- package/dist/native/iosnativeWebView/iosnativeWebView/NativeBridge.swift +0 -217
- package/dist/native/iosnativeWebView/iosnativeWebView/WebView.swift +0 -96
- package/dist/native/iosnativeWebView/iosnativeWebView/WebViewModel.swift +0 -47
- package/dist/native/iosnativeWebView/iosnativeWebView/WebViewNavigationDelegate.swift +0 -140
- package/license +0 -10
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MetricsMonitor.kt
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
package io.yourname.androidproject
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Debug
|
|
5
|
+
import android.os.SystemClock
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import android.view.Choreographer
|
|
8
|
+
import java.util.concurrent.TimeUnit
|
|
9
|
+
import java.util.concurrent.atomic.AtomicInteger
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Simple metrics monitor for WebView-based Android applications.
|
|
13
|
+
* Tracks key performance metrics and logs them on app destroy.
|
|
14
|
+
*/
|
|
15
|
+
class MetricsMonitor(private val context: Context) {
|
|
16
|
+
private val TAG = "MetricsMonitor"
|
|
17
|
+
|
|
18
|
+
// App Startup metrics
|
|
19
|
+
private var appStartTime: Long = SystemClock.elapsedRealtime()
|
|
20
|
+
private var coldStartDuration: Long = 0
|
|
21
|
+
|
|
22
|
+
// Page Load metrics
|
|
23
|
+
private val pageLoadTimes = mutableMapOf<String, Long>()
|
|
24
|
+
private var currentPageLoadStartTime: Long = 0
|
|
25
|
+
private var avgPageLoadTime: Long = 0
|
|
26
|
+
private var totalPagesLoaded: Int = 0
|
|
27
|
+
|
|
28
|
+
// Frame rate metrics
|
|
29
|
+
private var frameCounter = AtomicInteger(0)
|
|
30
|
+
private var lastFpsCheckTime = SystemClock.elapsedRealtime()
|
|
31
|
+
private var currentFps = 0.0
|
|
32
|
+
private var minFps = Double.MAX_VALUE
|
|
33
|
+
private var maxFps = 0.0
|
|
34
|
+
private var avgFps = 0.0
|
|
35
|
+
private var fpsReadings = 0
|
|
36
|
+
|
|
37
|
+
// Memory metrics
|
|
38
|
+
private var initialMemory: Long = getMemoryUsage()
|
|
39
|
+
private var peakMemory: Long = initialMemory
|
|
40
|
+
private var currentMemory: Long = initialMemory
|
|
41
|
+
|
|
42
|
+
// Cache metrics
|
|
43
|
+
private var cacheHits = 0
|
|
44
|
+
private var cacheMisses = 0
|
|
45
|
+
private val cachedFiles = mutableSetOf<String>()
|
|
46
|
+
private val filesServedFromCache = mutableListOf<String>()
|
|
47
|
+
private val cacheMissedUrls = mutableListOf<String>()
|
|
48
|
+
private val allNetworkRequests = mutableListOf<String>()
|
|
49
|
+
private val cacheEvaluatedRequests = mutableListOf<String>()
|
|
50
|
+
|
|
51
|
+
// Thread metrics
|
|
52
|
+
private var uiBlockingEvents = 0
|
|
53
|
+
private var longTaskThreshold = 16L // ms (1 frame at 60fps)
|
|
54
|
+
private var longTasksDetected = 0
|
|
55
|
+
|
|
56
|
+
// Battery impact (simplified)
|
|
57
|
+
private var appRunDuration: Long = 0
|
|
58
|
+
|
|
59
|
+
private val frameCallback = object : Choreographer.FrameCallback {
|
|
60
|
+
override fun doFrame(frameTimeNanos: Long) {
|
|
61
|
+
frameCounter.incrementAndGet()
|
|
62
|
+
Choreographer.getInstance().postFrameCallback(this)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
init {
|
|
67
|
+
recordAppStart()
|
|
68
|
+
startFrameMonitoring()
|
|
69
|
+
monitorMemoryPeriodically()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fun recordAppStart() {
|
|
73
|
+
appStartTime = SystemClock.elapsedRealtime()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fun markAppStartComplete() {
|
|
77
|
+
coldStartDuration = SystemClock.elapsedRealtime() - appStartTime
|
|
78
|
+
Log.d(TAG, "App cold start completed in: $coldStartDuration ms")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fun trackPageLoadStart(url: String) {
|
|
82
|
+
currentPageLoadStartTime = SystemClock.elapsedRealtime()
|
|
83
|
+
pageLoadTimes[url] = currentPageLoadStartTime
|
|
84
|
+
if (BuildConfig.DEBUG) {
|
|
85
|
+
Log.d(TAG, "📄 PAGE_LOAD_START: $url")
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
fun trackPageLoadEnd(url: String) {
|
|
90
|
+
val startTime = pageLoadTimes[url] ?: return
|
|
91
|
+
val loadTime = SystemClock.elapsedRealtime() - startTime
|
|
92
|
+
|
|
93
|
+
// Update average
|
|
94
|
+
val totalTime = avgPageLoadTime * totalPagesLoaded + loadTime
|
|
95
|
+
totalPagesLoaded++
|
|
96
|
+
avgPageLoadTime = totalTime / totalPagesLoaded
|
|
97
|
+
|
|
98
|
+
if (BuildConfig.DEBUG) {
|
|
99
|
+
Log.d(TAG, "📄 PAGE_LOAD_END: $url in $loadTime ms")
|
|
100
|
+
Log.d(TAG, "📄 PAGE_STATS: Total pages: $totalPagesLoaded, Avg time: $avgPageLoadTime ms")
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fun recordCacheHit(url: String = "") {
|
|
105
|
+
cacheHits++
|
|
106
|
+
if (url.isNotEmpty()) {
|
|
107
|
+
filesServedFromCache.add(url)
|
|
108
|
+
if (BuildConfig.DEBUG) {
|
|
109
|
+
Log.d(TAG, "💾 CACHE_HIT: $url (Total hits: $cacheHits)")
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fun recordCacheMiss(url: String = "") {
|
|
115
|
+
cacheMisses++
|
|
116
|
+
if (url.isNotEmpty()) {
|
|
117
|
+
cacheMissedUrls.add(url)
|
|
118
|
+
if (BuildConfig.DEBUG) {
|
|
119
|
+
Log.d(TAG, "❌ CACHE_MISS: $url (Total misses: $cacheMisses)")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
fun recordCachedFile(url: String) {
|
|
125
|
+
cachedFiles.add(url)
|
|
126
|
+
if (BuildConfig.DEBUG) {
|
|
127
|
+
Log.d(TAG, "💾 FILE_CACHED: $url (Total cached files: ${cachedFiles.size})")
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fun recordNetworkRequest(url: String) {
|
|
132
|
+
if (url.isNotEmpty()) {
|
|
133
|
+
allNetworkRequests.add(url)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
fun recordCacheEvaluation(url: String) {
|
|
138
|
+
if (url.isNotEmpty()) {
|
|
139
|
+
cacheEvaluatedRequests.add(url)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fun recordUiBlocking() {
|
|
144
|
+
uiBlockingEvents++
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fun recordLongTask() {
|
|
148
|
+
longTasksDetected++
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private fun startFrameMonitoring() {
|
|
152
|
+
Choreographer.getInstance().postFrameCallback(frameCallback)
|
|
153
|
+
|
|
154
|
+
// Start a thread to calculate FPS every second
|
|
155
|
+
Thread {
|
|
156
|
+
while (!Thread.interrupted()) {
|
|
157
|
+
try {
|
|
158
|
+
Thread.sleep(1000)
|
|
159
|
+
calculateFps()
|
|
160
|
+
} catch (e: InterruptedException) {
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}.apply {
|
|
165
|
+
isDaemon = true
|
|
166
|
+
start()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private fun calculateFps() {
|
|
171
|
+
val now = SystemClock.elapsedRealtime()
|
|
172
|
+
val frames = frameCounter.getAndSet(0)
|
|
173
|
+
val timeSpan = now - lastFpsCheckTime
|
|
174
|
+
lastFpsCheckTime = now
|
|
175
|
+
|
|
176
|
+
if (timeSpan > 0) {
|
|
177
|
+
currentFps = frames * 1000.0 / timeSpan
|
|
178
|
+
|
|
179
|
+
// Update min/max/avg
|
|
180
|
+
if (minFps == Double.MAX_VALUE) minFps = currentFps
|
|
181
|
+
minFps = minFps.coerceAtMost(currentFps)
|
|
182
|
+
maxFps = maxFps.coerceAtLeast(currentFps)
|
|
183
|
+
|
|
184
|
+
val totalFps = avgFps * fpsReadings + currentFps
|
|
185
|
+
fpsReadings++
|
|
186
|
+
avgFps = totalFps / fpsReadings
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private fun monitorMemoryPeriodically() {
|
|
191
|
+
Thread {
|
|
192
|
+
while (!Thread.interrupted()) {
|
|
193
|
+
try {
|
|
194
|
+
currentMemory = getMemoryUsage()
|
|
195
|
+
peakMemory = peakMemory.coerceAtLeast(currentMemory)
|
|
196
|
+
Thread.sleep(5000) // Check every 5 seconds
|
|
197
|
+
} catch (e: InterruptedException) {
|
|
198
|
+
break
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}.apply {
|
|
202
|
+
isDaemon = true
|
|
203
|
+
start()
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private fun getMemoryUsage(): Long {
|
|
208
|
+
val memoryInfo = Debug.MemoryInfo()
|
|
209
|
+
Debug.getMemoryInfo(memoryInfo)
|
|
210
|
+
return memoryInfo.totalPss.toLong() // in kilobytes
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fun getCacheStats(): String {
|
|
214
|
+
val cacheRate = if ((cacheHits + cacheMisses) > 0)
|
|
215
|
+
(cacheHits * 100 / (cacheHits + cacheMisses)) else 0
|
|
216
|
+
|
|
217
|
+
return "Cache: $cacheHits hits, $cacheMisses misses, ${cacheRate}% hit rate, ${cachedFiles.size} files cached"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fun getFilesServedFromCache(): List<String> {
|
|
221
|
+
return filesServedFromCache.toList()
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fun logAllMetrics() {
|
|
225
|
+
appRunDuration = SystemClock.elapsedRealtime() - appStartTime
|
|
226
|
+
|
|
227
|
+
val memoryDelta = currentMemory - initialMemory
|
|
228
|
+
val memoryGrowth = if (initialMemory > 0) (memoryDelta * 100 / initialMemory) else 0
|
|
229
|
+
|
|
230
|
+
val cacheRate = if ((cacheHits + cacheMisses) > 0)
|
|
231
|
+
(cacheHits * 100 / (cacheHits + cacheMisses)) else 0
|
|
232
|
+
|
|
233
|
+
val sb = StringBuilder()
|
|
234
|
+
sb.appendLine("==================== PERFORMANCE METRICS ====================")
|
|
235
|
+
sb.appendLine("App Run Duration: ${formatDuration(appRunDuration)}")
|
|
236
|
+
sb.appendLine("Cold Start Time: $coldStartDuration ms")
|
|
237
|
+
sb.appendLine("")
|
|
238
|
+
sb.appendLine("PAGE LOADING:")
|
|
239
|
+
sb.appendLine("- Pages Loaded: $totalPagesLoaded")
|
|
240
|
+
sb.appendLine("- Avg Load Time: $avgPageLoadTime ms")
|
|
241
|
+
sb.appendLine("")
|
|
242
|
+
sb.appendLine("RENDERING PERFORMANCE:")
|
|
243
|
+
sb.appendLine("- Current FPS: ${"%.1f".format(currentFps)}")
|
|
244
|
+
sb.appendLine("- Min FPS: ${"%.1f".format(if (minFps == Double.MAX_VALUE) 0.0 else minFps)}")
|
|
245
|
+
sb.appendLine("- Max FPS: ${"%.1f".format(maxFps)}")
|
|
246
|
+
sb.appendLine("- Avg FPS: ${"%.1f".format(avgFps)}")
|
|
247
|
+
sb.appendLine("- UI Blocking Events: $uiBlockingEvents")
|
|
248
|
+
sb.appendLine("- Long Tasks (>16ms): $longTasksDetected")
|
|
249
|
+
sb.appendLine("")
|
|
250
|
+
sb.appendLine("MEMORY USAGE:")
|
|
251
|
+
sb.appendLine("- Initial: ${initialMemory / 1024} MB")
|
|
252
|
+
sb.appendLine("- Current: ${currentMemory / 1024} MB")
|
|
253
|
+
sb.appendLine("- Peak: ${peakMemory / 1024} MB")
|
|
254
|
+
sb.appendLine("- Growth: $memoryGrowth%")
|
|
255
|
+
sb.appendLine("")
|
|
256
|
+
sb.appendLine("CACHE EFFICIENCY:")
|
|
257
|
+
sb.appendLine("- Cache Hits: $cacheHits")
|
|
258
|
+
sb.appendLine("- Cache Misses: $cacheMisses")
|
|
259
|
+
sb.appendLine("- Cache Hit Rate: $cacheRate%")
|
|
260
|
+
sb.appendLine("- Total Network Requests: ${allNetworkRequests.size}")
|
|
261
|
+
sb.appendLine("- Cache Evaluated Requests: ${cacheEvaluatedRequests.size}")
|
|
262
|
+
sb.appendLine("- Not Evaluated for Cache: ${allNetworkRequests.size - cacheEvaluatedRequests.size}")
|
|
263
|
+
sb.appendLine("- Total Files Cached: ${cachedFiles.size}")
|
|
264
|
+
sb.appendLine("- Files Served from Cache: ${filesServedFromCache.size}")
|
|
265
|
+
sb.appendLine("")
|
|
266
|
+
if (filesServedFromCache.isNotEmpty()) {
|
|
267
|
+
sb.appendLine("FILES SERVED FROM CACHE:")
|
|
268
|
+
filesServedFromCache.take(10).forEach { url ->
|
|
269
|
+
sb.appendLine(" ✅ ${url.substringAfterLast("/")}")
|
|
270
|
+
}
|
|
271
|
+
if (filesServedFromCache.size > 10) {
|
|
272
|
+
sb.appendLine(" ... and ${filesServedFromCache.size - 10} more files")
|
|
273
|
+
}
|
|
274
|
+
sb.appendLine("")
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (cacheMissedUrls.isNotEmpty()) {
|
|
278
|
+
sb.appendLine("CACHE MISSES (URLs that should have been cached):")
|
|
279
|
+
// Group by file extension to make it easier to analyze
|
|
280
|
+
val missedByExtension = cacheMissedUrls.groupBy { url ->
|
|
281
|
+
url.substringAfterLast('.').substringBefore('?').substringBefore('#').lowercase()
|
|
282
|
+
}
|
|
283
|
+
missedByExtension.forEach { (ext, urls) ->
|
|
284
|
+
sb.appendLine(" Extension .$ext: ${urls.size} files")
|
|
285
|
+
urls.take(3).forEach { url ->
|
|
286
|
+
sb.appendLine(" ❌ ${url.substringAfterLast("/")}")
|
|
287
|
+
}
|
|
288
|
+
if (urls.size > 3) {
|
|
289
|
+
sb.appendLine(" ... and ${urls.size - 3} more")
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
sb.appendLine("")
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Show requests that were not evaluated for caching
|
|
296
|
+
val notEvaluated = allNetworkRequests.filter { it !in cacheEvaluatedRequests }
|
|
297
|
+
if (notEvaluated.isNotEmpty()) {
|
|
298
|
+
sb.appendLine("REQUESTS NOT EVALUATED FOR CACHE (${notEvaluated.size} total):")
|
|
299
|
+
val groupedNotEvaluated = notEvaluated.groupBy { url ->
|
|
300
|
+
val ext = url.substringAfterLast('.').substringBefore('?').substringBefore('#').lowercase()
|
|
301
|
+
if (ext.length > 10 || !ext.matches(Regex("[a-z0-9]+"))) "other" else ext
|
|
302
|
+
}
|
|
303
|
+
groupedNotEvaluated.forEach { (ext, urls) ->
|
|
304
|
+
sb.appendLine(" .$ext: ${urls.size} files")
|
|
305
|
+
urls.take(2).forEach { url ->
|
|
306
|
+
sb.appendLine(" • ${url.substringAfterLast("/").take(50)}")
|
|
307
|
+
}
|
|
308
|
+
if (urls.size > 2) {
|
|
309
|
+
sb.appendLine(" ... and ${urls.size - 2} more")
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
sb.appendLine("")
|
|
313
|
+
}
|
|
314
|
+
sb.appendLine("==============================================================")
|
|
315
|
+
|
|
316
|
+
Log.i(TAG, sb.toString())
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private fun formatDuration(millis: Long): String {
|
|
320
|
+
return String.format("%02d:%02d:%02d",
|
|
321
|
+
TimeUnit.MILLISECONDS.toHours(millis),
|
|
322
|
+
TimeUnit.MILLISECONDS.toMinutes(millis) % TimeUnit.HOURS.toMinutes(1),
|
|
323
|
+
TimeUnit.MILLISECONDS.toSeconds(millis) % TimeUnit.MINUTES.toSeconds(1))
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fun cleanup() {
|
|
327
|
+
// Remove frame callback
|
|
328
|
+
Choreographer.getInstance().removeFrameCallback(frameCallback)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
companion object {
|
|
332
|
+
private var instance: MetricsMonitor? = null
|
|
333
|
+
|
|
334
|
+
fun getInstance(context: Context): MetricsMonitor {
|
|
335
|
+
return instance ?: synchronized(this) {
|
|
336
|
+
instance ?: MetricsMonitor(context.applicationContext)
|
|
337
|
+
.also { instance = it }
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|