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/MainActivity.kt
CHANGED
|
@@ -1,27 +1,52 @@
|
|
|
1
1
|
package io.yourname.androidproject
|
|
2
2
|
|
|
3
|
+
import android.content.Intent
|
|
4
|
+
import android.os.Build
|
|
3
5
|
import android.os.Bundle
|
|
4
6
|
import android.util.Log
|
|
5
7
|
import android.view.WindowManager
|
|
8
|
+
import androidx.activity.OnBackPressedCallback
|
|
6
9
|
import androidx.appcompat.app.AppCompatActivity
|
|
10
|
+
import androidx.core.view.ViewCompat
|
|
11
|
+
import androidx.core.view.WindowCompat
|
|
12
|
+
|
|
13
|
+
import org.json.JSONObject
|
|
7
14
|
import java.util.Properties
|
|
8
15
|
import io.yourname.androidproject.databinding.ActivityMainBinding
|
|
9
16
|
import io.yourname.androidproject.NativeBridge
|
|
17
|
+
import io.yourname.androidproject.utils.BridgeUtils
|
|
18
|
+
import io.yourname.androidproject.utils.KeyboardUtil
|
|
19
|
+
import io.yourname.androidproject.utils.NetworkUtils
|
|
20
|
+
import io.yourname.androidproject.utils.NotificationConstants
|
|
21
|
+
import io.yourname.androidproject.utils.SafeAreaInsets
|
|
22
|
+
import io.yourname.androidproject.utils.SafeAreaUtils
|
|
23
|
+
import io.yourname.androidproject.security.SecurityCheckScheduler
|
|
10
24
|
import kotlinx.coroutines.CoroutineScope
|
|
11
25
|
import kotlinx.coroutines.MainScope
|
|
12
26
|
import kotlinx.coroutines.cancelChildren
|
|
13
|
-
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|
14
27
|
|
|
15
28
|
class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
16
29
|
|
|
17
|
-
|
|
30
|
+
companion object {
|
|
31
|
+
private const val TAG = "WebViewDebug"
|
|
32
|
+
private const val PREFS_NAME = "safe_area_prefs"
|
|
33
|
+
private const val PREF_SAFE_AREA_TOP = "safe_area_top"
|
|
34
|
+
private const val PREF_SAFE_AREA_RIGHT = "safe_area_right"
|
|
35
|
+
private const val PREF_SAFE_AREA_BOTTOM = "safe_area_bottom"
|
|
36
|
+
private const val PREF_SAFE_AREA_LEFT = "safe_area_left"
|
|
37
|
+
private const val PREF_SAFE_AREA_CACHED = "safe_area_cached"
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
private lateinit var binding: ActivityMainBinding
|
|
19
41
|
private lateinit var nativeBridge: NativeBridge
|
|
20
42
|
private lateinit var customWebView: CustomWebView
|
|
21
|
-
|
|
43
|
+
lateinit var properties: Properties
|
|
44
|
+
private lateinit var metricsMonitor: MetricsMonitor
|
|
45
|
+
private lateinit var keyboardUtil: KeyboardUtil
|
|
22
46
|
private var isHardwareAccelerationEnabled = false
|
|
23
47
|
private var currentUrl: String = ""
|
|
24
|
-
private var
|
|
48
|
+
private var edgeToEdgeEnabled = false
|
|
49
|
+
private var latestSafeAreaInsets: SafeAreaInsets = SafeAreaInsets.ZERO
|
|
25
50
|
|
|
26
51
|
private fun enableHardwareAcceleration() {
|
|
27
52
|
if (!isHardwareAccelerationEnabled) {
|
|
@@ -46,13 +71,193 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
46
71
|
}
|
|
47
72
|
}
|
|
48
73
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
private fun configureEdgeToEdge() {
|
|
75
|
+
edgeToEdgeEnabled = properties
|
|
76
|
+
.getProperty("edgeToEdge.enabled", "false")
|
|
77
|
+
.equals("true", ignoreCase = true)
|
|
78
|
+
|
|
79
|
+
if (edgeToEdgeEnabled) {
|
|
80
|
+
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
81
|
+
} else {
|
|
82
|
+
WindowCompat.setDecorFitsSystemWindows(window, true)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private fun setupSafeAreaHandling() {
|
|
87
|
+
val rootView = binding.root
|
|
88
|
+
val cachedInsets = loadCachedSafeAreaInsets()
|
|
89
|
+
|
|
52
90
|
if (BuildConfig.DEBUG) {
|
|
53
|
-
Log.d(TAG, "
|
|
91
|
+
Log.d(TAG, "📋 Cache lookup result: $cachedInsets")
|
|
54
92
|
}
|
|
55
93
|
|
|
94
|
+
if (cachedInsets != null) {
|
|
95
|
+
latestSafeAreaInsets = cachedInsets
|
|
96
|
+
customWebView.setDefaultRequestHeaders(buildSafeAreaHeaders())
|
|
97
|
+
|
|
98
|
+
if (BuildConfig.DEBUG) {
|
|
99
|
+
Log.d(TAG, "✅ Using cached safe area insets: $cachedInsets")
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
val initialInsets = SafeAreaUtils.getSafeAreaInsets(window, rootView, edgeToEdgeEnabled)
|
|
103
|
+
latestSafeAreaInsets = initialInsets
|
|
104
|
+
customWebView.setDefaultRequestHeaders(buildSafeAreaHeaders())
|
|
105
|
+
|
|
106
|
+
if (BuildConfig.DEBUG) {
|
|
107
|
+
Log.d(TAG, "🆕 No cache found - initial insets: $initialInsets")
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
rootView.post {
|
|
112
|
+
val postLayoutInsets = SafeAreaUtils.getSafeAreaInsets(window, rootView, edgeToEdgeEnabled)
|
|
113
|
+
|
|
114
|
+
if (postLayoutInsets != latestSafeAreaInsets) {
|
|
115
|
+
latestSafeAreaInsets = postLayoutInsets
|
|
116
|
+
customWebView.setDefaultRequestHeaders(buildSafeAreaHeaders())
|
|
117
|
+
notifySafeAreaUpdate(postLayoutInsets)
|
|
118
|
+
|
|
119
|
+
if (BuildConfig.DEBUG) {
|
|
120
|
+
Log.d(TAG, "🔄 Updated safe area insets: $postLayoutInsets")
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Save to cache if not already cached or if values changed
|
|
125
|
+
if (cachedInsets == null || cachedInsets != postLayoutInsets) {
|
|
126
|
+
saveSafeAreaInsetsToCache(postLayoutInsets)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
private fun buildSafeAreaHeaders(): Map<String, String> = mapOf(
|
|
132
|
+
"X-Safe-Area-Top" to latestSafeAreaInsets.top.toString(),
|
|
133
|
+
"X-Safe-Area-Right" to latestSafeAreaInsets.right.toString(),
|
|
134
|
+
"X-Safe-Area-Bottom" to latestSafeAreaInsets.bottom.toString(),
|
|
135
|
+
"X-Safe-Area-Left" to latestSafeAreaInsets.left.toString(),
|
|
136
|
+
// Prevent caching of SSR response so updated headers are always used
|
|
137
|
+
"Cache-Control" to "no-cache, no-store, must-revalidate",
|
|
138
|
+
"Pragma" to "no-cache"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
// Public method for NativeBridge to get current safe area insets
|
|
142
|
+
fun getCurrentSafeAreaInsets(): SafeAreaInsets = latestSafeAreaInsets
|
|
143
|
+
|
|
144
|
+
fun clearWebCache() {
|
|
145
|
+
customWebView.clearAllCache()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fun setScreenSecure(enable: Boolean) {
|
|
149
|
+
if (enable) {
|
|
150
|
+
window.setFlags(
|
|
151
|
+
WindowManager.LayoutParams.FLAG_SECURE,
|
|
152
|
+
WindowManager.LayoutParams.FLAG_SECURE
|
|
153
|
+
)
|
|
154
|
+
} else {
|
|
155
|
+
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
fun isScreenSecure(): Boolean {
|
|
160
|
+
return (window.attributes.flags and WindowManager.LayoutParams.FLAG_SECURE) != 0
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Notify WebView when safe area insets are updated
|
|
165
|
+
* Called when deferred inset calculation completes
|
|
166
|
+
*/
|
|
167
|
+
private fun notifySafeAreaUpdate(insets: SafeAreaInsets) {
|
|
168
|
+
if (::nativeBridge.isInitialized && ::customWebView.isInitialized) {
|
|
169
|
+
val insetsJson = org.json.JSONObject().apply {
|
|
170
|
+
put("top", insets.top)
|
|
171
|
+
put("right", insets.right)
|
|
172
|
+
put("bottom", insets.bottom)
|
|
173
|
+
put("left", insets.left)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
BridgeUtils.notifyWebJson(
|
|
177
|
+
customWebView.getWebView(),
|
|
178
|
+
BridgeUtils.WebEvents.ON_SAFE_AREA_INSETS_UPDATED,
|
|
179
|
+
insetsJson
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if (BuildConfig.DEBUG) {
|
|
183
|
+
Log.d(TAG, "📐 Safe area insets updated and sent to WebView: $insets")
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Load cached safe area insets from SharedPreferences
|
|
190
|
+
* Returns null if no cache exists
|
|
191
|
+
*/
|
|
192
|
+
private fun loadCachedSafeAreaInsets(): SafeAreaInsets? {
|
|
193
|
+
val prefs = getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE)
|
|
194
|
+
|
|
195
|
+
if (!prefs.getBoolean(PREF_SAFE_AREA_CACHED, false)) {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return SafeAreaInsets(
|
|
200
|
+
top = prefs.getInt(PREF_SAFE_AREA_TOP, 0),
|
|
201
|
+
right = prefs.getInt(PREF_SAFE_AREA_RIGHT, 0),
|
|
202
|
+
bottom = prefs.getInt(PREF_SAFE_AREA_BOTTOM, 0),
|
|
203
|
+
left = prefs.getInt(PREF_SAFE_AREA_LEFT, 0)
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Save safe area insets to SharedPreferences for future launches
|
|
209
|
+
* Only saves if insets contain at least one non-zero value
|
|
210
|
+
*/
|
|
211
|
+
private fun saveSafeAreaInsetsToCache(insets: SafeAreaInsets) {
|
|
212
|
+
// Only cache if we have meaningful (non-zero) values
|
|
213
|
+
// This prevents caching zeros from incomplete initialization
|
|
214
|
+
if (insets.top == 0 && insets.right == 0 && insets.bottom == 0 && insets.left == 0) {
|
|
215
|
+
if (BuildConfig.DEBUG) {
|
|
216
|
+
Log.d(TAG, "⚠️ Skipping cache save - all insets are zero")
|
|
217
|
+
}
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
val saved = getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE).edit().apply {
|
|
222
|
+
putInt(PREF_SAFE_AREA_TOP, insets.top)
|
|
223
|
+
putInt(PREF_SAFE_AREA_RIGHT, insets.right)
|
|
224
|
+
putInt(PREF_SAFE_AREA_BOTTOM, insets.bottom)
|
|
225
|
+
putInt(PREF_SAFE_AREA_LEFT, insets.left)
|
|
226
|
+
putBoolean(PREF_SAFE_AREA_CACHED, true)
|
|
227
|
+
}.commit() // Use commit() instead of apply() to ensure immediate persistence
|
|
228
|
+
|
|
229
|
+
if (BuildConfig.DEBUG) {
|
|
230
|
+
if (saved) {
|
|
231
|
+
// Verify by reading back immediately
|
|
232
|
+
val prefs = getSharedPreferences(PREFS_NAME, android.content.Context.MODE_PRIVATE)
|
|
233
|
+
val verified = SafeAreaInsets(
|
|
234
|
+
top = prefs.getInt(PREF_SAFE_AREA_TOP, -1),
|
|
235
|
+
right = prefs.getInt(PREF_SAFE_AREA_RIGHT, -1),
|
|
236
|
+
bottom = prefs.getInt(PREF_SAFE_AREA_BOTTOM, -1),
|
|
237
|
+
left = prefs.getInt(PREF_SAFE_AREA_LEFT, -1)
|
|
238
|
+
)
|
|
239
|
+
Log.d(TAG, "💾 Cached safe area insets: $insets")
|
|
240
|
+
Log.d(TAG, "✓ Verified cached values: $verified")
|
|
241
|
+
} else {
|
|
242
|
+
Log.e(TAG, "❌ Failed to cache safe area insets: $insets")
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private fun loadUrlWithSafeArea(url: String) {
|
|
248
|
+
// Set headers with current safe area insets
|
|
249
|
+
customWebView.setDefaultRequestHeaders(buildSafeAreaHeaders())
|
|
250
|
+
// Load URL with safe area headers
|
|
251
|
+
customWebView.loadUrl(url)
|
|
252
|
+
|
|
253
|
+
if (BuildConfig.DEBUG) {
|
|
254
|
+
Log.d(TAG, "🔄 Loading URL with safe area headers: $latestSafeAreaInsets")
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
259
|
+
super.onCreate(savedInstanceState)
|
|
260
|
+
|
|
56
261
|
// Load properties
|
|
57
262
|
properties = Properties()
|
|
58
263
|
try {
|
|
@@ -66,15 +271,29 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
66
271
|
properties.setProperty("buildOptimisation", (!BuildConfig.DEBUG).toString())
|
|
67
272
|
}
|
|
68
273
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
274
|
+
configureEdgeToEdge()
|
|
275
|
+
|
|
276
|
+
// Initialize security checks (release builds only)
|
|
277
|
+
if (!BuildConfig.DEBUG) {
|
|
278
|
+
SecurityCheckScheduler.initialize(this, this, object : SecurityCheckScheduler.SecurityCheckCallback {
|
|
279
|
+
override fun onSecurityCheckComplete(results: JSONObject) {
|
|
280
|
+
io.yourname.androidproject.security.SecurityAlertHandler.handleSecurityCheckResults(this@MainActivity, results)
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Initialize MetricsMonitor
|
|
286
|
+
metricsMonitor = MetricsMonitor.getInstance(this)
|
|
72
287
|
|
|
73
288
|
// Setup UI
|
|
74
289
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
75
290
|
supportActionBar?.hide()
|
|
76
291
|
setContentView(binding.root)
|
|
77
|
-
|
|
292
|
+
|
|
293
|
+
// Initialize keyboard utility
|
|
294
|
+
keyboardUtil = KeyboardUtil(this, binding.webviewContainer)
|
|
295
|
+
keyboardUtil.initialize()
|
|
296
|
+
|
|
78
297
|
// Enable hardware acceleration for the window
|
|
79
298
|
enableHardwareAcceleration()
|
|
80
299
|
|
|
@@ -100,39 +319,80 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
100
319
|
|
|
101
320
|
// Clean the cache
|
|
102
321
|
customWebView.cleanupCache()
|
|
103
|
-
|
|
322
|
+
|
|
104
323
|
// Setup NativeBridge
|
|
105
324
|
try {
|
|
106
|
-
nativeBridge = NativeBridge(this, customWebView.getWebView())
|
|
325
|
+
nativeBridge = NativeBridge(this, customWebView.getWebView(), properties)
|
|
107
326
|
customWebView.addJavascriptInterface(nativeBridge, "NativeBridge")
|
|
108
327
|
} catch (e: Exception) {
|
|
109
328
|
Log.e(TAG, "Failed to initialize NativeBridge: ${e.message}")
|
|
110
329
|
}
|
|
330
|
+
|
|
331
|
+
setupSafeAreaHandling()
|
|
332
|
+
|
|
333
|
+
// Setup back press handler (modern API)
|
|
334
|
+
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
|
335
|
+
override fun handleOnBackPressed() {
|
|
336
|
+
if (customWebView.canGoBack()) {
|
|
337
|
+
customWebView.goBack()
|
|
338
|
+
} else {
|
|
339
|
+
// Disable this callback and let the system handle back press
|
|
340
|
+
isEnabled = false
|
|
341
|
+
onBackPressedDispatcher.onBackPressed()
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
})
|
|
111
345
|
|
|
112
|
-
// Load URL based on environment
|
|
346
|
+
// Load URL based on environment and deep link data
|
|
113
347
|
try {
|
|
114
|
-
if (BuildConfig.DEBUG) {
|
|
115
348
|
// Use the local development server in debug mode
|
|
116
349
|
val local_ip = properties.getProperty("LOCAL_IP", "localhost")
|
|
350
|
+
val initial_url = properties.getProperty("initial_url", "")
|
|
117
351
|
val port = properties.getProperty("port", "3005")
|
|
118
352
|
val useHttps = properties.getProperty("useHttps", "false").toBoolean()
|
|
119
353
|
val protocol = if (useHttps) "https" else "http"
|
|
120
|
-
currentUrl = "$protocol://$local_ip:$port"
|
|
354
|
+
currentUrl = "$protocol://$local_ip:$port/$initial_url"
|
|
355
|
+
customWebView.updateLastTargetUrl(currentUrl)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
// Check for notification and handle via /notification endpoint
|
|
359
|
+
if (intent.getBooleanExtra(NotificationConstants.EXTRA_IS_NOTIFICATION, false)) {
|
|
360
|
+
handleNotificationClick(currentUrl, intent)
|
|
121
361
|
} else {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
362
|
+
val isOnline = NetworkUtils.getCurrentStatus(this).isOnline
|
|
363
|
+
if (isOnline) {
|
|
364
|
+
if (BuildConfig.DEBUG) {
|
|
365
|
+
Log.d(TAG, "🔗 Loading base URL: $currentUrl")
|
|
366
|
+
}
|
|
367
|
+
loadUrlWithSafeArea(currentUrl)
|
|
368
|
+
} else {
|
|
369
|
+
if (BuildConfig.DEBUG) {
|
|
370
|
+
Log.w(TAG, "📴 Device offline on launch, showing offline page")
|
|
371
|
+
}
|
|
372
|
+
customWebView.showOfflinePage()
|
|
127
373
|
}
|
|
128
374
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
375
|
+
|
|
376
|
+
metricsMonitor.markAppStartComplete()
|
|
377
|
+
|
|
132
378
|
} catch (e: Exception) {
|
|
133
379
|
Log.e(TAG, "Failed to load initial URL: ${e.message}")
|
|
380
|
+
// TODO: Add HTML file workflow - error.html not available yet
|
|
134
381
|
// Fallback to local asset as a last resort
|
|
135
|
-
customWebView.loadUrl("file:///android_asset/build/public/error.html")
|
|
382
|
+
// customWebView.loadUrl("file:///android_asset/build/public/error.html")
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
override fun onNewIntent(intent: Intent?) {
|
|
387
|
+
super.onNewIntent(intent)
|
|
388
|
+
// Handle notification click when app is already running
|
|
389
|
+
intent?.let { newIntent ->
|
|
390
|
+
if (newIntent.getBooleanExtra(NotificationConstants.EXTRA_IS_NOTIFICATION, false)) {
|
|
391
|
+
handleNotificationClick(currentUrl, newIntent)
|
|
392
|
+
} else {
|
|
393
|
+
Log.d(TAG, "🔗 onNewIntent - Loading base URL: $currentUrl")
|
|
394
|
+
loadUrlWithSafeArea(currentUrl)
|
|
395
|
+
}
|
|
136
396
|
}
|
|
137
397
|
}
|
|
138
398
|
|
|
@@ -145,17 +405,15 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
145
405
|
}
|
|
146
406
|
}
|
|
147
407
|
|
|
148
|
-
override fun onBackPressed() {
|
|
149
|
-
if (customWebView.canGoBack()) {
|
|
150
|
-
customWebView.goBack()
|
|
151
|
-
} else {
|
|
152
|
-
super.onBackPressed()
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
408
|
override fun onPause() {
|
|
157
409
|
super.onPause()
|
|
158
410
|
customWebView.onPause()
|
|
411
|
+
|
|
412
|
+
// Log metrics on pause in case onDestroy isn't called
|
|
413
|
+
if (BuildConfig.DEBUG) {
|
|
414
|
+
Log.d(TAG, "⏸️ App paused - logging current metrics...")
|
|
415
|
+
metricsMonitor.logAllMetrics()
|
|
416
|
+
}
|
|
159
417
|
}
|
|
160
418
|
|
|
161
419
|
override fun onResume() {
|
|
@@ -163,26 +421,126 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
163
421
|
customWebView.onResume()
|
|
164
422
|
}
|
|
165
423
|
|
|
424
|
+
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
425
|
+
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
426
|
+
if (::nativeBridge.isInitialized) {
|
|
427
|
+
nativeBridge.handlePermissionResult(requestCode, permissions, grantResults)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
166
431
|
override fun onDestroy() {
|
|
432
|
+
// Log all performance metrics before destroying
|
|
433
|
+
if (BuildConfig.DEBUG) {
|
|
434
|
+
Log.d(TAG, "🏁 App shutting down - logging final metrics...")
|
|
435
|
+
}
|
|
436
|
+
metricsMonitor.logAllMetrics()
|
|
437
|
+
metricsMonitor.cleanup()
|
|
438
|
+
|
|
439
|
+
// Cleanup NativeBridge resources (stops FrameworkServer, cancels coroutines)
|
|
440
|
+
if (::nativeBridge.isInitialized) {
|
|
441
|
+
nativeBridge.cleanup()
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Cleanup keyboard utility
|
|
445
|
+
if (::keyboardUtil.isInitialized) {
|
|
446
|
+
keyboardUtil.cleanup()
|
|
447
|
+
}
|
|
448
|
+
if (::nativeBridge.isInitialized) {
|
|
449
|
+
nativeBridge.cleanup()
|
|
450
|
+
}
|
|
167
451
|
coroutineContext.cancelChildren()
|
|
168
452
|
customWebView.destroy()
|
|
169
453
|
super.onDestroy()
|
|
170
454
|
}
|
|
171
455
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
456
|
+
/**
|
|
457
|
+
* Handle notification click by navigating to /notification route
|
|
458
|
+
* Ultra-simple approach: always go to /notification with minimal params
|
|
459
|
+
*/
|
|
460
|
+
private fun handleNotificationClick(baseUrl: String, intent: Intent) {
|
|
461
|
+
try {
|
|
462
|
+
val action = intent.getStringExtra(NotificationConstants.EXTRA_ACTION)
|
|
463
|
+
val notificationData = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_DATA)
|
|
464
|
+
val notificationId = intent.getStringExtra(NotificationConstants.EXTRA_NOTIFICATION_ID)
|
|
465
|
+
|
|
466
|
+
Log.d(TAG, "🔔 Handling notification click - Action: ${action ?: "none"}")
|
|
467
|
+
|
|
468
|
+
// Dismiss the notification
|
|
469
|
+
val notificationManager = getSystemService(android.content.Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
|
|
470
|
+
if (!notificationId.isNullOrBlank()) {
|
|
471
|
+
notificationManager.cancel(notificationId.hashCode())
|
|
472
|
+
} else {
|
|
473
|
+
notificationManager.cancelAll() // Fallback if we didn't get an ID
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Build simple /notification URL
|
|
477
|
+
val url = buildNotificationUrl(baseUrl, action, notificationData)
|
|
478
|
+
Log.d(TAG, "🔔 Navigating to: $url")
|
|
479
|
+
|
|
480
|
+
// Notify web layer about the action/tap
|
|
481
|
+
try {
|
|
482
|
+
val payload = org.json.JSONObject().apply {
|
|
483
|
+
put("action", action ?: "tap")
|
|
484
|
+
put("deepLinkURL", url)
|
|
485
|
+
if (!notificationData.isNullOrBlank()) {
|
|
486
|
+
put("data", org.json.JSONObject(notificationData))
|
|
487
|
+
}
|
|
488
|
+
if (!notificationId.isNullOrBlank()) {
|
|
489
|
+
put("notificationId", notificationId)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
val webView = customWebView.getWebView()
|
|
493
|
+
if (action.isNullOrBlank()) {
|
|
494
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.NOTIFICATION_TAPPED, payload.toString())
|
|
495
|
+
} else {
|
|
496
|
+
BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.NOTIFICATION_ACTION_PERFORMED, payload.toString())
|
|
497
|
+
}
|
|
498
|
+
} catch (e: Exception) {
|
|
499
|
+
Log.e(TAG, "Failed to notify web of notification action", e)
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
customWebView.updateLastTargetUrl(url)
|
|
503
|
+
val isOnline = NetworkUtils.getCurrentStatus(this).isOnline
|
|
504
|
+
if (isOnline) {
|
|
505
|
+
loadUrlWithSafeArea(url)
|
|
183
506
|
} else {
|
|
184
|
-
|
|
507
|
+
Log.w(TAG, "📴 Offline during notification click, showing offline page")
|
|
508
|
+
customWebView.showOfflinePage()
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
} catch (e: Exception) {
|
|
512
|
+
Log.e(TAG, "🔔 Error handling notification click: ${e.message}", e)
|
|
513
|
+
loadUrlWithSafeArea(baseUrl)
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Build ultra-simple /notification URL
|
|
519
|
+
*/
|
|
520
|
+
private fun buildNotificationUrl(baseUrl: String, action: String?, notificationData: String?): String {
|
|
521
|
+
return try {
|
|
522
|
+
val url = StringBuilder("$baseUrl/notification")
|
|
523
|
+
val params = mutableListOf<String>()
|
|
524
|
+
|
|
525
|
+
// Add action if present
|
|
526
|
+
if (!action.isNullOrEmpty()) {
|
|
527
|
+
params.add("action=${java.net.URLEncoder.encode(action, "UTF-8")}")
|
|
185
528
|
}
|
|
529
|
+
|
|
530
|
+
// Add data if present
|
|
531
|
+
if (!notificationData.isNullOrEmpty()) {
|
|
532
|
+
params.add("data=${java.net.URLEncoder.encode(notificationData, "UTF-8")}")
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (params.isNotEmpty()) {
|
|
536
|
+
url.append("?").append(params.joinToString("&"))
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
url.toString()
|
|
540
|
+
|
|
541
|
+
} catch (e: Exception) {
|
|
542
|
+
Log.e(TAG, "Error building notification URL: ${e.message}", e)
|
|
543
|
+
"$baseUrl/notification"
|
|
186
544
|
}
|
|
187
545
|
}
|
|
188
|
-
}
|
|
546
|
+
}
|