catalyst-core 0.0.3-canary.7 โ†’ 0.0.3-canary.9

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 (22) hide show
  1. package/changelog.md +45 -25
  2. package/dist/native/androidProject/.idea/gradle.xml +0 -1
  3. package/dist/native/androidProject/.idea/misc.xml +7 -1
  4. package/dist/native/androidProject/app/build.gradle.kts +44 -26
  5. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +59 -0
  6. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +27 -1
  7. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +11 -0
  8. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +4 -0
  9. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/DeviceInfoUtils.kt +60 -0
  10. package/dist/native/androidProject/app/src/main/res/drawable/splashscreen_fallback.png +0 -0
  11. package/dist/native/androidProject/app/src/main/res/values/strings.xml +1 -1
  12. package/dist/native/androidProject/settings.gradle.kts +1 -1
  13. package/dist/native/bridge/WebBridge.js +10 -6
  14. package/dist/native/bridge/constants/NativeInterfaces.js +16 -14
  15. package/dist/native/bridge/errors.js +20 -20
  16. package/dist/native/bridge/hooks.js +17 -17
  17. package/dist/native/bridge/standardizedHooks.js +25 -25
  18. package/dist/native/bridge/useBaseHook.js +12 -12
  19. package/dist/native/bridge/utils/NativeBridge.js +18 -13
  20. package/dist/native/buildAppAndroid.js +64 -40
  21. package/package.json +1 -1
  22. package/dist/native/androidProject/.idea/deviceManager.xml +0 -13
package/changelog.md CHANGED
@@ -1,44 +1,64 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-canary.9] - 2025-09-11
4
+
5
+ - **App name configuration**
6
+ - **Device Info API**
7
+ - **Access control configuration**
8
+
9
+ ## [0.0.3-canary.8] - 2025-01-06
10
+
11
+ - **App icon configuration**
12
+ - **Splash screen integration**
13
+ - **Url Whitelisting**
3
14
 
4
15
  ## [0.0.3-canary.7] - 2025-01-06
5
16
 
6
17
  ### ๐Ÿš€ Features
7
- - **Native API Integration**: Enhanced native module integration for better performance
8
- - **Automated IP Resolution**: Intelligent network detection eliminates manual IP configuration
9
- - **Universal Build System**: Streamlined one-command builds for both Android and iOS platforms
10
- - **Enhanced CLI Interface**: Improved command-line tools for better developer experience
11
18
 
12
- ### ๐Ÿ› Bug Fixes
13
- - Fixed IP address detection issues on development environments
14
- - Resolved build configuration conflicts between platforms
15
- - Improved error handling and recovery mechanisms
19
+ - **Native API Integration**: Enhanced native module integration for better performance
20
+ - **Automated IP Resolution**: Intelligent network detection eliminates manual IP configuration
21
+ - **Universal Build System**: Streamlined one-command builds for both Android and iOS platforms
22
+ - **Enhanced CLI Interface**: Improved command-line tools for better developer experience
23
+
24
+ ### ๐Ÿ› Bug Fixes
25
+
26
+ - Fixed IP address detection issues on development environments
27
+ - Resolved build configuration conflicts between platforms
28
+ - Improved error handling and recovery mechanisms
16
29
 
17
30
  ### ๐Ÿ”ง Improvements
18
- - **Performance**: Optimized native module loading and execution
19
- - **Developer Experience**: Enhanced CLI with better error messages and debugging info
20
- - **Configuration**: Dynamic config updates without requiring restarts
21
- - **Build Process**: Faster and more reliable build pipeline
31
+
32
+ - **Performance**: Optimized native module loading and execution
33
+ - **Developer Experience**: Enhanced CLI with better error messages and debugging info
34
+ - **Configuration**: Dynamic config updates without requiring restarts
35
+ - **Build Process**: Faster and more reliable build pipeline
22
36
 
23
37
  ### ๐Ÿงช Testing & Validation
24
- - โœ… **Android Build**: Verified APK generation, installation, and runtime behavior
25
- - โœ… **iOS Build**: Tested build process and device compatibility
26
- - โœ… **IP Detection**: Validated automatic network resolution across platforms
27
- - โœ… **Native Modules**: Confirmed API integration and performance benchmarks
28
- - โœ… **CLI Operations**: Tested enhanced command-line workflows and error handling
38
+
39
+ - โœ… **Android Build**: Verified APK generation, installation, and runtime behavior
40
+ - โœ… **iOS Build**: Tested build process and device compatibility
41
+ - โœ… **IP Detection**: Validated automatic network resolution across platforms
42
+ - โœ… **Native Modules**: Confirmed API integration and performance benchmarks
43
+ - โœ… **CLI Operations**: Tested enhanced command-line workflows and error handling
29
44
 
