catalyst-core 0.1.0-beta.3 → 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/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/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/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/utils/BridgeUtils.kt +42 -5
- 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/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 +8 -3
- package/dist/native/bridge/utils/NativeBridge.js +36 -1
- package/dist/native/buildAppAndroid.js +2 -2
- package/dist/native/buildAppIos.js +10 -17
- 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/otel.js +70 -2
- package/dist/scripts/plugins.js +1 -0
- package/dist/server/renderer/extract.js +3 -3
- package/dist/server/renderer/handler.js +15 -14
- package/dist/webpack/development.client.babel.js +5 -1
- package/dist/webpack/production.ssr.babel.js +5 -1
- package/mcp_v2/knowledge-base.json +1564 -1067
- package/package.json +1 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
package io.yourname.androidproject.plugins
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import android.webkit.JavascriptInterface
|
|
7
|
+
import android.webkit.WebView
|
|
8
|
+
import io.yourname.androidproject.CatalystConstants
|
|
9
|
+
import org.json.JSONArray
|
|
10
|
+
import org.json.JSONException
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
import java.util.Properties
|
|
13
|
+
|
|
14
|
+
internal data class PluginRequest(
|
|
15
|
+
val pluginId: String,
|
|
16
|
+
val command: String,
|
|
17
|
+
val data: JSONObject?,
|
|
18
|
+
val requestId: String?
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
private class PluginBridgeRuntimeError(
|
|
22
|
+
val publicMessage: String,
|
|
23
|
+
val code: String,
|
|
24
|
+
cause: Throwable? = null
|
|
25
|
+
) : Exception(publicMessage, cause)
|
|
26
|
+
|
|
27
|
+
class PluginBridge(
|
|
28
|
+
private val activity: Activity,
|
|
29
|
+
private val webView: WebView,
|
|
30
|
+
private val properties: Properties
|
|
31
|
+
) {
|
|
32
|
+
companion object {
|
|
33
|
+
private const val TAG = "PluginBridge"
|
|
34
|
+
private const val ERROR_EVENT = "PLUGIN_BRIDGE_ERROR"
|
|
35
|
+
private const val SYSTEM_PLUGIN_ID = "__bridge__"
|
|
36
|
+
private const val ERROR_CODE_INVALID_PAYLOAD = "INVALID_PAYLOAD"
|
|
37
|
+
private const val ERROR_CODE_PLUGIN_NOT_FOUND = "PLUGIN_NOT_FOUND"
|
|
38
|
+
private const val ERROR_CODE_COMMAND_NOT_SUPPORTED = "COMMAND_NOT_SUPPORTED"
|
|
39
|
+
private const val ERROR_CODE_PLUGIN_NOT_REGISTERED = "PLUGIN_NOT_REGISTERED"
|
|
40
|
+
private const val ERROR_CODE_PLUGIN_INSTANTIATION_FAILED = "PLUGIN_INSTANTIATION_FAILED"
|
|
41
|
+
private const val ERROR_CODE_PLUGIN_EXECUTION_FAILED = "PLUGIN_EXECUTION_FAILED"
|
|
42
|
+
|
|
43
|
+
private fun readRequiredString(body: JSONObject, key: String): String {
|
|
44
|
+
if (!body.has(key) || body.isNull(key)) {
|
|
45
|
+
return ""
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
val rawValue = body.get(key)
|
|
49
|
+
if (rawValue !is String) {
|
|
50
|
+
throw IllegalArgumentException("$key must be a string")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return rawValue.trim()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private fun readOptionalString(body: JSONObject, key: String): String? {
|
|
57
|
+
if (!body.has(key) || body.isNull(key)) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
val rawValue = body.get(key)
|
|
62
|
+
if (rawValue !is String) {
|
|
63
|
+
throw IllegalArgumentException("$key must be a string when provided")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return rawValue.trim().ifEmpty { null }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private fun readOptionalObject(body: JSONObject, key: String): JSONObject? {
|
|
70
|
+
if (!body.has(key) || body.isNull(key)) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
val rawValue = body.get(key)
|
|
75
|
+
if (rawValue !is JSONObject) {
|
|
76
|
+
throw IllegalArgumentException("$key must be an object when provided")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return rawValue
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
internal fun parseRequest(payload: String?): PluginRequest {
|
|
83
|
+
if (payload.isNullOrBlank()) {
|
|
84
|
+
throw IllegalArgumentException("Payload is required")
|
|
85
|
+
}
|
|
86
|
+
val messageSize = payload.toByteArray(Charsets.UTF_8).size
|
|
87
|
+
if (messageSize > CatalystConstants.Bridge.MAX_MESSAGE_SIZE) {
|
|
88
|
+
throw IllegalArgumentException("Payload exceeds maximum size")
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
val body = JSONObject(payload)
|
|
92
|
+
return PluginRequest(
|
|
93
|
+
pluginId = readRequiredString(body, "pluginId"),
|
|
94
|
+
command = readRequiredString(body, "command"),
|
|
95
|
+
data = readOptionalObject(body, "data"),
|
|
96
|
+
requestId = readOptionalString(body, "requestId")
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private val pluginIdToClassName = GeneratedPluginIndex.pluginIdToClassName
|
|
102
|
+
private val pluginToCommands = GeneratedPluginIndex.pluginToCommands
|
|
103
|
+
|
|
104
|
+
@JavascriptInterface
|
|
105
|
+
fun emit(payload: String?) {
|
|
106
|
+
var request: PluginRequest? = null
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
request = parseRequest(payload)
|
|
110
|
+
|
|
111
|
+
if (request.pluginId.isEmpty()) {
|
|
112
|
+
sendBridgeError("pluginId is required", ERROR_CODE_INVALID_PAYLOAD, request)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
if (request.command.isEmpty()) {
|
|
116
|
+
sendBridgeError("command is required", ERROR_CODE_INVALID_PAYLOAD, request)
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!hasPlugin(request.pluginId)) {
|
|
121
|
+
sendBridgeError("Unsupported plugin: ${request.pluginId}", ERROR_CODE_PLUGIN_NOT_FOUND, request)
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!hasCommand(request.pluginId, request.command)) {
|
|
126
|
+
sendBridgeError(
|
|
127
|
+
"Unsupported command '${request.command}' for plugin '${request.pluginId}'",
|
|
128
|
+
ERROR_CODE_COMMAND_NOT_SUPPORTED,
|
|
129
|
+
request
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
val plugin = try {
|
|
135
|
+
getPluginForId(request.pluginId)
|
|
136
|
+
} catch (error: PluginBridgeRuntimeError) {
|
|
137
|
+
sendBridgeError(error.publicMessage, error.code, request)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
val callbackContext = PluginBridgeContext(
|
|
142
|
+
activity = activity,
|
|
143
|
+
webView = webView,
|
|
144
|
+
properties = properties,
|
|
145
|
+
pluginId = request.pluginId,
|
|
146
|
+
command = request.command,
|
|
147
|
+
requestId = request.requestId
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
plugin.handle(request.command, request.data, callbackContext)
|
|
151
|
+
} catch (error: IllegalArgumentException) {
|
|
152
|
+
sendBridgeError(error.message ?: "Invalid payload", ERROR_CODE_INVALID_PAYLOAD, request)
|
|
153
|
+
} catch (error: JSONException) {
|
|
154
|
+
sendBridgeError("Invalid JSON payload", ERROR_CODE_INVALID_PAYLOAD, request)
|
|
155
|
+
} catch (error: Exception) {
|
|
156
|
+
Log.e(TAG, "Plugin command failed for ${request?.pluginId ?: "<unknown>"}.${request?.command ?: "<unknown>"}", error)
|
|
157
|
+
sendBridgeError("Plugin execution failed", ERROR_CODE_PLUGIN_EXECUTION_FAILED, request)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private fun sendBridgeError(message: String, code: String, request: PluginRequest?) {
|
|
162
|
+
PluginBridgeContext(
|
|
163
|
+
activity = activity,
|
|
164
|
+
webView = webView,
|
|
165
|
+
properties = properties,
|
|
166
|
+
pluginId = SYSTEM_PLUGIN_ID,
|
|
167
|
+
command = request?.command,
|
|
168
|
+
requestId = request?.requestId
|
|
169
|
+
).callback(
|
|
170
|
+
ERROR_EVENT,
|
|
171
|
+
JSONObject().apply {
|
|
172
|
+
put("message", message)
|
|
173
|
+
put("code", code)
|
|
174
|
+
put("pluginId", request?.pluginId ?: SYSTEM_PLUGIN_ID)
|
|
175
|
+
request?.command?.takeIf { it.isNotEmpty() }?.let { put("command", it) }
|
|
176
|
+
}
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private fun hasPlugin(pluginId: String): Boolean {
|
|
181
|
+
return pluginIdToClassName.containsKey(pluginId)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private fun hasCommand(pluginId: String, command: String): Boolean {
|
|
185
|
+
return pluginToCommands[pluginId]?.contains(command) ?: false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private fun getPluginForId(pluginId: String): CatalystPlugin {
|
|
189
|
+
val className = pluginIdToClassName[pluginId]
|
|
190
|
+
?: throw PluginBridgeRuntimeError(
|
|
191
|
+
"Plugin is not registered",
|
|
192
|
+
ERROR_CODE_PLUGIN_NOT_REGISTERED
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return try {
|
|
196
|
+
val clazz = Class.forName(className)
|
|
197
|
+
val instance = clazz.getDeclaredConstructor().newInstance()
|
|
198
|
+
instance as? CatalystPlugin
|
|
199
|
+
?: throw IllegalStateException("Plugin class '$className' must implement CatalystPlugin")
|
|
200
|
+
} catch (error: Exception) {
|
|
201
|
+
Log.e(TAG, "Failed to instantiate plugin class $className for plugin $pluginId", error)
|
|
202
|
+
throw PluginBridgeRuntimeError(
|
|
203
|
+
"Plugin could not be initialized",
|
|
204
|
+
ERROR_CODE_PLUGIN_INSTANTIATION_FAILED,
|
|
205
|
+
error
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
class PluginBridgeContext(
|
|
212
|
+
val activity: Activity,
|
|
213
|
+
val webView: WebView,
|
|
214
|
+
val properties: Properties,
|
|
215
|
+
val pluginId: String,
|
|
216
|
+
val command: String?,
|
|
217
|
+
val requestId: String?
|
|
218
|
+
) {
|
|
219
|
+
val context: Context
|
|
220
|
+
get() = activity
|
|
221
|
+
|
|
222
|
+
fun callback(
|
|
223
|
+
eventName: String,
|
|
224
|
+
data: Any?,
|
|
225
|
+
command: String? = this.command
|
|
226
|
+
) {
|
|
227
|
+
require(eventName.isNotBlank()) { "Callback eventName is required" }
|
|
228
|
+
|
|
229
|
+
val pluginLiteral = JSONObject.quote(pluginId)
|
|
230
|
+
val eventLiteral = JSONObject.quote(eventName)
|
|
231
|
+
val dataLiteral = toJavaScriptLiteral(data)
|
|
232
|
+
val requestLiteral = requestId?.let(JSONObject::quote) ?: "null"
|
|
233
|
+
val commandLiteral = command?.takeIf { it.isNotBlank() }?.let(JSONObject::quote) ?: "null"
|
|
234
|
+
|
|
235
|
+
webView.post {
|
|
236
|
+
webView.evaluateJavascript(
|
|
237
|
+
"window.PluginBridgeWeb && window.PluginBridgeWeb.callback($pluginLiteral, $eventLiteral, $dataLiteral, $requestLiteral, $commandLiteral);",
|
|
238
|
+
null
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private fun toJavaScriptLiteral(value: Any?): String {
|
|
244
|
+
return when (value) {
|
|
245
|
+
null -> "null"
|
|
246
|
+
is JSONObject -> value.toString()
|
|
247
|
+
is JSONArray -> value.toString()
|
|
248
|
+
is Number, is Boolean -> value.toString()
|
|
249
|
+
is String -> JSONObject.quote(value)
|
|
250
|
+
else -> JSONObject.wrap(value)?.toString() ?: "null"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt
CHANGED
|
@@ -76,12 +76,25 @@ object BridgeUtils {
|
|
|
76
76
|
// Safe area events
|
|
77
77
|
ON_SAFE_AREA_INSETS_UPDATED("ON_SAFE_AREA_INSETS_UPDATED"),
|
|
78
78
|
|
|
79
|
+
// Video stream events
|
|
80
|
+
ON_VIDEO_STREAM_READY("ON_VIDEO_STREAM_READY"),
|
|
81
|
+
ON_VIDEO_STREAM_STOPPED("ON_VIDEO_STREAM_STOPPED"),
|
|
82
|
+
ON_QR_DETECTED("ON_QR_DETECTED"),
|
|
83
|
+
ON_TORCH_CHANGED("ON_TORCH_CHANGED"),
|
|
84
|
+
ON_ZOOM_CHANGED("ON_ZOOM_CHANGED"),
|
|
85
|
+
|
|
79
86
|
// Security events
|
|
80
87
|
ON_SCREEN_SECURE_SET("ON_SCREEN_SECURE_SET"),
|
|
81
88
|
ON_SCREEN_SECURE_STATUS("ON_SCREEN_SECURE_STATUS"),
|
|
82
89
|
ON_SCREEN_SECURE_ERROR("ON_SCREEN_SECURE_ERROR"),
|
|
83
90
|
ON_WEB_DATA_CLEARED("ON_WEB_DATA_CLEARED"),
|
|
84
91
|
ON_WEB_DATA_CLEAR_ERROR("ON_WEB_DATA_CLEAR_ERROR"),
|
|
92
|
+
|
|
93
|
+
// Transition events
|
|
94
|
+
ON_TRANSITION_COMMITTED("ON_TRANSITION_COMMITTED"),
|
|
95
|
+
ON_TRANSITION_CANCELLED("ON_TRANSITION_CANCELLED"),
|
|
96
|
+
ON_TRANSITION_TIMEOUT("ON_TRANSITION_TIMEOUT"),
|
|
97
|
+
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
/**
|
|
@@ -99,13 +112,25 @@ object BridgeUtils {
|
|
|
99
112
|
}
|
|
100
113
|
|
|
101
114
|
try {
|
|
102
|
-
val safeData = data?.replace("'", "\\'") ?: ""
|
|
103
115
|
val jsCode = if (data != null) {
|
|
104
|
-
|
|
116
|
+
// If data looks like a JSON object or array, inject it raw (with U+2028/U+2029
|
|
117
|
+
// escaping) so the JS side receives a live value rather than a double-encoded string.
|
|
118
|
+
// Otherwise wrap with JSONObject.quote() to produce a safe JS string literal.
|
|
119
|
+
val trimmed = data.trimStart()
|
|
120
|
+
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
|
|
121
|
+
(trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
122
|
+
val safeData = data
|
|
123
|
+
.replace("
", "\\u2028")
|
|
124
|
+
.replace("
", "\\u2029")
|
|
125
|
+
"window.WebBridge.callback('${event.eventName}', $safeData)"
|
|
126
|
+
} else {
|
|
127
|
+
val quotedData = org.json.JSONObject.quote(data)
|
|
128
|
+
"window.WebBridge.callback('${event.eventName}', $quotedData)"
|
|
129
|
+
}
|
|
105
130
|
} else {
|
|
106
131
|
"window.WebBridge.callback('${event.eventName}', null)"
|
|
107
132
|
}
|
|
108
|
-
|
|
133
|
+
|
|
109
134
|
webView.evaluateJavascript(jsCode, null)
|
|
110
135
|
Log.d(TAG, "✅ Web notification sent: ${event.eventName}")
|
|
111
136
|
} catch (e: Exception) {
|
|
@@ -146,9 +171,21 @@ object BridgeUtils {
|
|
|
146
171
|
* @param jsonData The JSON object to send
|
|
147
172
|
*/
|
|
148
173
|
fun notifyWebJson(webView: WebView, event: WebEvents, jsonData: JSONObject) {
|
|
174
|
+
// Ensure WebView interactions happen on the main thread
|
|
175
|
+
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
176
|
+
webView.post { notifyWebJson(webView, event, jsonData) }
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
149
180
|
try {
|
|
150
|
-
|
|
151
|
-
|
|
181
|
+
// Inject JSON directly into the JS call. U+2028/U+2029 are valid JSON whitespace but
|
|
182
|
+
// are treated as line terminators by V8, causing a SyntaxError — escape them first.
|
|
183
|
+
val safeJson = jsonData.toString()
|
|
184
|
+
.replace("
", "\\u2028")
|
|
185
|
+
.replace("
", "\\u2029")
|
|
186
|
+
val jsCode = "window.WebBridge.callback('${event.eventName}', $safeJson)"
|
|
187
|
+
webView.evaluateJavascript(jsCode, null)
|
|
188
|
+
Log.d(TAG, "✅ Web JSON notification sent: ${event.eventName}")
|
|
152
189
|
} catch (e: Exception) {
|
|
153
190
|
Log.e(TAG, "❌ Failed to send JSON to web: ${e.message}")
|
|
154
191
|
notifyWebError(webView, event, "Error processing data: ${e.message}")
|
|
@@ -13,6 +13,12 @@
|
|
|
13
13
|
app:layout_constraintStart_toStartOf="parent"
|
|
14
14
|
app:layout_constraintEnd_toEndOf="parent">
|
|
15
15
|
|
|
16
|
+
<androidx.camera.view.PreviewView
|
|
17
|
+
android:id="@+id/camera_preview"
|
|
18
|
+
android:layout_width="match_parent"
|
|
19
|
+
android:layout_height="match_parent"
|
|
20
|
+
android:visibility="invisible" />
|
|
21
|
+
|
|
16
22
|
<WebView
|
|
17
23
|
android:id="@+id/webview"
|
|
18
24
|
android:layout_width="match_parent"
|
|
@@ -24,6 +30,37 @@
|
|
|
24
30
|
android:layout_width="wrap_content"
|
|
25
31
|
android:layout_height="wrap_content"
|
|
26
32
|
android:visibility="gone" />
|
|
33
|
+
|
|
34
|
+
<RelativeLayout
|
|
35
|
+
android:id="@+id/debug_viewfinder_overlay"
|
|
36
|
+
android:layout_width="0dp"
|
|
37
|
+
android:layout_height="0dp"
|
|
38
|
+
android:background="@null"
|
|
39
|
+
android:visibility="gone">
|
|
40
|
+
|
|
41
|
+
<TextView
|
|
42
|
+
android:id="@+id/debug_qr_status"
|
|
43
|
+
android:layout_width="wrap_content"
|
|
44
|
+
android:layout_height="wrap_content"
|
|
45
|
+
android:layout_alignParentBottom="true"
|
|
46
|
+
android:layout_centerHorizontal="true"
|
|
47
|
+
android:layout_marginBottom="8dp"
|
|
48
|
+
android:paddingHorizontal="10dp"
|
|
49
|
+
android:paddingVertical="4dp"
|
|
50
|
+
android:background="#CC000000"
|
|
51
|
+
android:textColor="#FFFFFF"
|
|
52
|
+
android:textSize="12sp"
|
|
53
|
+
android:text="⬤ –" />
|
|
54
|
+
|
|
55
|
+
</RelativeLayout>
|
|
56
|
+
|
|
57
|
+
<!-- Debug: shows where native thinks the barcode box is in screen space -->
|
|
58
|
+
<View
|
|
59
|
+
android:id="@+id/debug_barcode_overlay"
|
|
60
|
+
android:layout_width="0dp"
|
|
61
|
+
android:layout_height="0dp"
|
|
62
|
+
android:background="#5500FF00"
|
|
63
|
+
android:visibility="gone" />
|
|
27
64
|
</RelativeLayout>
|
|
28
65
|
|
|
29
66
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
package io.yourname.androidproject.plugins
|
|
2
|
+
|
|
3
|
+
import io.yourname.androidproject.CatalystConstants
|
|
4
|
+
import org.json.JSONException
|
|
5
|
+
import org.json.JSONObject
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Assert.assertNull
|
|
8
|
+
import org.junit.Assert.assertTrue
|
|
9
|
+
import org.junit.Assert.fail
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
|
|
12
|
+
class PluginBridgeTest {
|
|
13
|
+
|
|
14
|
+
@Test
|
|
15
|
+
fun `parseRequest accepts valid payload and trims string fields`() {
|
|
16
|
+
val request = PluginBridge.parseRequest(
|
|
17
|
+
"""
|
|
18
|
+
{
|
|
19
|
+
"pluginId": " device-info-plugin ",
|
|
20
|
+
"command": " getDeviceInfo ",
|
|
21
|
+
"data": { "includeSecurity": true },
|
|
22
|
+
"requestId": " req-123 "
|
|
23
|
+
}
|
|
24
|
+
""".trimIndent()
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
assertEquals("device-info-plugin", request.pluginId)
|
|
28
|
+
assertEquals("getDeviceInfo", request.command)
|
|
29
|
+
assertEquals("req-123", request.requestId)
|
|
30
|
+
assertEquals(true, (request.data as JSONObject).getBoolean("includeSecurity"))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Test
|
|
34
|
+
fun `parseRequest treats blank requestId as null`() {
|
|
35
|
+
val request = PluginBridge.parseRequest(
|
|
36
|
+
"""
|
|
37
|
+
{
|
|
38
|
+
"pluginId": "device-info-plugin",
|
|
39
|
+
"command": "getDeviceInfo",
|
|
40
|
+
"requestId": " "
|
|
41
|
+
}
|
|
42
|
+
""".trimIndent()
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
assertNull(request.requestId)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@Test
|
|
49
|
+
fun `parseRequest rejects blank payload`() {
|
|
50
|
+
try {
|
|
51
|
+
PluginBridge.parseRequest(" ")
|
|
52
|
+
fail("Expected invalid blank payload to throw")
|
|
53
|
+
} catch (error: IllegalArgumentException) {
|
|
54
|
+
assertEquals("Payload is required", error.message)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
fun `parseRequest rejects oversized payload`() {
|
|
60
|
+
val oversizedData = "x".repeat(CatalystConstants.Bridge.MAX_MESSAGE_SIZE + 256)
|
|
61
|
+
val payload = """
|
|
62
|
+
{
|
|
63
|
+
"pluginId": "device-info-plugin",
|
|
64
|
+
"command": "getDeviceInfo",
|
|
65
|
+
"data": "$oversizedData"
|
|
66
|
+
}
|
|
67
|
+
""".trimIndent()
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
PluginBridge.parseRequest(payload)
|
|
71
|
+
fail("Expected oversized payload to throw")
|
|
72
|
+
} catch (error: IllegalArgumentException) {
|
|
73
|
+
assertEquals("Payload exceeds maximum size", error.message)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Test
|
|
78
|
+
fun `parseRequest rejects non string pluginId`() {
|
|
79
|
+
try {
|
|
80
|
+
PluginBridge.parseRequest(
|
|
81
|
+
"""
|
|
82
|
+
{
|
|
83
|
+
"pluginId": 42,
|
|
84
|
+
"command": "getDeviceInfo"
|
|
85
|
+
}
|
|
86
|
+
""".trimIndent()
|
|
87
|
+
)
|
|
88
|
+
fail("Expected non-string pluginId to throw")
|
|
89
|
+
} catch (error: IllegalArgumentException) {
|
|
90
|
+
assertEquals("pluginId must be a string", error.message)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Test
|
|
95
|
+
fun `parseRequest rejects non string requestId`() {
|
|
96
|
+
try {
|
|
97
|
+
PluginBridge.parseRequest(
|
|
98
|
+
"""
|
|
99
|
+
{
|
|
100
|
+
"pluginId": "device-info-plugin",
|
|
101
|
+
"command": "getDeviceInfo",
|
|
102
|
+
"requestId": 42
|
|
103
|
+
}
|
|
104
|
+
""".trimIndent()
|
|
105
|
+
)
|
|
106
|
+
fail("Expected non-string requestId to throw")
|
|
107
|
+
} catch (error: IllegalArgumentException) {
|
|
108
|
+
assertEquals("requestId must be a string when provided", error.message)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@Test
|
|
113
|
+
fun `parseRequest rejects invalid JSON`() {
|
|
114
|
+
try {
|
|
115
|
+
PluginBridge.parseRequest("{")
|
|
116
|
+
fail("Expected invalid JSON to throw")
|
|
117
|
+
} catch (error: JSONException) {
|
|
118
|
+
assertTrue(error.message?.isNotBlank() == true)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `parseRequest rejects non object data`() {
|
|
124
|
+
try {
|
|
125
|
+
PluginBridge.parseRequest(
|
|
126
|
+
"""
|
|
127
|
+
{
|
|
128
|
+
"pluginId": "device-info-plugin",
|
|
129
|
+
"command": "getDeviceInfo",
|
|
130
|
+
"data": "unsafe"
|
|
131
|
+
}
|
|
132
|
+
""".trimIndent()
|
|
133
|
+
)
|
|
134
|
+
fail("Expected non-object data to throw")
|
|
135
|
+
} catch (error: IllegalArgumentException) {
|
|
136
|
+
assertEquals("data must be an object when provided", error.message)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");var _NativeBridge=_interopRequireDefault(require("./utils/NativeBridge.js"));var _CameraUtils=_interopRequireDefault(require("./utils/CameraUtils.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");var _NativeBridge=_interopRequireDefault(require("./utils/NativeBridge.js"));var _CameraUtils=_interopRequireDefault(require("./utils/CameraUtils.js"));var _PluginBridge=_interopRequireDefault(require("../plugin-bridge/PluginBridge.js"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}const DEVICE_INFO_PLUGIN={pluginId:"io.catalyst.device_info",command:"getDeviceInfo",successEvent:"onSuccess",errorEvent:"onError"};const DEVICE_INFO_REQUEST_TIMEOUT_MS=5000;const DEVICE_INFO_PLUGIN_FALLBACK_CODES=new Set(["PLUGIN_NOT_FOUND","COMMAND_NOT_SUPPORTED","PLUGIN_NOT_REGISTERED"]);const DEVICE_INFO_PLUGIN_FALLBACK_MESSAGES=["pluginbridge is not available in this environment","pluginbridge is not available","device info plugin request timed out"];const normalizeDeviceInfoPayload=payload=>{if(typeof payload!=="string"){return payload;}try{return JSON.parse(payload);}catch{return{message:payload};}};const createDeviceInfoError=(payload,fallbackMessage)=>{const parsed=normalizeDeviceInfoPayload(payload);const message=parsed?.message||parsed?.error||fallbackMessage;const error=new Error(message);if(parsed?.code){error.code=parsed.code;}if(parsed?.pluginId){error.pluginId=parsed.pluginId;}if(parsed?.command){error.command=parsed.command;}return error;};class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
|
|
2
2
|
* Log initialization info
|
|
3
3
|
*/_logInitialization(){console.log("🌉 WebBridge initialized");console.log("🌉 Available callback interfaces:",_NativeInterfaces.ALL_CALLBACKS.length);if(process.env.NODE_ENV==="development"){(0,_NativeInterfaces.debugInterfaces)();}}/**
|
|
4
4
|
* Static method to initialize WebBridge on window object
|
|
@@ -51,9 +51,9 @@ if(this.handlers.has(interfaceName)){console.warn(`🌉 Interface ${interfaceNam
|
|
|
51
51
|
* Request camera permission
|
|
52
52
|
* @param {Object} config - Permission configuration (includeDetails)
|
|
53
53
|
* @returns {Promise<Object>} - Promise that resolves with permission status or rejects with error
|
|
54
|
-
*/requestCameraPermission=(config={})=>{return _CameraUtils.default.requestCameraPermission(config,this.register,this.unregister);};/**
|
|
54
|
+
*/requestCameraPermission=(config={})=>{return _CameraUtils.default.requestCameraPermission(config,this.register,this.unregister);};getDeviceInfoFromLegacyBridge=parseDeviceInfo=>{return new Promise((resolve,reject)=>{let timeoutId=null;const cleanup=()=>{if(timeoutId!=null){clearTimeout(timeoutId);}this.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS);this.unregister(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR);};this.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,data=>{cleanup();resolve(parseDeviceInfo(data));});this.register(_NativeInterfaces.NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR,error=>{cleanup();reject(new Error(error));});try{const dispatched=_NativeBridge.default.device.getDeviceInfo();if(dispatched===false){cleanup();reject(new Error("Legacy device info bridge failed to dispatch"));return;}timeoutId=setTimeout(()=>{cleanup();reject(new Error("Legacy device info request timed out"));},DEVICE_INFO_REQUEST_TIMEOUT_MS);}catch(error){cleanup();reject(error);}});};getDeviceInfoFromPluginBridge=parseDeviceInfo=>{return new Promise((resolve,reject)=>{_PluginBridge.default.init();let isSettled=false;let timeoutId=null;const cleanups=[];const cleanup=()=>{if(timeoutId!=null){clearTimeout(timeoutId);}while(cleanups.length>0){const unregister=cleanups.pop();unregister();}};const settle=callback=>{if(isSettled){return;}isSettled=true;cleanup();callback();};cleanups.push(_PluginBridge.default.register({pluginId:DEVICE_INFO_PLUGIN.pluginId,eventName:DEVICE_INFO_PLUGIN.successEvent,command:DEVICE_INFO_PLUGIN.command,handler:payload=>{settle(()=>{try{resolve(parseDeviceInfo(payload));}catch(error){reject(error);}});}}));cleanups.push(_PluginBridge.default.register({pluginId:DEVICE_INFO_PLUGIN.pluginId,eventName:DEVICE_INFO_PLUGIN.errorEvent,command:DEVICE_INFO_PLUGIN.command,handler:payload=>{settle(()=>{reject(createDeviceInfoError(payload,"Device info plugin failed"));});}}));cleanups.push(_PluginBridge.default.register({pluginId:"__bridge__",eventName:"PLUGIN_BRIDGE_ERROR",command:DEVICE_INFO_PLUGIN.command,handler:payload=>{const pluginId=normalizeDeviceInfoPayload(payload)?.pluginId;if(pluginId!==DEVICE_INFO_PLUGIN.pluginId){return;}settle(()=>{reject(createDeviceInfoError(payload,"Device info plugin unavailable"));});}}));try{_PluginBridge.default.emit({pluginId:DEVICE_INFO_PLUGIN.pluginId,command:DEVICE_INFO_PLUGIN.command});timeoutId=setTimeout(()=>{settle(()=>{reject(new Error("Device info plugin request timed out"));});},DEVICE_INFO_REQUEST_TIMEOUT_MS);}catch(error){cleanup();reject(error);}});};shouldFallbackToLegacyDeviceInfo=error=>{if(!error){return false;}if(DEVICE_INFO_PLUGIN_FALLBACK_CODES.has(error.code)){return true;}if(typeof error.message!=="string"){return false;}const normalizedMessage=error.message.toLowerCase();return DEVICE_INFO_PLUGIN_FALLBACK_MESSAGES.some(message=>normalizedMessage.includes(message));};/**
|
|
55
55
|
* Get device information
|
|
56
56
|
* @returns {Promise<Object>} - Promise that resolves with device info or rejects with error
|
|
57
|
-
*/getDeviceInfo=()=>{const{platform,isNativeEnvironment}=_NativeBridge.default.getEnvironmentInfo();// Handle web environment
|
|
58
|
-
if(!isNativeEnvironment||platform==="web"){return
|
|
59
|
-
const DEVICE_INFO_KEY_MAP={android:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"},ios:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"}};const parseDeviceInfo=data=>{const rawDeviceInfo=typeof data==="string"?JSON.parse(data):data;const platformMapping=DEVICE_INFO_KEY_MAP[platform]||{};return Object.fromEntries(Object.entries(rawDeviceInfo).map(([key,value])=>[platformMapping[key]||key,value]));};return
|
|
57
|
+
*/getDeviceInfo=async()=>{const{platform,isNativeEnvironment}=_NativeBridge.default.getEnvironmentInfo();// Handle web environment
|
|
58
|
+
if(!isNativeEnvironment||platform==="web"){return{model:navigator.userAgent,manufacturer:"browser",platform:"web",screenWidth:screen.width,screenHeight:screen.height,screenDensity:window.devicePixelRatio};}// Key mapping from native to web keys
|
|
59
|
+
const DEVICE_INFO_KEY_MAP={android:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"},ios:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity",appInfo:"appInfo"}};const parseDeviceInfo=data=>{const rawDeviceInfo=typeof data==="string"?JSON.parse(data):data;const platformMapping=DEVICE_INFO_KEY_MAP[platform]||{};return Object.fromEntries(Object.entries(rawDeviceInfo).map(([key,value])=>[platformMapping[key]||key,value]));};try{return await this.getDeviceInfoFromPluginBridge(parseDeviceInfo);}catch(error){if(!this.shouldFallbackToLegacyDeviceInfo(error)){throw error;}return this.getDeviceInfoFromLegacyBridge(parseDeviceInfo);}};}var _default=exports.default=WebBridge;
|
|
@@ -2,17 +2,20 @@
|
|
|
2
2
|
// Centralized definitions for all native interface communications
|
|
3
3
|
// Command Interfaces: Web → Native (outgoing commands)
|
|
4
4
|
const NATIVE_COMMANDS=exports.NATIVE_COMMANDS={// Camera commands
|
|
5
|
-
OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",//
|
|
5
|
+
OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",// Video stream commands
|
|
6
|
+
START_VIDEO_STREAM:"startVideoStream",STOP_VIDEO_STREAM:"stopVideoStream",SET_VIDEO_STREAM_ZOOM:"setVideoStreamZoom",SET_VIDEO_STREAM_TORCH:"setVideoStreamTorch",SET_VIDEO_STREAM_FPS:"setVideoStreamFps",FLIP_VIDEO_STREAM:"flipVideoStream",// File commands
|
|
6
7
|
PICK_FILE:"pickFile",OPEN_FILE_WITH_INTENT:"openFileWithIntent",// Haptic feedback commands
|
|
7
8
|
REQUEST_HAPTIC_FEEDBACK:"requestHapticFeedback",// Notification commands
|
|
8
9
|
SCHEDULE_LOCAL_NOTIFICATION:"scheduleLocalNotification",CANCEL_LOCAL_NOTIFICATION:"cancelLocalNotification",REQUEST_NOTIFICATION_PERMISSION:"requestNotificationPermission",REGISTER_FOR_PUSH_NOTIFICATIONS:"registerForPushNotifications",UPDATE_BADGE_COUNT:"updateBadgeCount",SUBSCRIBE_TO_TOPIC:"subscribeToTopic",UNSUBSCRIBE_FROM_TOPIC:"unsubscribeFromTopic",GET_SUBSCRIBED_TOPICS:"getSubscribedTopics",// Device info commands
|
|
9
10
|
GET_DEVICE_INFO:"getDeviceInfo",// Network commands
|
|
10
11
|
GET_NETWORK_STATUS:"getNetworkStatus",// Auth commands
|
|
11
|
-
GOOGLE_SIGN_IN:"googleSignIn",//
|
|
12
|
+
GOOGLE_SIGN_IN:"googleSignIn",// Transition commands
|
|
13
|
+
START_TRANSITION:"startTransition",COMMIT_TRANSITION:"commitTransition",CANCEL_TRANSITION:"cancelTransition",// Safe area commands
|
|
12
14
|
GET_SAFE_AREA:"getSafeArea",// Security commands
|
|
13
15
|
SET_SCREEN_SECURE:"setScreenSecure",GET_SCREEN_SECURE:"getScreenSecure",CLEAR_WEB_DATA:"clearWebData"};// Callback Interfaces: Native → Web (incoming events/callbacks)
|
|
14
16
|
const NATIVE_CALLBACKS=exports.NATIVE_CALLBACKS={// Camera callbacks
|
|
15
|
-
CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",//
|
|
17
|
+
CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",// Video stream callbacks
|
|
18
|
+
ON_VIDEO_STREAM_READY:"ON_VIDEO_STREAM_READY",ON_VIDEO_STREAM_STOPPED:"ON_VIDEO_STREAM_STOPPED",ON_QR_DETECTED:"ON_QR_DETECTED",ON_TORCH_CHANGED:"ON_TORCH_CHANGED",ON_ZOOM_CHANGED:"ON_ZOOM_CHANGED",// File picker callbacks
|
|
16
19
|
ON_FILE_PICKED:"ON_FILE_PICKED",ON_FILE_PICK_ERROR:"ON_FILE_PICK_ERROR",ON_FILE_PICK_CANCELLED:"ON_FILE_PICK_CANCELLED",ON_FILE_PICK_STATE_UPDATE:"ON_FILE_PICK_STATE_UPDATE",// Intent callbacks
|
|
17
20
|
ON_INTENT_SUCCESS:"ON_INTENT_SUCCESS",ON_INTENT_ERROR:"ON_INTENT_ERROR",ON_INTENT_CANCELLED:"ON_INTENT_CANCELLED",// Haptic feedback callbacks
|
|
18
21
|
HAPTIC_FEEDBACK:"HAPTIC_FEEDBACK",// Notification callbacks
|
|
@@ -20,9 +23,10 @@ NOTIFICATION_PERMISSION_STATUS:"NOTIFICATION_PERMISSION_STATUS",LOCAL_NOTIFICATI
|
|
|
20
23
|
ON_DEVICE_INFO_SUCCESS:"ON_DEVICE_INFO_SUCCESS",ON_DEVICE_INFO_ERROR:"ON_DEVICE_INFO_ERROR",// Auth callbacks
|
|
21
24
|
ON_GOOGLE_SIGN_IN_SUCCESS:"ON_GOOGLE_SIGN_IN_SUCCESS",ON_GOOGLE_SIGN_IN_ERROR:"ON_GOOGLE_SIGN_IN_ERROR",ON_GOOGLE_SIGN_IN_CANCELLED:"ON_GOOGLE_SIGN_IN_CANCELLED",// Network callbacks
|
|
22
25
|
NETWORK_STATUS_CHANGED:"NETWORK_STATUS_CHANGED",// Safe area callbacks
|
|
23
|
-
ON_SAFE_AREA_INSETS_UPDATED:"ON_SAFE_AREA_INSETS_UPDATED",//
|
|
26
|
+
ON_SAFE_AREA_INSETS_UPDATED:"ON_SAFE_AREA_INSETS_UPDATED",// Transition callbacks
|
|
27
|
+
ON_TRANSITION_COMMITTED:"ON_TRANSITION_COMMITTED",ON_TRANSITION_CANCELLED:"ON_TRANSITION_CANCELLED",ON_TRANSITION_TIMEOUT:"ON_TRANSITION_TIMEOUT",// Security callbacks
|
|
24
28
|
ON_SCREEN_SECURE_SET:"ON_SCREEN_SECURE_SET",ON_SCREEN_SECURE_STATUS:"ON_SCREEN_SECURE_STATUS",ON_SCREEN_SECURE_ERROR:"ON_SCREEN_SECURE_ERROR",ON_WEB_DATA_CLEARED:"ON_WEB_DATA_CLEARED",ON_WEB_DATA_CLEAR_ERROR:"ON_WEB_DATA_CLEAR_ERROR"};// Interface Categories for easier management
|
|
25
|
-
const INTERFACE_CATEGORIES=exports.INTERFACE_CATEGORIES={CAMERA:{commands:[NATIVE_COMMANDS.OPEN_CAMERA,NATIVE_COMMANDS.REQUEST_CAMERA_PERMISSION],callbacks:[NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,NATIVE_CALLBACKS.ON_CAMERA_ERROR]},FILE:{commands:[NATIVE_COMMANDS.PICK_FILE,NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_FILE_PICKED,NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE]},INTENT:{commands:[NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_INTENT_SUCCESS,NATIVE_CALLBACKS.ON_INTENT_ERROR,NATIVE_CALLBACKS.ON_INTENT_CANCELLED]},HAPTIC:{commands:[NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK],callbacks:[NATIVE_CALLBACKS.HAPTIC_FEEDBACK]},NOTIFICATION:{commands:[NATIVE_COMMANDS.SCHEDULE_LOCAL_NOTIFICATION,NATIVE_COMMANDS.CANCEL_LOCAL_NOTIFICATION,NATIVE_COMMANDS.REQUEST_NOTIFICATION_PERMISSION,NATIVE_COMMANDS.REGISTER_FOR_PUSH_NOTIFICATIONS,NATIVE_COMMANDS.UPDATE_BADGE_COUNT,NATIVE_COMMANDS.SUBSCRIBE_TO_TOPIC,NATIVE_COMMANDS.UNSUBSCRIBE_FROM_TOPIC,NATIVE_COMMANDS.GET_SUBSCRIBED_TOPICS],callbacks:[NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,NATIVE_CALLBACKS.NOTIFICATION_TAPPED,NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT]},DEVICE_INFO:{commands:[NATIVE_COMMANDS.GET_DEVICE_INFO],callbacks:[NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR]},AUTH:{commands:[NATIVE_COMMANDS.GOOGLE_SIGN_IN],callbacks:[NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED]},NETWORK:{commands:[NATIVE_COMMANDS.GET_NETWORK_STATUS],callbacks:[NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED]},SECURITY:{commands:[NATIVE_COMMANDS.SET_SCREEN_SECURE,NATIVE_COMMANDS.GET_SCREEN_SECURE,NATIVE_COMMANDS.CLEAR_WEB_DATA],callbacks:[NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR]}};// All available interfaces (for validation)
|
|
29
|
+
const INTERFACE_CATEGORIES=exports.INTERFACE_CATEGORIES={CAMERA:{commands:[NATIVE_COMMANDS.OPEN_CAMERA,NATIVE_COMMANDS.REQUEST_CAMERA_PERMISSION],callbacks:[NATIVE_CALLBACKS.CAMERA_PERMISSION_STATUS,NATIVE_CALLBACKS.ON_CAMERA_CAPTURE,NATIVE_CALLBACKS.ON_CAMERA_ERROR]},FILE:{commands:[NATIVE_COMMANDS.PICK_FILE,NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_FILE_PICKED,NATIVE_CALLBACKS.ON_FILE_PICK_ERROR,NATIVE_CALLBACKS.ON_FILE_PICK_CANCELLED,NATIVE_CALLBACKS.ON_FILE_PICK_STATE_UPDATE]},INTENT:{commands:[NATIVE_COMMANDS.OPEN_FILE_WITH_INTENT],callbacks:[NATIVE_CALLBACKS.ON_INTENT_SUCCESS,NATIVE_CALLBACKS.ON_INTENT_ERROR,NATIVE_CALLBACKS.ON_INTENT_CANCELLED]},HAPTIC:{commands:[NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK],callbacks:[NATIVE_CALLBACKS.HAPTIC_FEEDBACK]},NOTIFICATION:{commands:[NATIVE_COMMANDS.SCHEDULE_LOCAL_NOTIFICATION,NATIVE_COMMANDS.CANCEL_LOCAL_NOTIFICATION,NATIVE_COMMANDS.REQUEST_NOTIFICATION_PERMISSION,NATIVE_COMMANDS.REGISTER_FOR_PUSH_NOTIFICATIONS,NATIVE_COMMANDS.UPDATE_BADGE_COUNT,NATIVE_COMMANDS.SUBSCRIBE_TO_TOPIC,NATIVE_COMMANDS.UNSUBSCRIBE_FROM_TOPIC,NATIVE_COMMANDS.GET_SUBSCRIBED_TOPICS],callbacks:[NATIVE_CALLBACKS.NOTIFICATION_PERMISSION_STATUS,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_SCHEDULED,NATIVE_CALLBACKS.LOCAL_NOTIFICATION_CANCELLED,NATIVE_CALLBACKS.PUSH_NOTIFICATION_TOKEN,NATIVE_CALLBACKS.NOTIFICATION_RECEIVED,NATIVE_CALLBACKS.NOTIFICATION_ACTION_PERFORMED,NATIVE_CALLBACKS.NOTIFICATION_TAPPED,NATIVE_CALLBACKS.TOPIC_SUBSCRIPTION_RESULT,NATIVE_CALLBACKS.SUBSCRIBED_TOPICS_RESULT]},DEVICE_INFO:{commands:[NATIVE_COMMANDS.GET_DEVICE_INFO],callbacks:[NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR]},AUTH:{commands:[NATIVE_COMMANDS.GOOGLE_SIGN_IN],callbacks:[NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_SUCCESS,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_ERROR,NATIVE_CALLBACKS.ON_GOOGLE_SIGN_IN_CANCELLED]},NETWORK:{commands:[NATIVE_COMMANDS.GET_NETWORK_STATUS],callbacks:[NATIVE_CALLBACKS.NETWORK_STATUS_CHANGED]},SECURITY:{commands:[NATIVE_COMMANDS.SET_SCREEN_SECURE,NATIVE_COMMANDS.GET_SCREEN_SECURE,NATIVE_COMMANDS.CLEAR_WEB_DATA],callbacks:[NATIVE_CALLBACKS.ON_SCREEN_SECURE_SET,NATIVE_CALLBACKS.ON_SCREEN_SECURE_ERROR,NATIVE_CALLBACKS.ON_WEB_DATA_CLEARED,NATIVE_CALLBACKS.ON_WEB_DATA_CLEAR_ERROR]},TRANSITION:{commands:[NATIVE_COMMANDS.START_TRANSITION,NATIVE_COMMANDS.COMMIT_TRANSITION,NATIVE_COMMANDS.CANCEL_TRANSITION],callbacks:[NATIVE_CALLBACKS.ON_TRANSITION_COMMITTED,NATIVE_CALLBACKS.ON_TRANSITION_CANCELLED,NATIVE_CALLBACKS.ON_TRANSITION_TIMEOUT]}};// All available interfaces (for validation)
|
|
26
30
|
const ALL_COMMANDS=exports.ALL_COMMANDS=Object.values(NATIVE_COMMANDS);const ALL_CALLBACKS=exports.ALL_CALLBACKS=Object.values(NATIVE_CALLBACKS);const ALL_INTERFACES=exports.ALL_INTERFACES=[...ALL_COMMANDS,...ALL_CALLBACKS];// Permission status constants
|
|
27
31
|
const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:"GRANTED",DENIED:"DENIED",NOT_DETERMINED:"NOT_DETERMINED",RESTRICTED:"RESTRICTED"};// Haptic feedback types (matching Android implementation)
|
|
28
32
|
const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={VIRTUAL_KEY:"VIRTUAL_KEY",LONG_PRESS:"LONG_PRESS",DEFAULT:"DEFAULT",// Legacy types for backward compatibility
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"requestCameraPermission",{enumerable:true,get:function(){return _useCamera.requestCameraPermission;}});Object.defineProperty(exports,"requestHapticFeedback",{enumerable:true,get:function(){return _useHapticFeedback.requestHapticFeedback;}});Object.defineProperty(exports,"requestNotificationPermission",{enumerable:true,get:function(){return _useNotification.requestNotificationPermission;}});Object.defineProperty(exports,"sanitizeFilePickerOptions",{enumerable:true,get:function(){return _useFilePicker.sanitizeFilePickerOptions;}});Object.defineProperty(exports,"useCamera",{enumerable:true,get:function(){return _useCamera.useCamera;}});Object.defineProperty(exports,"useCameraPermission",{enumerable:true,get:function(){return _useCamera.useCameraPermission;}});Object.defineProperty(exports,"useDataProtection",{enumerable:true,get:function(){return _useDataProtection.useDataProtection;}});Object.defineProperty(exports,"useDeviceInfo",{enumerable:true,get:function(){return _useDeviceInfo.useDeviceInfo;}});Object.defineProperty(exports,"useFilePicker",{enumerable:true,get:function(){return _useFilePicker.useFilePicker;}});Object.defineProperty(exports,"useGoogleSignIn",{enumerable:true,get:function(){return _useGoogleSignIn.useGoogleSignIn;}});Object.defineProperty(exports,"useHapticFeedback",{enumerable:true,get:function(){return _useHapticFeedback.useHapticFeedback;}});Object.defineProperty(exports,"useIntent",{enumerable:true,get:function(){return _useIntent.useIntent;}});Object.defineProperty(exports,"useNativeTransition",{enumerable:true,get:function(){return _useNativeTransition.useNativeTransition;}});Object.defineProperty(exports,"useNetworkStatus",{enumerable:true,get:function(){return _useNetworkStatus.useNetworkStatus;}});Object.defineProperty(exports,"useNotification",{enumerable:true,get:function(){return _useNotification.useNotification;}});Object.defineProperty(exports,"useNotificationPermission",{enumerable:true,get:function(){return _useNotification.useNotificationPermission;}});Object.defineProperty(exports,"useSafeArea",{enumerable:true,get:function(){return _useSafeArea.useSafeArea;}});Object.defineProperty(exports,"useVideoStream",{enumerable:true,get:function(){return _useVideoStream.useVideoStream;}});var _useGoogleSignIn=require("./useGoogleSignIn.js");var _useCamera=require("./useCamera.js");var _useIntent=require("./useIntent.js");var _useFilePicker=require("./useFilePicker.js");var _useHapticFeedback=require("./useHapticFeedback.js");var _useNotification=require("./useNotification.js");var _useNetworkStatus=require("./useNetworkStatus.js");var _useDeviceInfo=require("./useDeviceInfo.js");var _useDataProtection=require("./useDataProtection.js");var _useSafeArea=require("./useSafeArea.js");var _useVideoStream=require("./useVideoStream.js");var _useNativeTransition=require("./useNativeTransition.js");
|