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.
Files changed (81) hide show
  1. package/bin/catalyst.js +8 -1
  2. package/changelog.md +6 -0
  3. package/dist/native/androidProject/app/build.gradle.kts +9 -0
  4. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +1 -1
  5. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +17 -1
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +31 -3
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +148 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeCameraManager.kt +337 -0
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/TransitionManager.kt +202 -0
  10. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +123 -0
  11. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +244 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/HoldController.kt +70 -0
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/TorchController.kt +57 -0
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamState.kt +41 -0
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/VideoStreamStateMachine.kt +40 -0
  16. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ViewfinderMapper.kt +75 -0
  17. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/ZoomController.kt +156 -0
  18. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/CatalystPlugin.kt +7 -0
  19. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/GeneratedPluginIndex.kt +6 -0
  20. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/plugins/PluginBridge.kt +253 -0
  21. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +42 -5
  22. package/dist/native/androidProject/app/src/main/res/drawable/debug_viewfinder_border.xml +8 -0
  23. package/dist/native/androidProject/app/src/main/res/layout/activity_main.xml +37 -0
  24. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +139 -0
  25. package/dist/native/bridge/WebBridge.js +5 -5
  26. package/dist/native/bridge/constants/NativeInterfaces.js +9 -5
  27. package/dist/native/bridge/hooks/index.js +1 -0
  28. package/dist/native/bridge/hooks/useCamera.js +9 -0
  29. package/dist/native/bridge/hooks/useDataProtection.js +2 -0
  30. package/dist/native/bridge/hooks/useDeviceInfo.js +2 -0
  31. package/dist/native/bridge/hooks/useFilePicker.js +2 -0
  32. package/dist/native/bridge/hooks/useGoogleSignIn.js +1 -0
  33. package/dist/native/bridge/hooks/useHapticFeedback.js +1 -0
  34. package/dist/native/bridge/hooks/useIntent.js +1 -0
  35. package/dist/native/bridge/hooks/useNativeTransition.js +43 -0
  36. package/dist/native/bridge/hooks/useNetworkStatus.js +3 -0
  37. package/dist/native/bridge/hooks/useNotification.js +2 -0
  38. package/dist/native/bridge/hooks/useSafeArea.js +2 -0
  39. package/dist/native/bridge/hooks/useVideoStream.js +6 -0
  40. package/dist/native/bridge/hooks/utils.js +1 -0
  41. package/dist/native/bridge/hooks.js +1 -137
  42. package/dist/native/bridge/useBaseHook.js +8 -3
  43. package/dist/native/bridge/utils/NativeBridge.js +36 -1
  44. package/dist/native/buildAppAndroid.js +2 -2
  45. package/dist/native/buildAppIos.js +10 -17
  46. package/dist/native/internalPluginUtils.js +1 -0
  47. package/dist/native/iosnativeWebView/Package.resolved +36 -0
  48. package/dist/native/iosnativeWebView/Sources/Core/Camera/BarcodeDetector.swift +116 -0
  49. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraPreviewView.swift +58 -0
  50. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +308 -0
  51. package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +63 -0
  52. package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +194 -0
  53. package/dist/native/iosnativeWebView/Sources/Core/Camera/TorchController.swift +60 -0
  54. package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamState.swift +44 -0
  55. package/dist/native/iosnativeWebView/Sources/Core/Camera/VideoStreamStateMachine.swift +60 -0
  56. package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +130 -0
  57. package/dist/native/iosnativeWebView/Sources/Core/Constants/CatalystConstants.swift +11 -1
  58. package/dist/native/iosnativeWebView/Sources/Core/Plugins/CatalystPlugin.swift +5 -0
  59. package/dist/native/iosnativeWebView/Sources/Core/Plugins/GeneratedPluginIndex.swift +6 -0
  60. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +364 -0
  61. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +4 -1
  62. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +227 -4
  63. package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +169 -0
  64. package/dist/native/iosnativeWebView/Sources/Core/WebView/WeakScriptMessageHandler.swift +14 -0
  65. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +38 -7
  66. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +6 -2
  67. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +15 -4
  68. package/dist/native/iosnativeWebView/iosnativeWebView/Info.plist +11 -17
  69. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +8 -2
  70. package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +160 -0
  71. package/dist/native/plugin-bridge/PluginBridge.js +1 -0
  72. package/dist/native/pluginComposerAndroid.js +9 -0
  73. package/dist/native/pluginComposerIos.js +7 -0
  74. package/dist/otel.js +70 -2
  75. package/dist/scripts/plugins.js +1 -0
  76. package/dist/server/renderer/extract.js +3 -3
  77. package/dist/server/renderer/handler.js +15 -14
  78. package/dist/webpack/development.client.babel.js +5 -1
  79. package/dist/webpack/production.ssr.babel.js +5 -1
  80. package/mcp_v2/knowledge-base.json +1564 -1067
  81. package/package.json +1 -1
@@ -0,0 +1,6 @@
1
+ package io.yourname.androidproject.plugins
2
+
3
+ object GeneratedPluginIndex {
4
+ val pluginIdToClassName: Map<String, String> = emptyMap()
5
+ val pluginToCommands: Map<String, Set<String>> = emptyMap()
6
+ }
@@ -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
+ }
@@ -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
- "window.WebBridge.callback('${event.eventName}', '$safeData')"
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
- val jsonString = jsonData.toString().replace("'", "\\'")
151
- notifyWeb(webView, event, jsonString)
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}")
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shape="rectangle">
4
+ <stroke
5
+ android:width="3dp"
6
+ android:color="#FF0000" />
7
+ <solid android:color="#33FF0000" />
8
+ </shape>
@@ -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 new Promise(resolve=>{const webDeviceInfo={model:navigator.userAgent,manufacturer:"browser",platform:"web",screenWidth:screen.width,screenHeight:screen.height,screenDensity:window.devicePixelRatio};resolve(webDeviceInfo);});}// 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]));};return new Promise((resolve,reject)=>{const cleanup=()=>{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{_NativeBridge.default.device.getDeviceInfo();}catch(error){cleanup();reject(error);}});};}var _default=exports.default=WebBridge;
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",// File commands
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",// Safe area commands
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",// File picker callbacks
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",// Security callbacks
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");