30
45
  ### ๐Ÿ”„ Breaking Changes
46
+
31
47
  **None** - All changes maintain backward compatibility while enhancing existing functionality
32
48
 
33
49
  ### ๐Ÿ“ Technical Details
34
- - Improved native API bridge for better cross-platform communication
35
- - Enhanced error reporting with detailed stack traces and suggestions
36
- - Optimized build configuration for faster development cycles
37
- - Added comprehensive logging for debugging and monitoring
50
+
51
+ - Improved native API bridge for better cross-platform communication
52
+ - Enhanced error reporting with detailed stack traces and suggestions
53
+ - Optimized build configuration for faster development cycles
54
+ - Added comprehensive logging for debugging and monitoring
38
55
 
39
56
  ### ๐Ÿ”— Migration Notes
40
- - No migration steps required
41
- - Existing projects will automatically benefit from improvements
42
- - Optional: Update CLI usage to leverage new enhanced features
57
+
58
+ - No migration steps required
59
+ - Existing projects will automatically benefit from improvements
60
+ - Optional: Update CLI usage to leverage new enhanced features
61
+
43
62
  ## Version
44
- - Target version: 0.0.3-canary.7
63
+
64
+ - Target version: 0.0.3-canary.7
@@ -1,6 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <project version="4">
3
- <component name="GradleMigrationSettings" migrationVersion="1" />
4
3
  <component name="GradleSettings">
5
4
  <option name="linkedExternalProjectsSettings">
6
5
  <GradleProjectSettings>
@@ -1,4 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
1
2
  <project version="4">
2
3
  <component name="ExternalStorageConfigurationManager" enabled="true" />
3
- <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK" />
4
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
5
+ <output url="file://$PROJECT_DIR$/build/classes" />
6
+ </component>
7
+ <component name="ProjectType">
8
+ <option name="id" value="Android" />
9
+ </component>
4
10
  </project>
@@ -1,6 +1,14 @@
1
1
  import java.net.NetworkInterface
2
2
  import java.io.File
3
3
  import java.util.Properties
4
+ import org.json.JSONObject
5
+ import org.json.JSONArray
6
+
7
+ buildscript {
8
+ dependencies {
9
+ classpath("org.json:json:20231013")
10
+ }
11
+ }
4
12
 
5
13
  val configPath: String? by project.properties
6
14
  val keystorePassword: String? by project.properties // Changed from keyStorePassword to keystorePassword
