@thelacanians/vue-native-cli 0.4.2 → 0.4.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/dist/cli.js +43 -23
- package/native/android/README.md +205 -0
- package/native/android/VueNativeCore/build.gradle.kts +100 -0
- package/native/android/VueNativeCore/consumer-rules.pro +12 -0
- package/native/android/VueNativeCore/proguard-rules.pro +33 -0
- package/native/android/VueNativeCore/src/main/AndroidManifest.xml +17 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/ErrorOverlayView.kt +94 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/HotReloadManager.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +652 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +207 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +417 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +76 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +78 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +46 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +84 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VCheckboxFactory.kt +93 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VDropdownFactory.kt +125 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +210 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +31 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +183 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +57 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +109 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VProgressBarFactory.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRadioFactory.kt +103 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRefreshControlFactory.kt +73 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSafeAreaFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +144 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +77 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSliderFactory.kt +74 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +52 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +62 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VTextFactory.kt +53 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VVideoFactory.kt +191 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +48 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +90 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +40 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/VTextNodeView.kt +23 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +16 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/TouchableView.kt +105 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +292 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AppStateModule.kt +41 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +59 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AudioModule.kt +331 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +166 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +56 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +198 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CameraModule.kt +64 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +288 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DatabaseModule.kt +229 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +39 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +193 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +68 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +61 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +111 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +302 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +26 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModule.kt +27 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +92 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +75 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +181 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +255 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +147 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +126 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +51 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +134 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +36 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +160 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +155 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +802 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +43 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +169 -0
- package/native/android/VueNativeCore/src/main/res/values/ids.xml +8 -0
- package/native/android/app/build.gradle.kts +45 -0
- package/native/android/app/proguard-rules.pro +5 -0
- package/native/android/app/src/main/AndroidManifest.xml +25 -0
- package/native/android/app/src/main/assets/.gitkeep +0 -0
- package/native/android/app/src/main/kotlin/com/vuenative/example/counter/MainActivity.kt +14 -0
- package/native/android/app/src/main/res/layout/activity_main.xml +6 -0
- package/native/android/app/src/main/res/values/strings.xml +3 -0
- package/native/android/app/src/main/res/values/themes.xml +9 -0
- package/native/android/app/src/main/res/xml/network_security_config.xml +8 -0
- package/native/android/build.gradle.kts +6 -0
- package/native/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/native/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/native/android/gradle.properties +4 -0
- package/native/android/gradlew +87 -0
- package/native/android/gradlew.bat +48 -0
- package/native/android/settings.gradle.kts +20 -0
- package/native/ios/VueNativeCore/Package.resolved +23 -0
- package/native/ios/VueNativeCore/Package.swift +32 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/CertificatePinning.swift +132 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/ErrorOverlayView.swift +92 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/HotReloadManager.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSPolyfills.swift +711 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/JSRuntime.swift +421 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +891 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/VueNativeViewController.swift +88 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/ComponentRegistry.swift +193 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActionSheetFactory.swift +91 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VActivityIndicatorFactory.swift +74 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VAlertDialogFactory.swift +150 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VButtonFactory.swift +93 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VCheckboxFactory.swift +114 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VDropdownFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VImageFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VInputFactory.swift +357 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VKeyboardAvoidingFactory.swift +99 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VListFactory.swift +250 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VModalFactory.swift +112 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPickerFactory.swift +96 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VPressableFactory.swift +168 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VProgressBarFactory.swift +39 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRadioFactory.swift +167 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VRefreshControlFactory.swift +153 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSafeAreaFactory.swift +56 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VScrollViewFactory.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSectionListFactory.swift +248 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSegmentedControlFactory.swift +73 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSliderFactory.swift +63 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VStatusBarFactory.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VSwitchFactory.swift +108 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +290 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VVideoFactory.swift +246 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +157 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VWebViewFactory.swift +172 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/NativeComponentFactory.swift +53 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +107 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/TouchableView.swift +136 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/UIColor+Hex.swift +80 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AnimationModule.swift +291 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AppStateModule.swift +65 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AsyncStorageModule.swift +68 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/AudioModule.swift +366 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BackgroundTaskModule.swift +135 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BiometryModule.swift +61 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/BluetoothModule.swift +387 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CalendarModule.swift +161 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/CameraModule.swift +318 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ClipboardModule.swift +33 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ContactsModule.swift +173 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DatabaseModule.swift +259 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/DeviceInfoModule.swift +34 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/FileSystemModule.swift +233 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeolocationModule.swift +147 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/HapticsModule.swift +50 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/IAPModule.swift +194 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/KeyboardModule.swift +31 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/LinkingModule.swift +42 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModule.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +78 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NetworkModule.swift +62 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NotificationsModule.swift +215 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/OTAModule.swift +281 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PerformanceModule.swift +138 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/PermissionsModule.swift +190 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SecureStorageModule.swift +118 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SensorsModule.swift +103 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/ShareModule.swift +49 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/SocialAuthModule.swift +240 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/WebSocketModule.swift +213 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Resources/vue-native-placeholder.js +8 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Styling/StyleEngine.swift +885 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSRuntimeTests.swift +362 -0
- package/package.json +3 -2
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.HandlerThread
|
|
6
|
+
import android.os.Looper
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import com.eclipsesource.v8.V8
|
|
9
|
+
import com.eclipsesource.v8.JavaVoidCallback
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Core JavaScript runtime. Wraps J2V8's V8 engine on a dedicated HandlerThread.
|
|
13
|
+
*
|
|
14
|
+
* Thread safety contract:
|
|
15
|
+
* - All V8 access happens exclusively on jsThread via jsHandler
|
|
16
|
+
* - Never pass J2V8 objects (V8Array, V8Object) across threads
|
|
17
|
+
* - All View operations happen on main thread via mainHandler
|
|
18
|
+
*/
|
|
19
|
+
class JSRuntime(private val context: Context) {
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
private const val TAG = "VueNative-JSRuntime"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Dedicated JS thread. All V8 operations run here. */
|
|
26
|
+
private val jsThread = HandlerThread("VueNative-JS").apply { start() }
|
|
27
|
+
|
|
28
|
+
/** Handler for posting work to the JS thread. */
|
|
29
|
+
val jsHandler = Handler(jsThread.looper)
|
|
30
|
+
|
|
31
|
+
/** Handler for posting work to the main (UI) thread. */
|
|
32
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
33
|
+
|
|
34
|
+
/** V8 runtime instance. Only access from jsThread. */
|
|
35
|
+
private var v8: V8? = null
|
|
36
|
+
|
|
37
|
+
/** Whether the runtime has been initialized. */
|
|
38
|
+
private var isInitialized = false
|
|
39
|
+
|
|
40
|
+
/** The NativeBridge instance — manages view registry and operations. */
|
|
41
|
+
val bridge = NativeBridge(context)
|
|
42
|
+
|
|
43
|
+
/** Startup time in milliseconds for performance.now(). */
|
|
44
|
+
val startTimeMs: Long = System.currentTimeMillis()
|
|
45
|
+
|
|
46
|
+
init {
|
|
47
|
+
// Wire bridge callbacks
|
|
48
|
+
bridge.onFireEvent = { nodeId, eventName, payloadJson ->
|
|
49
|
+
// Called on main thread — dispatch to JS
|
|
50
|
+
jsHandler.post {
|
|
51
|
+
try {
|
|
52
|
+
if (nodeId == -1 && eventName == "__callback__") {
|
|
53
|
+
// Native module async callback — route to __VN_resolveCallback
|
|
54
|
+
v8?.executeVoidScript(
|
|
55
|
+
"if(typeof __VN_resolveCallback==='function'){" +
|
|
56
|
+
"var _d=$payloadJson;" +
|
|
57
|
+
"__VN_resolveCallback(_d.callbackId,_d.result,_d.error);}"
|
|
58
|
+
)
|
|
59
|
+
} else {
|
|
60
|
+
v8?.executeVoidScript(
|
|
61
|
+
"if(typeof __VN_handleEvent==='function')" +
|
|
62
|
+
"__VN_handleEvent($nodeId,${encodeJs(eventName)},$payloadJson)"
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
} catch (e: Exception) {
|
|
66
|
+
Log.e(TAG, "Error dispatching event $eventName to JS", e)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
bridge.onDispatchGlobalEvent = { eventName, payloadJson ->
|
|
71
|
+
jsHandler.post {
|
|
72
|
+
try {
|
|
73
|
+
v8?.executeVoidScript(
|
|
74
|
+
"if(typeof __VN_handleGlobalEvent==='function')" +
|
|
75
|
+
"__VN_handleGlobalEvent(${encodeJs(eventName)},${encodeJs(payloadJson)})"
|
|
76
|
+
)
|
|
77
|
+
} catch (e: Exception) {
|
|
78
|
+
Log.e(TAG, "Error dispatching global event $eventName", e)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Initialize the V8 runtime on the JS thread. Calls completion when ready. */
|
|
85
|
+
fun initialize(completion: (() -> Unit)? = null) {
|
|
86
|
+
jsHandler.post {
|
|
87
|
+
if (isInitialized) {
|
|
88
|
+
mainHandler.post { completion?.invoke() }
|
|
89
|
+
return@post
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
v8 = V8.createV8Runtime()
|
|
93
|
+
JSPolyfills.register(this)
|
|
94
|
+
// Register __VN_flushOperations — JS calls this to send batched ops to native
|
|
95
|
+
v8?.registerJavaMethod(JavaVoidCallback { _, params ->
|
|
96
|
+
try {
|
|
97
|
+
val json = if (params.length() > 0) params.getString(0) else "[]"
|
|
98
|
+
bridge.processOperations(json)
|
|
99
|
+
} finally {
|
|
100
|
+
params.close()
|
|
101
|
+
}
|
|
102
|
+
}, "__VN_flushOperations")
|
|
103
|
+
|
|
104
|
+
// Register __VN_handleError — JS calls this to report errors to native
|
|
105
|
+
v8?.registerJavaMethod(JavaVoidCallback { _, params ->
|
|
106
|
+
try {
|
|
107
|
+
val errorJson = if (params.length() > 0) params.getString(0) else "{}"
|
|
108
|
+
Log.e(TAG, "[VueNative Error] $errorJson")
|
|
109
|
+
} finally {
|
|
110
|
+
params.close()
|
|
111
|
+
}
|
|
112
|
+
}, "__VN_handleError")
|
|
113
|
+
|
|
114
|
+
isInitialized = true
|
|
115
|
+
Log.d(TAG, "V8 runtime initialized")
|
|
116
|
+
mainHandler.post { completion?.invoke() }
|
|
117
|
+
} catch (e: Exception) {
|
|
118
|
+
Log.e(TAG, "Failed to initialize V8 runtime", e)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Load and execute a JavaScript bundle. Must be called after initialize().
|
|
124
|
+
* @param onComplete Optional callback invoked on main thread: (success, errorMessage). */
|
|
125
|
+
fun loadBundle(bundleCode: String, onComplete: ((Boolean, String?) -> Unit)? = null) {
|
|
126
|
+
jsHandler.post {
|
|
127
|
+
try {
|
|
128
|
+
v8?.executeVoidScript(bundleCode)
|
|
129
|
+
Log.d(TAG, "Bundle loaded successfully")
|
|
130
|
+
mainHandler.post { onComplete?.invoke(true, null) }
|
|
131
|
+
} catch (e: Exception) {
|
|
132
|
+
Log.e(TAG, "Error loading bundle", e)
|
|
133
|
+
val msg = e.message ?: "Unknown error"
|
|
134
|
+
mainHandler.post {
|
|
135
|
+
ErrorOverlayView.show(context, msg)
|
|
136
|
+
onComplete?.invoke(false, msg)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Post work to the JS thread. */
|
|
143
|
+
fun runOnJsThread(block: () -> Unit) {
|
|
144
|
+
jsHandler.post(block)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Post work to the main (UI) thread. */
|
|
148
|
+
fun runOnMainThread(block: () -> Unit) {
|
|
149
|
+
mainHandler.post(block)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Execute a void script on the JS thread. Safe to call from any thread. */
|
|
153
|
+
fun executeVoidScript(script: String) {
|
|
154
|
+
jsHandler.post {
|
|
155
|
+
try {
|
|
156
|
+
v8?.executeVoidScript(script)
|
|
157
|
+
} catch (e: Exception) {
|
|
158
|
+
Log.e(TAG, "Script error: ${e.message}")
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Register a void Java method as a global JS function. Call from JS thread only. */
|
|
164
|
+
fun registerVoidCallback(name: String, callback: (Array<Any?>) -> Unit) {
|
|
165
|
+
jsHandler.post {
|
|
166
|
+
v8?.registerJavaMethod(JavaVoidCallback { _, params ->
|
|
167
|
+
try {
|
|
168
|
+
val args = Array<Any?>(params.length()) { i ->
|
|
169
|
+
when {
|
|
170
|
+
params.getType(i) == 1 -> params.getInteger(i) // INT
|
|
171
|
+
params.getType(i) == 2 -> params.getDouble(i) // DOUBLE
|
|
172
|
+
params.getType(i) == 3 -> params.getBoolean(i) // BOOLEAN
|
|
173
|
+
params.getType(i) == 4 -> params.getString(i) // STRING
|
|
174
|
+
else -> null
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
callback(args)
|
|
178
|
+
} finally {
|
|
179
|
+
params.close()
|
|
180
|
+
}
|
|
181
|
+
}, name)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Access raw V8 instance — only call from JS thread! */
|
|
186
|
+
fun withV8(block: (V8) -> Unit) {
|
|
187
|
+
jsHandler.post {
|
|
188
|
+
v8?.let(block)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Get raw V8 — ONLY call from JS thread. Returns null if not initialized. */
|
|
193
|
+
fun v8(): V8? = v8
|
|
194
|
+
|
|
195
|
+
/** Release the V8 runtime. */
|
|
196
|
+
fun release() {
|
|
197
|
+
jsHandler.post {
|
|
198
|
+
v8?.release(true)
|
|
199
|
+
v8 = null
|
|
200
|
+
isInitialized = false
|
|
201
|
+
}
|
|
202
|
+
jsThread.quitSafely()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private fun encodeJs(value: String): String =
|
|
206
|
+
"\"${value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r")}\""
|
|
207
|
+
}
|
package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt
ADDED
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.Looper
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import android.view.View
|
|
8
|
+
import android.view.ViewGroup
|
|
9
|
+
import org.json.JSONArray
|
|
10
|
+
import org.json.JSONObject
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Receives batched JS operations, dispatches them to the native view system,
|
|
14
|
+
* and provides event firing back to JS.
|
|
15
|
+
*/
|
|
16
|
+
class NativeBridge(private val context: Context) {
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
private const val TAG = "VueNative-Bridge"
|
|
20
|
+
|
|
21
|
+
/** Operations that mutate the view tree and require a layout pass. */
|
|
22
|
+
private val treeMutationOps = setOf(
|
|
23
|
+
"create", "createText", "appendChild", "insertBefore", "removeChild",
|
|
24
|
+
"setRootView", "setText", "setElementText"
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
29
|
+
|
|
30
|
+
// -- View registry --
|
|
31
|
+
/** Maps node IDs to native Views. Accessed only on main thread. */
|
|
32
|
+
val nodeViews = mutableMapOf<Int, View>()
|
|
33
|
+
|
|
34
|
+
/** Maps node IDs to component type strings. Accessed only on main thread. */
|
|
35
|
+
val nodeTypes = mutableMapOf<Int, String>()
|
|
36
|
+
|
|
37
|
+
/** Maps "nodeId:eventName" to handler. Accessed only on main thread. */
|
|
38
|
+
val eventHandlers = mutableMapOf<String, (Any?) -> Unit>()
|
|
39
|
+
|
|
40
|
+
/** Reverse index: maps nodeId to the set of event keys registered for that node.
|
|
41
|
+
* Enables O(k) cleanup where k = handlers per node, instead of O(n) full scan. */
|
|
42
|
+
private val eventKeysPerNode = mutableMapOf<Int, MutableSet<String>>()
|
|
43
|
+
|
|
44
|
+
/** Maps child nodeId to parent nodeId. Accessed only on main thread. */
|
|
45
|
+
val nodeParents = mutableMapOf<Int, Int>()
|
|
46
|
+
|
|
47
|
+
/** Reverse index: maps parent nodeId to ordered list of child nodeIds. O(1) children lookup. */
|
|
48
|
+
val nodeChildren = mutableMapOf<Int, MutableList<Int>>()
|
|
49
|
+
|
|
50
|
+
/** The root view of the tree — set when __ROOT__ node is made the root view. */
|
|
51
|
+
var rootView: View? = null
|
|
52
|
+
|
|
53
|
+
/** The container view from the host Activity — where the root view is attached. */
|
|
54
|
+
var hostContainer: ViewGroup? = null
|
|
55
|
+
|
|
56
|
+
/** Called when native fires an event to JS (nodeId, eventName, payloadJson). */
|
|
57
|
+
var onFireEvent: ((nodeId: Int, eventName: String, payloadJson: String) -> Unit)? = null
|
|
58
|
+
|
|
59
|
+
/** Called when a module dispatches a global event to JS (eventName, payloadJson). */
|
|
60
|
+
var onDispatchGlobalEvent: ((eventName: String, payloadJson: String) -> Unit)? = null
|
|
61
|
+
|
|
62
|
+
private val componentRegistry: ComponentRegistry by lazy { ComponentRegistry.getInstance(context) }
|
|
63
|
+
|
|
64
|
+
// -------------------------------------------------------------------------
|
|
65
|
+
// Operation processing — called from JS thread
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
/** Called from JS thread via __VN_flushOperations. Parses JSON and dispatches to main. */
|
|
69
|
+
fun processOperations(json: String) {
|
|
70
|
+
val operations: JSONArray = try {
|
|
71
|
+
JSONArray(json)
|
|
72
|
+
} catch (e: Exception) {
|
|
73
|
+
Log.e(TAG, "Failed to parse operations JSON: ${e.message}")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Collect ops before dispatching to main thread
|
|
78
|
+
val ops = ArrayList<JSONObject>(operations.length())
|
|
79
|
+
for (i in 0 until operations.length()) {
|
|
80
|
+
ops.add(operations.getJSONObject(i))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
mainHandler.post {
|
|
84
|
+
var hasMutations = false
|
|
85
|
+
for (op in ops) {
|
|
86
|
+
try {
|
|
87
|
+
val opName = op.optString("op")
|
|
88
|
+
if (!hasMutations && opName in treeMutationOps) {
|
|
89
|
+
hasMutations = true
|
|
90
|
+
}
|
|
91
|
+
handleOperation(op)
|
|
92
|
+
} catch (e: Exception) {
|
|
93
|
+
Log.e(TAG, "Error handling op '${op.optString("op")}': ${e.message}")
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Only trigger layout when the batch mutated the view tree
|
|
97
|
+
if (hasMutations) {
|
|
98
|
+
triggerLayout()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private fun handleOperation(op: JSONObject) {
|
|
104
|
+
val opName = op.getString("op")
|
|
105
|
+
val args = op.optJSONArray("args") ?: JSONArray()
|
|
106
|
+
|
|
107
|
+
when (opName) {
|
|
108
|
+
"create" -> handleCreate(args)
|
|
109
|
+
"createText" -> handleCreateText(args)
|
|
110
|
+
"setText" -> handleSetText(args)
|
|
111
|
+
"setElementText" -> handleSetElementText(args)
|
|
112
|
+
"updateProp" -> handleUpdateProp(args)
|
|
113
|
+
"updateStyle" -> handleUpdateStyle(args)
|
|
114
|
+
"appendChild" -> handleAppendChild(args)
|
|
115
|
+
"insertBefore" -> handleInsertBefore(args)
|
|
116
|
+
"removeChild" -> handleRemoveChild(args)
|
|
117
|
+
"setRootView" -> handleSetRootView(args)
|
|
118
|
+
"addEventListener" -> handleAddEventListener(args)
|
|
119
|
+
"removeEventListener" -> handleRemoveEventListener(args)
|
|
120
|
+
"invokeNativeModule" -> handleInvokeNativeModule(args)
|
|
121
|
+
"invokeNativeModuleSync" -> handleInvokeNativeModuleSync(args)
|
|
122
|
+
else -> Log.w(TAG, "Unknown operation: $opName")
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private fun handleCreate(args: JSONArray) {
|
|
127
|
+
val nodeId = args.getInt(0)
|
|
128
|
+
val type = args.getString(1)
|
|
129
|
+
val view = componentRegistry.createView(type) ?: return
|
|
130
|
+
nodeViews[nodeId] = view
|
|
131
|
+
nodeTypes[nodeId] = type
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private fun handleCreateText(args: JSONArray) {
|
|
135
|
+
// Text nodes — create a special "text node" view
|
|
136
|
+
val nodeId = args.getInt(0)
|
|
137
|
+
val text = args.getString(1)
|
|
138
|
+
val textView = VTextNodeView(context).apply { setText(text) }
|
|
139
|
+
nodeViews[nodeId] = textView
|
|
140
|
+
nodeTypes[nodeId] = "__TEXT__"
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private fun handleSetText(args: JSONArray) {
|
|
144
|
+
val nodeId = args.getInt(0)
|
|
145
|
+
val text = args.getString(1)
|
|
146
|
+
val view = nodeViews[nodeId] ?: return
|
|
147
|
+
(view as? VTextNodeView)?.setText(text)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private fun handleSetElementText(args: JSONArray) {
|
|
151
|
+
val nodeId = args.getInt(0)
|
|
152
|
+
val text = args.getString(1)
|
|
153
|
+
val view = nodeViews[nodeId] ?: return
|
|
154
|
+
// Delegate to the factory
|
|
155
|
+
val factory = componentRegistry.factoryForType(nodeTypes[nodeId] ?: return) ?: return
|
|
156
|
+
factory.updateProp(view, "text", text)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private fun handleUpdateProp(args: JSONArray) {
|
|
160
|
+
val nodeId = args.getInt(0)
|
|
161
|
+
val key = args.getString(1)
|
|
162
|
+
val value = if (args.isNull(2)) null else args.get(2)
|
|
163
|
+
val view = nodeViews[nodeId] ?: return
|
|
164
|
+
val factory = componentRegistry.factoryForView(view) ?: return
|
|
165
|
+
factory.updateProp(view, key, value)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private fun handleUpdateStyle(args: JSONArray) {
|
|
169
|
+
val nodeId = args.getInt(0)
|
|
170
|
+
val styleObj = args.optJSONObject(1) ?: return
|
|
171
|
+
val view = nodeViews[nodeId] ?: return
|
|
172
|
+
val factory = componentRegistry.factoryForView(view) ?: return
|
|
173
|
+
val iter = styleObj.keys()
|
|
174
|
+
while (iter.hasNext()) {
|
|
175
|
+
val key = iter.next()
|
|
176
|
+
val value = if (styleObj.isNull(key)) null else styleObj.get(key)
|
|
177
|
+
factory.updateProp(view, key, value)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private fun handleAppendChild(args: JSONArray) {
|
|
182
|
+
val parentId = args.getInt(0)
|
|
183
|
+
val childId = args.getInt(1)
|
|
184
|
+
val parent = nodeViews[parentId] ?: return
|
|
185
|
+
val child = nodeViews[childId] ?: return
|
|
186
|
+
nodeParents[childId] = parentId
|
|
187
|
+
nodeChildren.getOrPut(parentId) { mutableListOf() }.add(childId)
|
|
188
|
+
|
|
189
|
+
val factory = componentRegistry.factoryForView(parent)
|
|
190
|
+
if (factory != null) {
|
|
191
|
+
val idx = (parent as? ViewGroup)?.childCount ?: 0
|
|
192
|
+
factory.insertChild(parent, child, idx)
|
|
193
|
+
} else {
|
|
194
|
+
(parent as? ViewGroup)?.addView(child)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private fun handleInsertBefore(args: JSONArray) {
|
|
199
|
+
val parentId = args.getInt(0)
|
|
200
|
+
val childId = args.getInt(1)
|
|
201
|
+
val anchorId = args.getInt(2)
|
|
202
|
+
val parent = nodeViews[parentId] ?: return
|
|
203
|
+
val child = nodeViews[childId] ?: return
|
|
204
|
+
val anchor = nodeViews[anchorId] ?: return
|
|
205
|
+
nodeParents[childId] = parentId
|
|
206
|
+
// Insert into nodeChildren at the correct position (before anchorId)
|
|
207
|
+
val siblings = nodeChildren.getOrPut(parentId) { mutableListOf() }
|
|
208
|
+
val anchorIndex = siblings.indexOf(anchorId)
|
|
209
|
+
if (anchorIndex >= 0) {
|
|
210
|
+
siblings.add(anchorIndex, childId)
|
|
211
|
+
} else {
|
|
212
|
+
siblings.add(childId)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
val vg = parent as? ViewGroup ?: return
|
|
216
|
+
val anchorIdx = vg.indexOfChild(anchor)
|
|
217
|
+
val insertIdx = if (anchorIdx < 0) vg.childCount else anchorIdx
|
|
218
|
+
|
|
219
|
+
val factory = componentRegistry.factoryForView(parent)
|
|
220
|
+
if (factory != null) {
|
|
221
|
+
factory.insertChild(parent, child, insertIdx)
|
|
222
|
+
} else {
|
|
223
|
+
vg.addView(child, insertIdx)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private fun handleRemoveChild(args: JSONArray) {
|
|
228
|
+
val childId = args.getInt(0)
|
|
229
|
+
val child = nodeViews[childId] ?: return
|
|
230
|
+
val parentId = nodeParents[childId]
|
|
231
|
+
val parent = parentId?.let { nodeViews[it] }
|
|
232
|
+
|
|
233
|
+
if (parent != null) {
|
|
234
|
+
val factory = componentRegistry.factoryForView(parent)
|
|
235
|
+
if (factory != null) {
|
|
236
|
+
factory.removeChild(parent, child)
|
|
237
|
+
} else {
|
|
238
|
+
(parent as? ViewGroup)?.removeView(child)
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
(child.parent as? ViewGroup)?.removeView(child)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Remove child from parent's nodeChildren list
|
|
245
|
+
if (parentId != null) {
|
|
246
|
+
nodeChildren[parentId]?.remove(childId)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Recursively clean up descendants
|
|
250
|
+
cleanupNode(childId)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private fun cleanupNode(nodeId: Int) {
|
|
254
|
+
// Recursively clean up children using the reverse index (O(subtree) not O(n))
|
|
255
|
+
nodeChildren[nodeId]?.toList()?.forEach { cleanupNode(it) }
|
|
256
|
+
nodeParents.remove(nodeId)
|
|
257
|
+
// O(k) event cleanup via reverse index instead of O(n) full scan
|
|
258
|
+
eventKeysPerNode.remove(nodeId)?.forEach { key ->
|
|
259
|
+
eventHandlers.remove(key)
|
|
260
|
+
}
|
|
261
|
+
// Call destroyView on the factory to clean up factory-level state (e.g. VListFactory maps)
|
|
262
|
+
val view = nodeViews[nodeId]
|
|
263
|
+
if (view != null) {
|
|
264
|
+
val factory = componentRegistry.factoryForView(view)
|
|
265
|
+
factory?.destroyView(view)
|
|
266
|
+
}
|
|
267
|
+
nodeViews.remove(nodeId)
|
|
268
|
+
nodeTypes.remove(nodeId)
|
|
269
|
+
nodeChildren.remove(nodeId)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private fun handleSetRootView(args: JSONArray) {
|
|
273
|
+
val nodeId = args.getInt(0)
|
|
274
|
+
val view = nodeViews[nodeId] ?: return
|
|
275
|
+
rootView = view
|
|
276
|
+
|
|
277
|
+
val container = hostContainer ?: return
|
|
278
|
+
container.removeAllViews()
|
|
279
|
+
container.addView(
|
|
280
|
+
view,
|
|
281
|
+
ViewGroup.LayoutParams(
|
|
282
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
283
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private fun handleAddEventListener(args: JSONArray) {
|
|
289
|
+
val nodeId = args.getInt(0)
|
|
290
|
+
val eventName = args.getString(1)
|
|
291
|
+
val view = nodeViews[nodeId] ?: return
|
|
292
|
+
val factory = componentRegistry.factoryForView(view) ?: return
|
|
293
|
+
|
|
294
|
+
val key = "$nodeId:$eventName"
|
|
295
|
+
|
|
296
|
+
// Remove old handler first to prevent duplicate event firing (e.g. recycled node IDs)
|
|
297
|
+
if (eventHandlers.containsKey(key)) {
|
|
298
|
+
factory.removeEventListener(view, eventName)
|
|
299
|
+
eventHandlers.remove(key)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
val handler: (Any?) -> Unit = { payload ->
|
|
303
|
+
val payloadJson = when (payload) {
|
|
304
|
+
null -> "null"
|
|
305
|
+
is Map<*, *> -> JSONObject(payload).toString()
|
|
306
|
+
is String -> JSONObject.quote(payload)
|
|
307
|
+
else -> payload.toString()
|
|
308
|
+
}
|
|
309
|
+
onFireEvent?.invoke(nodeId, eventName, payloadJson)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
factory.addEventListener(view, eventName, handler)
|
|
313
|
+
eventHandlers[key] = handler
|
|
314
|
+
eventKeysPerNode.getOrPut(nodeId) { mutableSetOf() }.add(key)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
private fun handleRemoveEventListener(args: JSONArray) {
|
|
318
|
+
val nodeId = args.getInt(0)
|
|
319
|
+
val eventName = args.getString(1)
|
|
320
|
+
val view = nodeViews[nodeId] ?: return
|
|
321
|
+
val factory = componentRegistry.factoryForView(view) ?: return
|
|
322
|
+
factory.removeEventListener(view, eventName)
|
|
323
|
+
|
|
324
|
+
val key = "$nodeId:$eventName"
|
|
325
|
+
eventHandlers.remove(key)
|
|
326
|
+
eventKeysPerNode[nodeId]?.remove(key)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private fun handleInvokeNativeModule(args: JSONArray) {
|
|
330
|
+
val moduleName = args.getString(0)
|
|
331
|
+
val methodName = args.getString(1)
|
|
332
|
+
val moduleArgs = buildArgsList(args.optJSONArray(2))
|
|
333
|
+
val callbackId = args.optInt(3, -1)
|
|
334
|
+
|
|
335
|
+
NativeModuleRegistry.getInstance(context).invoke(
|
|
336
|
+
moduleName, methodName, moduleArgs, this
|
|
337
|
+
) { result, error ->
|
|
338
|
+
if (callbackId >= 0) {
|
|
339
|
+
val resultJson = when (result) {
|
|
340
|
+
null -> "null"
|
|
341
|
+
is Map<*, *> -> JSONObject(result).toString()
|
|
342
|
+
is List<*> -> JSONArray(result).toString()
|
|
343
|
+
is String -> JSONObject.quote(result)
|
|
344
|
+
is Boolean -> result.toString()
|
|
345
|
+
is Number -> result.toString()
|
|
346
|
+
else -> result.toString()
|
|
347
|
+
}
|
|
348
|
+
val errorJson = if (error != null) JSONObject.quote(error) else "null"
|
|
349
|
+
resolveCallbackInJs(callbackId, resultJson, errorJson)
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private fun handleInvokeNativeModuleSync(args: JSONArray) {
|
|
355
|
+
val moduleName = args.getString(0)
|
|
356
|
+
val methodName = args.getString(1)
|
|
357
|
+
val moduleArgs = buildArgsList(args.optJSONArray(2))
|
|
358
|
+
try {
|
|
359
|
+
NativeModuleRegistry.getInstance(context).invokeSync(moduleName, methodName, moduleArgs, this)
|
|
360
|
+
} catch (e: Exception) {
|
|
361
|
+
Log.e(TAG, "Error in sync module invoke $moduleName.$methodName: ${e.message}")
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private fun resolveCallbackInJs(callbackId: Int, resultJson: String, errorJson: String) {
|
|
366
|
+
onFireEvent?.invoke(-1, "__callback__",
|
|
367
|
+
"{\"callbackId\":$callbackId,\"result\":$resultJson,\"error\":$errorJson}")
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private fun buildArgsList(arr: JSONArray?): List<Any?> {
|
|
371
|
+
arr ?: return emptyList()
|
|
372
|
+
return (0 until arr.length()).map { i ->
|
|
373
|
+
if (arr.isNull(i)) null else arr.get(i)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// -------------------------------------------------------------------------
|
|
378
|
+
// Registry management
|
|
379
|
+
// -------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
/** Clear all registries. Called during hot reload after JS teardown. Must run on main thread. */
|
|
382
|
+
fun clearAllRegistries() {
|
|
383
|
+
nodeViews.clear()
|
|
384
|
+
nodeTypes.clear()
|
|
385
|
+
eventHandlers.clear()
|
|
386
|
+
eventKeysPerNode.clear()
|
|
387
|
+
nodeParents.clear()
|
|
388
|
+
nodeChildren.clear()
|
|
389
|
+
rootView = null
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// -------------------------------------------------------------------------
|
|
393
|
+
// Layout
|
|
394
|
+
// -------------------------------------------------------------------------
|
|
395
|
+
|
|
396
|
+
fun triggerLayout() {
|
|
397
|
+
rootView?.let { root ->
|
|
398
|
+
root.requestLayout()
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// -------------------------------------------------------------------------
|
|
403
|
+
// Events: Native to JS
|
|
404
|
+
// -------------------------------------------------------------------------
|
|
405
|
+
|
|
406
|
+
/** Fire an event from a native view to the JS side. */
|
|
407
|
+
fun fireEvent(nodeId: Int, eventName: String, payload: Map<String, Any?>? = null) {
|
|
408
|
+
val payloadJson = if (payload != null) JSONObject(payload).toString() else "null"
|
|
409
|
+
onFireEvent?.invoke(nodeId, eventName, payloadJson)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** Dispatch a global push event to JS. Called from modules. */
|
|
413
|
+
fun dispatchGlobalEvent(eventName: String, payload: Map<String, Any> = emptyMap()) {
|
|
414
|
+
val payloadJson = JSONObject(payload).toString()
|
|
415
|
+
onDispatchGlobalEvent?.invoke(eventName, payloadJson)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.view.View
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Singleton registry mapping component type strings to factories.
|
|
8
|
+
* Also maps views to the factory that created them (via view tag).
|
|
9
|
+
*/
|
|
10
|
+
class ComponentRegistry private constructor(private val context: Context) {
|
|
11
|
+
|
|
12
|
+
companion object {
|
|
13
|
+
@Volatile private var instance: ComponentRegistry? = null
|
|
14
|
+
|
|
15
|
+
fun getInstance(context: Context): ComponentRegistry {
|
|
16
|
+
return instance ?: synchronized(this) {
|
|
17
|
+
instance ?: ComponentRegistry(context.applicationContext).also {
|
|
18
|
+
instance = it
|
|
19
|
+
it.registerDefaults()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private val factories = mutableMapOf<String, NativeComponentFactory>()
|
|
26
|
+
|
|
27
|
+
private fun registerDefaults() {
|
|
28
|
+
register("VView", VViewFactory())
|
|
29
|
+
register("VText", VTextFactory())
|
|
30
|
+
register("VButton", VButtonFactory())
|
|
31
|
+
register("VInput", VInputFactory())
|
|
32
|
+
register("VSwitch", VSwitchFactory())
|
|
33
|
+
register("VActivityIndicator", VActivityIndicatorFactory())
|
|
34
|
+
register("VScrollView", VScrollViewFactory())
|
|
35
|
+
register("VImage", VImageFactory())
|
|
36
|
+
register("VKeyboardAvoiding", VKeyboardAvoidingFactory())
|
|
37
|
+
register("VSafeArea", VSafeAreaFactory())
|
|
38
|
+
register("VSlider", VSliderFactory())
|
|
39
|
+
register("VList", VListFactory())
|
|
40
|
+
register("VModal", VModalFactory())
|
|
41
|
+
register("VAlertDialog", VAlertDialogFactory())
|
|
42
|
+
register("VStatusBar", VStatusBarFactory())
|
|
43
|
+
register("VWebView", VWebViewFactory())
|
|
44
|
+
register("VProgressBar", VProgressBarFactory())
|
|
45
|
+
register("VPicker", VPickerFactory())
|
|
46
|
+
register("VSegmentedControl", VSegmentedControlFactory())
|
|
47
|
+
register("VActionSheet", VActionSheetFactory())
|
|
48
|
+
register("VRefreshControl", VRefreshControlFactory())
|
|
49
|
+
register("VPressable", VPressableFactory())
|
|
50
|
+
register("VSectionList", VSectionListFactory())
|
|
51
|
+
register("VCheckbox", VCheckboxFactory())
|
|
52
|
+
register("VRadio", VRadioFactory())
|
|
53
|
+
register("VDropdown", VDropdownFactory())
|
|
54
|
+
register("VVideo", VVideoFactory())
|
|
55
|
+
register("__ROOT__", VRootFactory())
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fun register(type: String, factory: NativeComponentFactory) {
|
|
59
|
+
factories[type] = factory
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
fun createView(type: String): View? {
|
|
63
|
+
val factory = factories[type] ?: run {
|
|
64
|
+
android.util.Log.w("VueNative", "No factory for type: $type")
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
val view = factory.createView(context)
|
|
68
|
+
view.setTag(TAG_FACTORY, factory)
|
|
69
|
+
return view
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fun factoryForType(type: String): NativeComponentFactory? = factories[type]
|
|
73
|
+
|
|
74
|
+
fun factoryForView(view: View): NativeComponentFactory? =
|
|
75
|
+
view.getTag(TAG_FACTORY) as? NativeComponentFactory
|
|
76
|
+
}
|