catalyst-core 0.1.0-beta.2 → 0.1.0-beta.4
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 +1 -1
- package/bin/catalyst.js +8 -1
- package/changelog.md +6 -0
- package/dist/native/androidProject/app/build.gradle.kts +9 -0
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +2 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +17 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +31 -3
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +148 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeCameraManager.kt +337 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/TransitionManager.kt +202 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/WebCacheManager.kt +3 -3
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +123 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +244 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/HoldController.kt +70 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/TorchController.kt +57 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamState.kt +41 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamStateMachine.kt +40 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ViewfinderMapper.kt +75 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ZoomController.kt +156 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +7 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +253 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/security/FridaDetector.kt +16 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +42 -5
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +7 -3
- package/dist/native/androidProject/app/src/main/res/drawable/debug_viewfinder_border.xml +8 -0
- package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +37 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +139 -0
- package/dist/native/androidSetup.js +1 -1
- package/dist/native/bridge/WebBridge.js +5 -5
- package/dist/native/bridge/constants/NativeInterfaces.js +9 -5
- package/dist/native/bridge/hooks/index.js +1 -0
- package/dist/native/bridge/hooks/useCamera.js +9 -0
- package/dist/native/bridge/hooks/useDataProtection.js +2 -0
- package/dist/native/bridge/hooks/useDeviceInfo.js +2 -0
- package/dist/native/bridge/hooks/useFilePicker.js +2 -0
- package/dist/native/bridge/hooks/useGoogleSignIn.js +1 -0
- package/dist/native/bridge/hooks/useHapticFeedback.js +1 -0
- package/dist/native/bridge/hooks/useIntent.js +1 -0
- package/dist/native/bridge/hooks/useNativeTransition.js +43 -0
- package/dist/native/bridge/hooks/useNetworkStatus.js +3 -0
- package/dist/native/bridge/hooks/useNotification.js +2 -0
- package/dist/native/bridge/hooks/useSafeArea.js +2 -0
- package/dist/native/bridge/hooks/useVideoStream.js +6 -0
- package/dist/native/bridge/hooks/utils.js +1 -0
- package/dist/native/bridge/hooks.js +1 -137
- package/dist/native/bridge/useBaseHook.js +12 -7
- package/dist/native/bridge/utils/NativeBridge.js +41 -6
- package/dist/native/buildAppAndroid.js +7 -9
- package/dist/native/buildAppIos.js +14 -22
- package/dist/native/internalPluginUtils.js +1 -0
- package/dist/native/iosnativeWebView/Package.resolved +36 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/BarcodeDetector.swift +116 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraPreviewView.swift +58 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +308 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +63 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +194 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/TorchController.swift +60 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamState.swift +44 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamStateMachine.swift +60 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +130 -0
- package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +11 -1
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +4 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +227 -4
- package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +169 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +38 -7
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +6 -2
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +15 -4
- package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +11 -17
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +8 -2
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
- package/dist/native/plugin-bridge/PluginBridge.js +1 -0
- package/dist/native/pluginComposerAndroid.js +9 -0
- package/dist/native/pluginComposerIos.js +7 -0
- package/dist/native/renameAndroidProject.js +3 -3
- package/dist/native/setupServer.js +2 -3
- package/dist/native/utils.js +4 -1
- package/dist/otel.js +70 -2
- package/dist/scripts/devServe.js +1 -1
- package/dist/scripts/plugins.js +1 -0
- package/dist/scripts/registerAliases.js +6 -2
- package/dist/scripts/scriptUtils.js +2 -1
- package/dist/scripts/serve.js +2 -1
- package/dist/scripts/start.js +4 -1
- package/dist/server/renderer/extract.js +5 -5
- package/dist/server/renderer/handler.js +15 -14
- package/dist/server/utils/validator.js +1 -1
- package/dist/webpack/base.babel.js +2 -1
- package/dist/webpack/development.client.babel.js +6 -2
- package/dist/webpack/production.ssr.babel.js +5 -1
- package/mcp_v2/knowledge-base.json +1564 -1067
- package/mcp_v2/lib/helpers.js +19 -7
- package/mcp_v2/tools/build.js +1 -1
- package/mcp_v2/tools/config.js +16 -4
- package/mcp_v2/tools/conversion.js +11 -11
- package/mcp_v2/tools/knowledge.js +16 -3
- package/mcp_v2/tools/tasks.js +13 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
Catalyst offers a comprehensive suite of features designed for modern web development. It includes isomorphic rendering for optimal performance, an extendable server with full-stack capabilities, and configurable state management. The framework employs smart prefetching of data and chunks, allows easy configuration of global styles and layouts, and provides SEO optimization at both global and page levels.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Catalyst also supports universal apps for Android and iOS. See the [Universal App guide](https://catalyst.1mg.com/public_docs/content/Guides%20and%20Tutorials/First%20Universal%20App/first-universal-app) to get started.
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
package/bin/catalyst.js
CHANGED
|
@@ -13,6 +13,8 @@ const validCommands = [
|
|
|
13
13
|
"serve",
|
|
14
14
|
"devBuild",
|
|
15
15
|
"devServe",
|
|
16
|
+
"plugin",
|
|
17
|
+
"plugins",
|
|
16
18
|
"buildApp",
|
|
17
19
|
"buildApp:ios",
|
|
18
20
|
"buildApp:android",
|
|
@@ -67,11 +69,14 @@ const scriptIndex = args.findIndex(
|
|
|
67
69
|
x === "serve" ||
|
|
68
70
|
x === "devBuild" ||
|
|
69
71
|
x === "devServe" ||
|
|
72
|
+
x === "plugin" ||
|
|
73
|
+
x === "plugins" ||
|
|
70
74
|
isPlatformCommand(x, "buildApp") ||
|
|
71
75
|
isPlatformCommand(x, "setupEmulator")
|
|
72
76
|
)
|
|
73
77
|
const script = scriptIndex === -1 ? args[0] : args[scriptIndex]
|
|
74
78
|
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []
|
|
79
|
+
const resolvedScript = script === "plugin" ? "plugins" : script
|
|
75
80
|
|
|
76
81
|
if (validCommands.includes(script)) {
|
|
77
82
|
// Handle platform-specific or combined commands
|
|
@@ -90,7 +95,9 @@ if (validCommands.includes(script)) {
|
|
|
90
95
|
// Original commands
|
|
91
96
|
const result = spawnSync(
|
|
92
97
|
process.execPath,
|
|
93
|
-
nodeArgs
|
|
98
|
+
nodeArgs
|
|
99
|
+
.concat(require.resolve("../dist/scripts/" + resolvedScript))
|
|
100
|
+
.concat(args.slice(scriptIndex + 1)),
|
|
94
101
|
{ stdio: "inherit" }
|
|
95
102
|
)
|
|
96
103
|
handleProcessResult(result)
|
package/changelog.md
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
- Moved Catalyst into a monorepo structure with `catalyst-core`, `create-catalyst-app`, the Catalyst docs app, and the Catalyst core test app managed from one repository.
|
|
6
6
|
- Replaced the internal-package pre-release flow with a local release sandbox that scaffolds a real app using current-branch CCA and a locally packed current-branch `catalyst-core`.
|
|
7
7
|
|
|
8
|
+
## [0.1.0-canary.8] - 2026-04-15
|
|
9
|
+
|
|
10
|
+
- Introduced Catalyst’s internal plugin architecture, adding the `PluginBridge` web API, native plugin runtimes for Android and iOS, and manifest-driven build composition for modular native capabilities.
|
|
11
|
+
- Added config-based plugin enablement through `WEBVIEW_CONFIG.plugins` and a `catalyst plugins` CLI for discovering and managing internal framework plugins from the app layer.
|
|
12
|
+
- Shipped the first cross-platform migrated plugin, `device_info`
|
|
13
|
+
|
|
8
14
|
## [0.1.0-beta.1] - 2026-04-15
|
|
9
15
|
|
|
10
16
|
- Promoted `0.1.0-canary.7` to the first proper beta release after stabilization.
|
|
@@ -208,6 +208,15 @@ dependencies {
|
|
|
208
208
|
// SLF4J simple logger for Ktor (optional, can be excluded if needed)
|
|
209
209
|
implementation("org.slf4j:slf4j-simple:2.0.9")
|
|
210
210
|
|
|
211
|
+
// CameraX
|
|
212
|
+
implementation("androidx.camera:camera-core:1.3.4")
|
|
213
|
+
implementation("androidx.camera:camera-camera2:1.3.4")
|
|
214
|
+
implementation("androidx.camera:camera-lifecycle:1.3.4")
|
|
215
|
+
implementation("androidx.camera:camera-view:1.3.4")
|
|
216
|
+
|
|
217
|
+
// ML Kit Barcode Scanning
|
|
218
|
+
implementation("com.google.mlkit:barcode-scanning:17.3.0")
|
|
219
|
+
|
|
211
220
|
// Notification dependencies - conditional based on config
|
|
212
221
|
if (isNotificationsEnabled()) {
|
|
213
222
|
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0")
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
android:usesCleartextTraffic="true"
|
|
25
25
|
android:networkSecurityConfig="@xml/network_security_config"
|
|
26
26
|
tools:targetApi="31">
|
|
27
|
+
<!-- nosemgrep: java.android.security.exported_activity.exported_activity - Launcher activity must be exported so Android can start the app from the launcher. -->
|
|
27
28
|
<activity
|
|
28
29
|
android:name="io.yourname.androidproject.SplashActivity"
|
|
29
30
|
android:exported="true"
|
|
@@ -51,4 +52,4 @@
|
|
|
51
52
|
</provider>
|
|
52
53
|
</application>
|
|
53
54
|
|
|
54
|
-
</manifest>
|
|
55
|
+
</manifest>
|
|
@@ -60,7 +60,7 @@ data class SchemaDefinition(
|
|
|
60
60
|
object BridgeMessageValidator {
|
|
61
61
|
|
|
62
62
|
private const val TAG = "BridgeMessageValidator"
|
|
63
|
-
private const val MAX_MESSAGE_SIZE =
|
|
63
|
+
private const val MAX_MESSAGE_SIZE = CatalystConstants.Bridge.MAX_MESSAGE_SIZE
|
|
64
64
|
|
|
65
65
|
// Valid commands — single source of truth from CatalystConstants
|
|
66
66
|
private val validCommands = CatalystConstants.Bridge.VALID_COMMANDS
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt
CHANGED
|
@@ -51,6 +51,7 @@ class CustomWebView(
|
|
|
51
51
|
private var offlinePageVisible = false
|
|
52
52
|
private var lastTargetUrl: String? = null
|
|
53
53
|
private var defaultRequestHeaders: Map<String, String> = emptyMap()
|
|
54
|
+
var onPageStarted: (() -> Unit)? = null
|
|
54
55
|
|
|
55
56
|
// Counters for asset loading statistics
|
|
56
57
|
private var assetLoadAttempts = 0
|
|
@@ -233,7 +234,7 @@ class CustomWebView(
|
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
// Additional security check: only allow whitelisted interface names
|
|
236
|
-
val allowedInterfaces = setOf("NativeBridge", "AndroidBridge")
|
|
237
|
+
val allowedInterfaces = setOf("NativeBridge", "AndroidBridge", "PluginBridge")
|
|
237
238
|
if (name !in allowedInterfaces) {
|
|
238
239
|
Log.e(TAG, "❌ Security: Interface name '$name' is not in whitelist. Refusing to add interface.")
|
|
239
240
|
return
|
|
@@ -246,6 +247,17 @@ class CustomWebView(
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
fun removeJavascriptInterface(name: String) {
|
|
251
|
+
try {
|
|
252
|
+
webView.removeJavascriptInterface(name)
|
|
253
|
+
if (BuildConfig.DEBUG) {
|
|
254
|
+
Log.d(TAG, "🔌 Removed JavaScript interface: $name")
|
|
255
|
+
}
|
|
256
|
+
} catch (e: Exception) {
|
|
257
|
+
Log.w(TAG, "⚠️ Failed to remove JavaScript interface '$name': ${e.message}")
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
249
261
|
fun destroy() {
|
|
250
262
|
job.cancel()
|
|
251
263
|
webView.destroy()
|
|
@@ -606,6 +618,9 @@ class CustomWebView(
|
|
|
606
618
|
domStorageEnabled = true
|
|
607
619
|
allowFileAccess = true
|
|
608
620
|
allowContentAccess = true
|
|
621
|
+
setSupportZoom(false)
|
|
622
|
+
builtInZoomControls = false
|
|
623
|
+
displayZoomControls = false
|
|
609
624
|
|
|
610
625
|
// TODO: Enable these when build optimization feature is implemented
|
|
611
626
|
// These are deprecated but may be needed for local file access in development
|
|
@@ -882,6 +897,7 @@ class CustomWebView(
|
|
|
882
897
|
super.onPageStarted(view, url, favicon)
|
|
883
898
|
if (url != null && url != offlineAssetUrl) {
|
|
884
899
|
offlinePageVisible = false
|
|
900
|
+
onPageStarted?.invoke()
|
|
885
901
|
}
|
|
886
902
|
progressBar.visibility = View.VISIBLE
|
|
887
903
|
val startTime = System.currentTimeMillis()
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -4,6 +4,7 @@ import android.content.Intent
|
|
|
4
4
|
import android.os.Build
|
|
5
5
|
import android.os.Bundle
|
|
6
6
|
import android.util.Log
|
|
7
|
+
import android.view.MotionEvent
|
|
7
8
|
import android.view.WindowManager
|
|
8
9
|
import androidx.activity.OnBackPressedCallback
|
|
9
10
|
import androidx.appcompat.app.AppCompatActivity
|
|
@@ -14,6 +15,7 @@ import org.json.JSONObject
|
|
|
14
15
|
import java.util.Properties
|
|
15
16
|
import io.yourname.androidproject.databinding.ActivityMainBinding
|
|
16
17
|
import io.yourname.androidproject.NativeBridge
|
|
18
|
+
import io.yourname.androidproject.plugins.PluginBridge
|
|
17
19
|
import io.yourname.androidproject.utils.BridgeUtils
|
|
18
20
|
import io.yourname.androidproject.utils.KeyboardUtil
|
|
19
21
|
import io.yourname.androidproject.utils.NetworkUtils
|
|
@@ -39,6 +41,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
39
41
|
|
|
40
42
|
private lateinit var binding: ActivityMainBinding
|
|
41
43
|
private lateinit var nativeBridge: NativeBridge
|
|
44
|
+
private lateinit var pluginBridge: PluginBridge
|
|
42
45
|
private lateinit var customWebView: CustomWebView
|
|
43
46
|
lateinit var properties: Properties
|
|
44
47
|
private lateinit var metricsMonitor: MetricsMonitor
|
|
@@ -324,10 +327,23 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
324
327
|
try {
|
|
325
328
|
nativeBridge = NativeBridge(this, customWebView.getWebView(), properties)
|
|
326
329
|
customWebView.addJavascriptInterface(nativeBridge, "NativeBridge")
|
|
330
|
+
|
|
331
|
+
// Wire NativeCameraManager
|
|
332
|
+
val cameraManager = NativeCameraManager(this, binding.cameraPreview, customWebView.getWebView(), binding.debugViewfinderOverlay, binding.debugViewfinderOverlay.findViewById(R.id.debug_qr_status), binding.debugBarcodeOverlay)
|
|
333
|
+
nativeBridge.setCameraManager(cameraManager)
|
|
334
|
+
customWebView.onPageStarted = { cameraManager.stop() }
|
|
327
335
|
} catch (e: Exception) {
|
|
328
336
|
Log.e(TAG, "Failed to initialize NativeBridge: ${e.message}")
|
|
329
337
|
}
|
|
330
338
|
|
|
339
|
+
// Setup isolated PluginBridge
|
|
340
|
+
try {
|
|
341
|
+
pluginBridge = PluginBridge(this, customWebView.getWebView(), properties)
|
|
342
|
+
customWebView.addJavascriptInterface(pluginBridge, "PluginBridge")
|
|
343
|
+
} catch (e: Exception) {
|
|
344
|
+
Log.e(TAG, "Failed to initialize PluginBridge: ${e.message}")
|
|
345
|
+
}
|
|
346
|
+
|
|
331
347
|
setupSafeAreaHandling()
|
|
332
348
|
|
|
333
349
|
// Setup back press handler (modern API)
|
|
@@ -421,6 +437,13 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
421
437
|
customWebView.onResume()
|
|
422
438
|
}
|
|
423
439
|
|
|
440
|
+
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
|
441
|
+
if (::nativeBridge.isInitialized) {
|
|
442
|
+
nativeBridge.getCameraManager()?.onTouchEvent(ev)
|
|
443
|
+
}
|
|
444
|
+
return super.dispatchTouchEvent(ev)
|
|
445
|
+
}
|
|
446
|
+
|
|
424
447
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
425
448
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
426
449
|
if (::nativeBridge.isInitialized) {
|
|
@@ -445,11 +468,16 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
|
|
|
445
468
|
if (::keyboardUtil.isInitialized) {
|
|
446
469
|
keyboardUtil.cleanup()
|
|
447
470
|
}
|
|
448
|
-
if (::
|
|
449
|
-
|
|
471
|
+
if (::customWebView.isInitialized) {
|
|
472
|
+
if (::pluginBridge.isInitialized) {
|
|
473
|
+
customWebView.removeJavascriptInterface("PluginBridge")
|
|
474
|
+
}
|
|
475
|
+
if (::nativeBridge.isInitialized) {
|
|
476
|
+
customWebView.removeJavascriptInterface("NativeBridge")
|
|
477
|
+
}
|
|
478
|
+
customWebView.destroy()
|
|
450
479
|
}
|
|
451
480
|
coroutineContext.cancelChildren()
|
|
452
|
-
customWebView.destroy()
|
|
453
481
|
super.onDestroy()
|
|
454
482
|
}
|
|
455
483
|
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt
CHANGED
|
@@ -151,9 +151,16 @@ class NativeBridge(
|
|
|
151
151
|
|
|
152
152
|
private var networkMonitor: NetworkMonitor? = null
|
|
153
153
|
|
|
154
|
+
// Video stream manager
|
|
155
|
+
private var nativeCameraManager: NativeCameraManager? = null
|
|
156
|
+
private var webViewOriginalBackground: Int = android.graphics.Color.WHITE
|
|
157
|
+
|
|
154
158
|
// Unified notification manager
|
|
155
159
|
private val notificationManager = AppNotificationManager(mainActivity, mainActivity.properties)
|
|
156
160
|
|
|
161
|
+
// Transition manager — snapshot overlay pattern for native page transitions
|
|
162
|
+
private val transitionManager = TransitionManager(mainActivity, webView)
|
|
163
|
+
|
|
157
164
|
companion object {
|
|
158
165
|
private const val TAG = "NativeBridge"
|
|
159
166
|
private const val GOOGLE_SIGN_IN_SAFE_EXECUTE_ACTION = "start Google Sign-In"
|
|
@@ -239,6 +246,90 @@ class NativeBridge(
|
|
|
239
246
|
}
|
|
240
247
|
}
|
|
241
248
|
|
|
249
|
+
fun setCameraManager(manager: NativeCameraManager) {
|
|
250
|
+
nativeCameraManager = manager
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fun getCameraManager(): NativeCameraManager? = nativeCameraManager
|
|
254
|
+
|
|
255
|
+
@JavascriptInterface
|
|
256
|
+
fun startVideoStream(optionsRaw: String?) {
|
|
257
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "start video stream") {
|
|
258
|
+
android.util.Log.d("NativeBridge", "startVideoStream raw options: $optionsRaw")
|
|
259
|
+
val options = try { JSONObject(optionsRaw ?: "{}") } catch (e: Exception) { JSONObject() }
|
|
260
|
+
val facing = options.optString("facing", "back")
|
|
261
|
+
val viewfinderRect = options.optJSONObject("viewfinderRect")
|
|
262
|
+
val zoomOptions = options.optJSONObject("zoom")
|
|
263
|
+
val scanFormat = options.optString("format", "all")
|
|
264
|
+
val showQrDetected = options.optBoolean("showQrDetected", false)
|
|
265
|
+
android.util.Log.d("NativeBridge", "startVideoStream parsed: facing=$facing zoom=$zoomOptions scanFormat=$scanFormat viewfinderRect=$viewfinderRect showQrDetected=$showQrDetected")
|
|
266
|
+
mainActivity.runOnUiThread {
|
|
267
|
+
webViewOriginalBackground = webView.solidColor.takeIf { it != 0 } ?: android.graphics.Color.WHITE
|
|
268
|
+
webView.setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
|
269
|
+
nativeCameraManager?.start(facing, viewfinderRect, zoomOptions, scanFormat, showQrDetected = showQrDetected)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@JavascriptInterface
|
|
275
|
+
fun stopVideoStream(optionsRaw: String?) {
|
|
276
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "stop video stream") {
|
|
277
|
+
mainActivity.runOnUiThread {
|
|
278
|
+
webView.setBackgroundColor(webViewOriginalBackground)
|
|
279
|
+
nativeCameraManager?.stop()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@JavascriptInterface
|
|
285
|
+
fun setVideoStreamZoom(multiplierRaw: String?) {
|
|
286
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "set video stream zoom") {
|
|
287
|
+
val multiplier = multiplierRaw?.trim()?.toFloatOrNull() ?: run {
|
|
288
|
+
android.util.Log.w("NativeBridge", "setVideoStreamZoom — invalid value: $multiplierRaw, ignoring")
|
|
289
|
+
return@safeExecute
|
|
290
|
+
}
|
|
291
|
+
android.util.Log.d("NativeBridge", "setVideoStreamZoom — multiplier=${multiplier}x")
|
|
292
|
+
mainActivity.runOnUiThread {
|
|
293
|
+
nativeCameraManager?.setZoom(multiplier)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@JavascriptInterface
|
|
299
|
+
fun setVideoStreamTorch(optionsRaw: String?) {
|
|
300
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "set video stream torch") {
|
|
301
|
+
val options = try { JSONObject(optionsRaw ?: "{}") } catch (e: Exception) { JSONObject() }
|
|
302
|
+
val on = options.optBoolean("on", false)
|
|
303
|
+
android.util.Log.d("NativeBridge", "setVideoStreamTorch — on=$on")
|
|
304
|
+
mainActivity.runOnUiThread {
|
|
305
|
+
nativeCameraManager?.setTorch(on)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
@JavascriptInterface
|
|
311
|
+
fun setVideoStreamFps(optionsRaw: String?) {
|
|
312
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "set video stream fps") {
|
|
313
|
+
val options = try { JSONObject(optionsRaw ?: "{}") } catch (e: Exception) { JSONObject() }
|
|
314
|
+
val min = if (options.isNull("min")) null else options.optInt("min")
|
|
315
|
+
val max = if (options.isNull("max")) null else options.optInt("max")
|
|
316
|
+
android.util.Log.d("NativeBridge", "setVideoStreamFps — min=$min max=$max")
|
|
317
|
+
mainActivity.runOnUiThread {
|
|
318
|
+
nativeCameraManager?.setFps(min, max)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
@JavascriptInterface
|
|
324
|
+
fun flipVideoStream(optionsRaw: String?) {
|
|
325
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "flip video stream") {
|
|
326
|
+
android.util.Log.d("NativeBridge", "flipVideoStream called")
|
|
327
|
+
mainActivity.runOnUiThread {
|
|
328
|
+
nativeCameraManager?.flip()
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
242
333
|
@JavascriptInterface
|
|
243
334
|
fun openCamera(options: String?) {
|
|
244
335
|
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_CAMERA_ERROR, "open camera") {
|
|
@@ -1023,6 +1114,17 @@ class NativeBridge(
|
|
|
1023
1114
|
* Handle permission request results
|
|
1024
1115
|
*/
|
|
1025
1116
|
fun handlePermissionResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
1117
|
+
if (requestCode == NativeCameraManager.PERMISSION_REQUEST_CODE) {
|
|
1118
|
+
val granted = grantResults.isNotEmpty() &&
|
|
1119
|
+
grantResults[0] == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
1120
|
+
val cam = nativeCameraManager
|
|
1121
|
+
if (cam == null) {
|
|
1122
|
+
Log.w(TAG, "handlePermissionResult — nativeCameraManager is null, ignoring camera permission result")
|
|
1123
|
+
return
|
|
1124
|
+
}
|
|
1125
|
+
cam.onPermissionResult(granted)
|
|
1126
|
+
return
|
|
1127
|
+
}
|
|
1026
1128
|
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.NOTIFICATION_PERMISSION_STATUS, "handle permission result") {
|
|
1027
1129
|
// Delegate to notification manager which handles notification permissions
|
|
1028
1130
|
notificationManager.getNotificationUtils().handlePermissionResult(requestCode, permissions, grantResults)
|
|
@@ -1133,6 +1235,45 @@ class NativeBridge(
|
|
|
1133
1235
|
}
|
|
1134
1236
|
}
|
|
1135
1237
|
|
|
1238
|
+
@JavascriptInterface
|
|
1239
|
+
fun startTransition(optionsRaw: String?) {
|
|
1240
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_TRANSITION_CANCELLED, "start transition") {
|
|
1241
|
+
val options = try {
|
|
1242
|
+
JSONObject(optionsRaw ?: "{}")
|
|
1243
|
+
} catch (e: Exception) {
|
|
1244
|
+
Log.w(TAG, "startTransition — malformed JSON, using defaults: $optionsRaw")
|
|
1245
|
+
JSONObject()
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
val type = options.optString("type", "slide")
|
|
1249
|
+
val direction = options.optString("direction", "left")
|
|
1250
|
+
val duration = options.optInt("duration", 300).coerceIn(0, 2000)
|
|
1251
|
+
val timeout = options.optInt("timeout", maxOf(duration * 3, 800))
|
|
1252
|
+
|
|
1253
|
+
mainActivity.runOnUiThread {
|
|
1254
|
+
transitionManager.startTransition(type, direction, duration, timeout)
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
@JavascriptInterface
|
|
1260
|
+
fun commitTransition(optionsRaw: String?) {
|
|
1261
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_TRANSITION_CANCELLED, "commit transition") {
|
|
1262
|
+
mainActivity.runOnUiThread {
|
|
1263
|
+
transitionManager.commitTransition()
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
@JavascriptInterface
|
|
1269
|
+
fun cancelTransition(optionsRaw: String?) {
|
|
1270
|
+
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_TRANSITION_CANCELLED, "cancel transition") {
|
|
1271
|
+
mainActivity.runOnUiThread {
|
|
1272
|
+
transitionManager.cancelTransition()
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1136
1277
|
@JavascriptInterface
|
|
1137
1278
|
fun getSafeArea(data: String? = null) {
|
|
1138
1279
|
BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_SAFE_AREA_INSETS_UPDATED, "get safe area") {
|
|
@@ -1164,6 +1305,13 @@ class NativeBridge(
|
|
|
1164
1305
|
// Cleanup NotificationManager
|
|
1165
1306
|
notificationManager.cleanup()
|
|
1166
1307
|
|
|
1308
|
+
// Cancel any in-flight transition and release overlay
|
|
1309
|
+
transitionManager.cleanup()
|
|
1310
|
+
|
|
1311
|
+
// Stop video stream
|
|
1312
|
+
nativeCameraManager?.cleanup()
|
|
1313
|
+
nativeCameraManager = null
|
|
1314
|
+
|
|
1167
1315
|
// Stop network monitoring
|
|
1168
1316
|
networkMonitor?.stop()
|
|
1169
1317
|
networkMonitor = null
|