@@ -133,6 +141,7 @@ dependencies {
133
141
  androidTestImplementation(libs.androidx.espresso.core)
134
142
  implementation("androidx.webkit:webkit:1.12.1")
135
143
  implementation("org.json:json:20231013")
144
+ implementation("androidx.core:core-splashscreen:1.0.1")
136
145
 
137
146
  // Ktor Server dependencies for FrameworkServer (~200KB total)
138
147
  implementation("io.ktor:ktor-server-core:2.3.7")
@@ -196,12 +205,15 @@ tasks.register("generateWebViewConfig") {
196
205
  }
197
206
 
198
207
  val configContent = configJsonFile.readText()
199
-
200
- // Extract WEBVIEW_CONFIG section
201
- val webviewConfigRegex = """"WEBVIEW_CONFIG"\s*:\s*\{([^}]*)\}""".toRegex()
202
- val webviewConfigMatch = webviewConfigRegex.find(configContent)
203
- val webviewConfigContent = webviewConfigMatch?.groupValues?.get(1)
204
-
208
+
209
+ val jsonObject = JSONObject(configContent)
210
+
211
+ if (!jsonObject.has("WEBVIEW_CONFIG")) {
212
+ throw GradleException("WEBVIEW_CONFIG not found in config file")
213
+ }
214
+
215
+ val webviewConfig = jsonObject.getJSONObject("WEBVIEW_CONFIG")
216
+
205
217
  val properties = Properties()
206
218
 
207
219
  // Set different IP based on build type
@@ -215,30 +227,36 @@ tasks.register("generateWebViewConfig") {
215
227
  properties.setProperty("buildOptimisation", "false")
216
228
  }
217
229
 
218
- // Parse top-level properties
219
- val topLevelRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
220
- webviewConfigContent?.let {
221
- topLevelRegex.findAll(it).forEach { matchResult ->
222
- val (key, value) = matchResult.destructured
223
- if (key != "android") {
224
- properties.setProperty(key, value)
230
+ fun extractProperties(jsonObj: org.json.JSONObject, prefix: String = "") {
231
+ try {
232
+ val keys = jsonObj.keys()
233
+
234
+ while (keys.hasNext()) {
235
+ val key = keys.next()
236
+ val value = jsonObj.opt(key)
237
+ val fullKey = if (prefix.isEmpty()) key else "$prefix.$key"
238
+
239
+ when (value) {
240
+ is org.json.JSONObject -> {
241
+ extractProperties(value, fullKey)
242
+ }
243
+ is org.json.JSONArray -> {
244
+ val arrayValues = (0 until value.length()).map { i ->
245
+ value.opt(i).toString()
246
+ }.joinToString(",")
247
+ properties.setProperty(fullKey, arrayValues)
248
+ }
249
+ else -> {
250
+ properties.setProperty(fullKey, value.toString())
251
+ }
252
+ }
225
253
  }
254
+ } catch (e: Exception) {
255
+ throw RuntimeException("Failed to extract properties from JSON at prefix: $prefix", e)
226
256
  }
227
257
  }
228
258
 
229
- // Parse android object specifically
230
- val androidRegex = """"android"\s*:\s*\{([^}]*)\}""".toRegex()
231
- val androidMatch = androidRegex.find(webviewConfigContent ?: "")
232
- val androidContent = androidMatch?.groupValues?.get(1)
233
-
234
- // Parse android object properties
235
- val androidPropsRegex = """"([^"]+)"\s*:\s*"([^"]+)"""".toRegex()
236
- androidContent?.let {
237
- androidPropsRegex.findAll(it).forEach { matchResult ->
238
- val (key, value) = matchResult.destructured
239
- properties.setProperty("android.$key", value)
240
- }
241
- }
259
+ extractProperties(webviewConfig)
242
260
 
243
261
  // Set production-specific properties for release builds
244
262
  if (gradle.startParameter.taskNames.any { it.contains("Release") || it.contains("release") }) {
@@ -2,6 +2,7 @@ package io.yourname.androidproject
2
2
 
3
3
  import android.annotation.SuppressLint
4
4
  import android.content.Context
5
+ import android.content.Intent
5
6
  import android.graphics.Bitmap
6
7
  import android.net.Uri
7
8
  import android.os.Bundle
@@ -11,6 +12,8 @@ import android.webkit.*
11
12
  import android.widget.ProgressBar
12
13
  import androidx.webkit.WebViewAssetLoader
13
14
  import io.yourname.androidproject.WebCacheManager
15
+ import io.yourname.androidproject.isUrlAllowed
16
+ import io.yourname.androidproject.isExternalDomain
14
17
  import kotlinx.coroutines.*
15
18
  import java.util.Properties
16
19
 
@@ -34,6 +37,8 @@ class CustomWebView(
34
37
  private var isInitialPageLoaded: Boolean = false
35
38
  private var buildOptimisation: Boolean = false // Added property for build optimization
36
39
  private lateinit var assetLoader: WebViewAssetLoader
40
+ private var allowedUrls: List<String> = emptyList()
41
+ private var accessControlEnabled: Boolean = false
37
42
 
38
43
  // Counters for asset loading statistics
39
44
  private var assetLoadAttempts = 0
@@ -56,6 +61,13 @@ class CustomWebView(
56
61
  // Parse buildOptimisation property
57
62
  buildOptimisation = properties.getProperty("buildOptimisation", "false").toBoolean()
58
63
 
64
+ // Load access control settings from properties
65
+ accessControlEnabled = properties.getProperty("accessControl.enabled", "false").toBoolean()
66
+ allowedUrls = properties.getProperty("accessControl.allowedUrls", "")
67
+ .split(",")
68
+ .map { it.trim() }
69
+ .filter { it.isNotEmpty() }
70
+
59
71
  // Set initial flags based on buildOptimisation
60
72
  if (buildOptimisation) {
61
73
  isInitialApiCalled = false
@@ -71,6 +83,8 @@ class CustomWebView(
71
83
  Log.d(TAG, "Cache Pattern: $cachePatterns")
72
84
  Log.d(TAG, "API Base URL: $apiBaseUrl")
73
85
  Log.d(TAG, "Build Optimisation: $buildOptimisation")
86
+ Log.d(TAG, "Access Control Enabled: $accessControlEnabled")
87
+ Log.d(TAG, "Allowed URLs: $allowedUrls")
74
88
  Log.d(TAG, "Initial API Called: $isInitialApiCalled")
75
89
  Log.d(TAG, "Initial Page Loaded: $isInitialPageLoaded")
76
90
  }
@@ -198,6 +212,22 @@ class CustomWebView(
198
212
  }
199
213
  }
200
214
 
215
+
216
+
217
+ private fun openInInAppBrowser(url: String) {
218
+ try {
219
+ val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
220
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
221
+ context.startActivity(intent)
222
+ if (BuildConfig.DEBUG) {
223
+ Log.d(TAG, "๐ŸŒ Opening external URL in in-app browser: $url")
224
+ }
225
+ } catch (e: Exception) {
226
+ Log.e(TAG, "โŒ Failed to open URL in in-app browser: $url", e)
227
+ }
228
+ }
229
+
230
+
201
231
  private fun isApiCall(url: String): Boolean {
202
232
  // Check if URL is an API call based on your API base URL
203
233
  return apiBaseUrl.isNotEmpty() && url.startsWith(apiBaseUrl)
@@ -290,6 +320,27 @@ class CustomWebView(
290
320
  webView.webViewClient = object : WebViewClient() {
291
321
  override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
292
322
  request?.url?.let { url ->
323
+ val urlString = url.toString()
324
+
325
+ if (accessControlEnabled) {
326
+ // Check if URL is an external domain
327
+ if (url.scheme in listOf("http", "https") && isExternalDomain(urlString, allowedUrls)) {
328
+ if (BuildConfig.DEBUG) {
329
+ Log.d(TAG, "๐ŸŒ External domain detected, opening in in-app browser: $urlString")
330
+ }
331
+ openInInAppBrowser(urlString)
332
+ return true
333
+ }
334
+
335
+ // Check if URL is allowed for internal navigation
336
+ if (!isUrlAllowed(urlString, allowedUrls)) {
337
+ if (BuildConfig.DEBUG) {
338
+ Log.w(TAG, "๐Ÿšซ URL blocked by access control: $urlString")
339
+ }
340
+ return true
341
+ }
342
+ }
343
+
293
344
  // Let WebView handle loading non-API HTTP/HTTPS URLs
294
345
  if (url.scheme in listOf("http", "https")) {
295
346
  return false
@@ -307,6 +358,14 @@ class CustomWebView(
307
358
  Log.d(TAG, "๐Ÿ”„ Intercepting request for: $url on thread: ${Thread.currentThread().name}")
308
359
  }
309
360
 
361
+ if (accessControlEnabled && !isUrlAllowed(url, allowedUrls)) {
362
+ if (BuildConfig.DEBUG) {
363
+ Log.w(TAG, "๐Ÿšซ Network request blocked by access control: $url")
364
+ }
365
+ // Return an empty response to block the request
366
+ return WebResourceResponse("text/plain", "utf-8", null)
367
+ }
368
+
310
369
  // Handle the initial route request - intercept first request regardless of host
311
370
  if (!isInitialApiCalled && request.method == "GET") {
312
371
  isInitialApiCalled = true
@@ -10,6 +10,7 @@ import io.yourname.androidproject.NativeBridge
10
10
  import kotlinx.coroutines.CoroutineScope
11
11
  import kotlinx.coroutines.MainScope
12
12
  import kotlinx.coroutines.cancelChildren
13
+ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
13
14
 
14
15
  class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
15
16
 
@@ -20,6 +21,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
20
21
  private lateinit var properties: Properties
21
22
  private var isHardwareAccelerationEnabled = false
22
23
  private var currentUrl: String = ""
24
+ private var splashStartTime: Long = 0
23
25
 
24
26
  private fun enableHardwareAcceleration() {
25
27
  if (!isHardwareAccelerationEnabled) {
@@ -45,6 +47,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
45
47
  }
46
48
 
47
49
  override fun onCreate(savedInstanceState: Bundle?) {
50
+ val splashScreen = installSplashScreen()
48
51
  super.onCreate(savedInstanceState)
49
52
  if (BuildConfig.DEBUG) {
50
53
  Log.d(TAG, "๐Ÿ“ฑ onCreate started on thread: ${Thread.currentThread().name}")
@@ -63,6 +66,10 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
63
66
  properties.setProperty("buildOptimisation", (!BuildConfig.DEBUG).toString())
64
67
  }
65
68
 
69
+ // Configure splash screen
70
+ splashStartTime = System.currentTimeMillis()
71
+ configureSplashScreen(splashScreen)
72
+
66
73
  // Setup UI
67
74
  binding = ActivityMainBinding.inflate(layoutInflater)
68
75
  supportActionBar?.hide()
@@ -108,7 +115,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
108
115
  // Use the local development server in debug mode
109
116
  val local_ip = properties.getProperty("LOCAL_IP", "localhost")
110
117
  val port = properties.getProperty("port", "3005")
111
- currentUrl = "http://$local_ip:$port"
118
+ val useHttps = properties.getProperty("useHttps", "false").toBoolean()
119
+ val protocol = if (useHttps) "https" else "http"
120
+ currentUrl = "$protocol://$local_ip:$port"
112
121
  } else {
113
122
  // In production, use the configured production URL or fallback to a file:// URL
114
123
  currentUrl = properties.getProperty("PRODUCTION_URL", "")
@@ -159,4 +168,21 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
159
168
  customWebView.destroy()
160
169
  super.onDestroy()
161
170
  }
171
+
172
+ private fun configureSplashScreen(splashScreen: androidx.core.splashscreen.SplashScreen) {
173
+ val durationProperty = properties.getProperty("splashScreen.duration")
174
+
175
+ splashScreen.setKeepOnScreenCondition {
176
+ val webViewLoaded = ::customWebView.isInitialized &&
177
+ customWebView.getWebView().progress >= 100
178
+
179
+ if (durationProperty != null) {
180
+ val duration = durationProperty.toLong()
181
+ val timeElapsed = System.currentTimeMillis() - splashStartTime >= duration
182
+ !timeElapsed
183
+ } else {
184
+ !webViewLoaded
185
+ }
186
+ }
187
+ }
162
188
  }
@@ -132,6 +132,17 @@ class NativeBridge(private val mainActivity: MainActivity, private val webView:
132
132
  }
133
133
  }
134
134
 
135
+ @JavascriptInterface
136
+ fun getDeviceInfo(options: String?) {
137
+ BridgeUtils.safeExecute(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_ERROR, "get device info") {
138
+ mainActivity.runOnUiThread {
139
+ val deviceInfo = DeviceInfoUtils.getDeviceInfo(mainActivity)
140
+ BridgeUtils.logDebug(TAG, "Device info retrieved: $deviceInfo")
141
+ BridgeUtils.notifyWeb(webView, BridgeUtils.WebEvents.ON_DEVICE_INFO_SUCCESS, deviceInfo.toString())
142
+ }
143
+ }
144
+ }
145
+
135
146
  private fun initializeCameraLauncher() {
136
147
  cameraLauncher = mainActivity.registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
137
148
  CameraUtils.processCameraResult(mainActivity, webView, currentPhotoUri, success)
@@ -48,6 +48,10 @@ object BridgeUtils {
48
48
  ON_FRAMEWORK_SERVER_READY("ON_FRAMEWORK_SERVER_READY"),
49
49
  ON_FRAMEWORK_SERVER_ERROR("ON_FRAMEWORK_SERVER_ERROR"),
50
50
  ON_FRAMEWORK_SERVER_STOPPED("ON_FRAMEWORK_SERVER_STOPPED"),
51
+
52
+ // Device info events
53
+ ON_DEVICE_INFO_SUCCESS("ON_DEVICE_INFO_SUCCESS"),
54
+ ON_DEVICE_INFO_ERROR("ON_DEVICE_INFO_ERROR"),
51
55
  }
52
56
 
53
57
  /**
@@ -0,0 +1,60 @@
1
+ package io.yourname.androidproject.utils
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.util.DisplayMetrics
6
+ import android.view.WindowManager
7
+ import org.json.JSONObject
8
+ import io.yourname.androidproject.BuildConfig
9
+ import android.hardware.display.DisplayManager
10
+
11
+ object DeviceInfoUtils {
12
+ private const val TAG = "DeviceInfoUtils"
13
+
14
+ fun getDeviceInfo(context: Context): JSONObject {
15
+ val deviceInfo = JSONObject()
16
+
17
+ try {
18
+ // Basic device information
19
+ deviceInfo.apply {
20
+ put("model", Build.MODEL)
21
+ put("manufacturer", Build.MANUFACTURER)
22
+ put("platform", "android")
23
+ }
24
+
25
+ // Screen dimensions only
26
+ val displayMetrics = getDisplayMetrics(context)
27
+ deviceInfo.apply {
28
+ put("screenWidth", displayMetrics.widthPixels)
29
+ put("screenHeight", displayMetrics.heightPixels)
30
+ put("screenDensity", displayMetrics.density.toDouble())
31
+ }
32
+
33
+ } catch (e: Exception) {
34
+ BridgeUtils.logError(TAG, "Error getting device info", e)
35
+ return JSONObject().apply {
36
+ put("error", "Failed to get device info: ${e.message}")
37
+ }
38
+ }
39
+
40
+ return deviceInfo
41
+ }
42
+
43
+ private fun getDisplayMetrics(context: Context): DisplayMetrics {
44
+ val displayMetrics = DisplayMetrics()
45
+
46
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
47
+ // Use modern Display API for Android 11+
48
+ val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
49
+ displayManager.getDisplay(0)?.getMetrics(displayMetrics)
50
+ } else {
51
+ // Fallback to deprecated API for older versions
52
+ @Suppress("DEPRECATION")
53
+ val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
54
+ @Suppress("DEPRECATION")
55
+ windowManager.defaultDisplay.getMetrics(displayMetrics)
56
+ }
57
+
58
+ return displayMetrics
59
+ }
60
+ }
@@ -1,3 +1,3 @@
1
1
  <resources>
2
- <string name="app_name">My Application</string>
2
+ <string name="app_name">Catalyst Application</string>
3
3
  </resources>
@@ -31,5 +31,5 @@ dependencyResolutionManagement {
31
31
  }
32
32
 
33
33
 
34
- rootProject.name = "My Application"
34
+ rootProject.name = "Catalyst Application"
35
35
  include(":app")
@@ -1,14 +1,14 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.default=void 0;var _NativeInterfaces=require("./constants/NativeInterfaces.js");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"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e};}class WebBridge{constructor(){this.handlers=new Map();this.initialized=false;this._logInitialization();}/**
2
2
  * Log initialization info
3
- */_logInitialization(){console.log("๐ŸŒ‰ WebBridge initialized");console.log("๐ŸŒ‰ Available callback interfaces:",_NativeInterfaces.ALL_CALLBACKS.length);if(process.env.NODE_ENV==='development'){(0,_NativeInterfaces.debugInterfaces)();}}/**
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
5
- */static init=()=>{if(typeof window==='undefined'){console.error("๐ŸŒ‰ WebBridge cannot be initialized outside the browser!");return null;}if(window.WebBridge){console.warn("๐ŸŒ‰ WebBridge already initialized!");return window.WebBridge;}const bridge=new WebBridge();window.WebBridge=bridge;bridge.initialized=true;console.log("๐ŸŒ‰ WebBridge created and attached to window");return bridge;};/**
5
+ */static init=()=>{if(typeof window==="undefined"){console.error("๐ŸŒ‰ WebBridge cannot be initialized outside the browser!");return null;}if(window.WebBridge){console.warn("๐ŸŒ‰ WebBridge already initialized!");return window.WebBridge;}const bridge=new WebBridge();window.WebBridge=bridge;bridge.initialized=true;const{platform}=_NativeBridge.default.getEnvironmentInfo();console.log("๐ŸŒ‰ WebBridge created and attached to window");return{bridge,platform,getDeviceInfo:bridge.getDeviceInfo};};/**
6
6
  * Main callback handler called by native platforms
7
7
  * @param {string} interfaceName - The callback interface name
8
8
  * @param {any} data - Data from native platform
9
- */callback=(interfaceName,data)=>{console.log(`๐ŸŒ‰ WebBridge callback: ${interfaceName}`,data?{data}:'');// Validate interface
9
+ */callback=(interfaceName,data)=>{console.log(`๐ŸŒ‰ WebBridge callback: ${interfaceName}`,data?{data}:"");// Validate interface
10
10
  if(!(0,_NativeInterfaces.isValidCallback)(interfaceName)){console.error(`๐ŸŒ‰ Invalid callback interface: ${interfaceName}`);console.log("๐ŸŒ‰ Available callbacks:",_NativeInterfaces.ALL_CALLBACKS);return;}if(!this.handlers.has(interfaceName)){console.warn(`๐ŸŒ‰ No handler registered for interface: ${interfaceName}`);return;}try{const handler=this.handlers.get(interfaceName);handler(data);}catch(error){console.error(`๐ŸŒ‰ Error executing callback for ${interfaceName}:`,error);// In development, provide more helpful error info
11
- if(process.env.NODE_ENV==='development'){console.error('๐ŸŒ‰ Handler details:',{interfaceName,data,handlerType:typeof this.handlers.get(interfaceName)});}}};/**
11
+ if(process.env.NODE_ENV==="development"){console.error("๐ŸŒ‰ Handler details:",{interfaceName,data,handlerType:typeof this.handlers.get(interfaceName)});}}};/**
12
12
  * Register a callback handler for a native interface
13
13
  * @param {string} interfaceName - The callback interface name (from NATIVE_CALLBACKS)
14
14
  * @param {function} callback - The callback function to execute
@@ -39,4 +39,8 @@ if(this.handlers.has(interfaceName)){console.warn(`๐ŸŒ‰ Interface ${interfaceNam
39
39
  * Test callback functionality (for debugging)
40
40
  * @param {string} interfaceName - The callback interface to test
41
41
  * @param {any} testData - Test data to pass
42
- */testCallback=(interfaceName,testData='test')=>{if(process.env.NODE_ENV!=='development'){console.warn('๐ŸŒ‰ testCallback is only available in development mode');return;}console.log(`๐ŸŒ‰ Testing callback: ${interfaceName}`);this.callback(interfaceName,testData);};}var _default=exports.default=WebBridge;
42
+ */testCallback=(interfaceName,testData="test")=>{if(process.env.NODE_ENV!=="development"){console.warn("๐ŸŒ‰ testCallback is only available in development mode");return;}console.log(`๐ŸŒ‰ Testing callback: ${interfaceName}`);this.callback(interfaceName,testData);};/**
43
+ * Get device information
44
+ * @returns {Promise<Object>} - Promise that resolves with device info or rejects with error
45
+ */getDeviceInfo=()=>{// Key mapping from native to web keys
46
+ const DEVICE_INFO_KEY_MAP={android:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity"},ios:{model:"model",manufacturer:"manufacturer",platform:"platform",screenWidth:"screenWidth",screenHeight:"screenHeight",screenDensity:"screenDensity"}};const parseDeviceInfo=data=>{const rawDeviceInfo=JSON.parse(data);const{platform}=_NativeBridge.default.getEnvironmentInfo();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;
@@ -2,21 +2,23 @@
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
6
- PICK_FILE:'pickFile',OPEN_FILE_WITH_INTENT:'openFileWithIntent',// Haptic feedback commands
7
- REQUEST_HAPTIC_FEEDBACK:'requestHapticFeedback'};// Callback Interfaces: Native โ†’ Web (incoming events/callbacks)
5
+ OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",// File commands
6
+ PICK_FILE:"pickFile",OPEN_FILE_WITH_INTENT:"openFileWithIntent",// Haptic feedback commands
7
+ REQUEST_HAPTIC_FEEDBACK:"requestHapticFeedback",// Device info commands
8
+ GET_DEVICE_INFO:"getDeviceInfo"};// Callback Interfaces: Native โ†’ Web (incoming events/callbacks)
8
9
  const NATIVE_CALLBACKS=exports.NATIVE_CALLBACKS={// Camera callbacks
9
- CAMERA_PERMISSION_STATUS:'CAMERA_PERMISSION_STATUS',ON_CAMERA_CAPTURE:'ON_CAMERA_CAPTURE',ON_CAMERA_ERROR:'ON_CAMERA_ERROR',// File picker callbacks
10
- 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
11
- ON_INTENT_SUCCESS:'ON_INTENT_SUCCESS',ON_INTENT_ERROR:'ON_INTENT_ERROR',ON_INTENT_CANCELLED:'ON_INTENT_CANCELLED',// Haptic feedback callbacks
12
- HAPTIC_FEEDBACK:'HAPTIC_FEEDBACK'};// Interface Categories for easier management
13
- 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]}};// All available interfaces (for validation)
10
+ CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",// File picker callbacks
11
+ 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
12
+ ON_INTENT_SUCCESS:"ON_INTENT_SUCCESS",ON_INTENT_ERROR:"ON_INTENT_ERROR",ON_INTENT_CANCELLED:"ON_INTENT_CANCELLED",// Haptic feedback callbacks
13
+ HAPTIC_FEEDBACK:"HAPTIC_FEEDBACK",// Device info callbacks
14
+ ON_DEVICE_INFO_SUCCESS:"ON_DEVICE_INFO_SUCCESS",ON_DEVICE_INFO_ERROR:"ON_DEVICE_INFO_ERROR"};// Interface Categories for easier management
15
+ 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]},DEVICE_INFO:{commands:[NATIVE_COMMANDS.GET_DEVICE_INFO],callbacks:[NATIVE_CALLBACKS.ON_DEVICE_INFO_SUCCESS,NATIVE_CALLBACKS.ON_DEVICE_INFO_ERROR]}};// All available interfaces (for validation)
14
16
  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
15
- const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:'GRANTED',DENIED:'DENIED',NOT_DETERMINED:'NOT_DETERMINED',RESTRICTED:'RESTRICTED'};// Haptic feedback types
16
- const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={LIGHT:'light',MEDIUM:'medium',HEAVY:'heavy',SUCCESS:'success',WARNING:'warning',ERROR:'error'};// File picker state constants
17
- const FILE_PICKER_STATES=exports.FILE_PICKER_STATES={OPENING:'opening',PROCESSING:'processing',IDLE:null};// Intent processing states
18
- const INTENT_STATES=exports.INTENT_STATES={PROCESSING:'processing',DOWNLOADING:'downloading',OPENING:'opening',IDLE:null};// Response status constants
19
- const RESPONSE_STATUS=exports.RESPONSE_STATUS={SUCCESS:'SUCCESS',ERROR:'ERROR',CANCELLED:'CANCELLED'};// Utility functions for interface validation
17
+ const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:"GRANTED",DENIED:"DENIED",NOT_DETERMINED:"NOT_DETERMINED",RESTRICTED:"RESTRICTED"};// Haptic feedback types
18
+ const HAPTIC_FEEDBACK_TYPES=exports.HAPTIC_FEEDBACK_TYPES={LIGHT:"light",MEDIUM:"medium",HEAVY:"heavy",SUCCESS:"success",WARNING:"warning",ERROR:"error"};// File picker state constants
19
+ const FILE_PICKER_STATES=exports.FILE_PICKER_STATES={OPENING:"opening",PROCESSING:"processing",IDLE:null};// Intent processing states
20
+ const INTENT_STATES=exports.INTENT_STATES={PROCESSING:"processing",DOWNLOADING:"downloading",OPENING:"opening",IDLE:null};// Response status constants
21
+ const RESPONSE_STATUS=exports.RESPONSE_STATUS={SUCCESS:"SUCCESS",ERROR:"ERROR",CANCELLED:"CANCELLED"};// Utility functions for interface validation
20
22
  const isValidCommand=command=>ALL_COMMANDS.includes(command);exports.isValidCommand=isValidCommand;const isValidCallback=callback=>ALL_CALLBACKS.includes(callback);exports.isValidCallback=isValidCallback;const isValidInterface=interfaceName=>ALL_INTERFACES.includes(interfaceName);// Get interfaces by category
21
23
  exports.isValidInterface=isValidInterface;const getInterfacesByCategory=category=>{const categoryData=INTERFACE_CATEGORIES[category.toUpperCase()];if(!categoryData){throw new Error(`Invalid category: ${category}`);}return categoryData;};// Debug helper to list all interfaces
22
- exports.getInterfacesByCategory=getInterfacesByCategory;const debugInterfaces=()=>{console.group('๐ŸŒ‰ Native Bridge Interfaces');console.log('Commands:',ALL_COMMANDS);console.log('Callbacks:',ALL_CALLBACKS);console.log('Categories:',Object.keys(INTERFACE_CATEGORIES));console.groupEnd();};exports.debugInterfaces=debugInterfaces;var _default=exports.default={NATIVE_COMMANDS,NATIVE_CALLBACKS,INTERFACE_CATEGORIES,ALL_COMMANDS,ALL_CALLBACKS,ALL_INTERFACES,PERMISSION_STATUS,HAPTIC_FEEDBACK_TYPES,FILE_PICKER_STATES,INTENT_STATES,RESPONSE_STATUS,isValidCommand,isValidCallback,isValidInterface,getInterfacesByCategory,debugInterfaces};
24
+ exports.getInterfacesByCategory=getInterfacesByCategory;const debugInterfaces=()=>{console.group("๐ŸŒ‰ Native Bridge Interfaces");console.log("Commands:",ALL_COMMANDS);console.log("Callbacks:",ALL_CALLBACKS);console.log("Categories:",Object.keys(INTERFACE_CATEGORIES));console.groupEnd();};exports.debugInterfaces=debugInterfaces;var _default=exports.default={NATIVE_COMMANDS,NATIVE_CALLBACKS,INTERFACE_CATEGORIES,ALL_COMMANDS,ALL_CALLBACKS,ALL_INTERFACES,PERMISSION_STATUS,HAPTIC_FEEDBACK_TYPES,FILE_PICKER_STATES,INTENT_STATES,RESPONSE_STATUS,isValidCommand,isValidCallback,isValidInterface,getInterfacesByCategory,debugInterfaces};