catalyst-core 0.0.3-canary.8 → 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.
package/changelog.md CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## [0.0.3-canary.8] - 2025-01-06
4
10
 
5
11
  - **App icon configuration**
@@ -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,5 +1,10 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <project version="4">
3
3
  <component name="ExternalStorageConfigurationManager" enabled="true" />
4
- <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>
5
10
  </project>
@@ -38,6 +38,7 @@ class CustomWebView(
38
38
  private var buildOptimisation: Boolean = false // Added property for build optimization
39
39
  private lateinit var assetLoader: WebViewAssetLoader
40
40
  private var allowedUrls: List<String> = emptyList()
41
+ private var accessControlEnabled: Boolean = false
41
42
 
42
43
  // Counters for asset loading statistics
43
44
  private var assetLoadAttempts = 0
@@ -60,7 +61,8 @@ class CustomWebView(
60
61
  // Parse buildOptimisation property
61
62
  buildOptimisation = properties.getProperty("buildOptimisation", "false").toBoolean()
62
63
 
63
- // Load allowed URLs from properties
64
+ // Load access control settings from properties
65
+ accessControlEnabled = properties.getProperty("accessControl.enabled", "false").toBoolean()
64
66
  allowedUrls = properties.getProperty("accessControl.allowedUrls", "")
65
67
  .split(",")
66
68
  .map { it.trim() }
@@ -81,6 +83,7 @@ class CustomWebView(
81
83
  Log.d(TAG, "Cache Pattern: $cachePatterns")
82
84
  Log.d(TAG, "API Base URL: $apiBaseUrl")
83
85
  Log.d(TAG, "Build Optimisation: $buildOptimisation")
86
+ Log.d(TAG, "Access Control Enabled: $accessControlEnabled")
84
87
  Log.d(TAG, "Allowed URLs: $allowedUrls")
85
88
  Log.d(TAG, "Initial API Called: $isInitialApiCalled")
86
89
  Log.d(TAG, "Initial Page Loaded: $isInitialPageLoaded")
@@ -319,21 +322,23 @@ class CustomWebView(
319
322
  request?.url?.let { url ->
320
323
  val urlString = url.toString()
321
324
 
322
- // Check if URL is an external domain
323
- if (url.scheme in listOf("http", "https") && isExternalDomain(urlString, allowedUrls)) {
324
- if (BuildConfig.DEBUG) {
325
- Log.d(TAG, "🌍 External domain detected, opening in in-app browser: $urlString")
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
326
333
  }
327
- openInInAppBrowser(urlString)
328
- return true
329
- }
330
-
331
- // Check if URL is allowed for internal navigation
332
- if (!isUrlAllowed(urlString, allowedUrls)) {
333
- if (BuildConfig.DEBUG) {
334
- Log.w(TAG, "🚫 URL blocked by access control: $urlString")
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
335
341
  }
336
- return true
337
342
  }
338
343
 
339
344
  // Let WebView handle loading non-API HTTP/HTTPS URLs
@@ -353,7 +358,7 @@ class CustomWebView(
353
358
  Log.d(TAG, "🔄 Intercepting request for: $url on thread: ${Thread.currentThread().name}")
354
359
  }
355
360
 
356
- if (!isUrlAllowed(url, allowedUrls)) {
361
+ if (accessControlEnabled && !isUrlAllowed(url, allowedUrls)) {
357
362
  if (BuildConfig.DEBUG) {
358
363
  Log.w(TAG, "🚫 Network request blocked by access control: $url")
359
364
  }
@@ -115,7 +115,9 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
115
115
  // Use the local development server in debug mode
116
116
  val local_ip = properties.getProperty("LOCAL_IP", "localhost")
117
117
  val port = properties.getProperty("port", "3005")
118
- 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"
119
121
  } else {
120
122
  // In production, use the configured production URL or fallback to a file:// URL
121
123
  currentUrl = properties.getProperty("PRODUCTION_URL", "")
@@ -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,8 +1,8 @@
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
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
@@ -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;
@@ -4,13 +4,15 @@
4
4
  const NATIVE_COMMANDS=exports.NATIVE_COMMANDS={// Camera commands
5
5
  OPEN_CAMERA:"openCamera",REQUEST_CAMERA_PERMISSION:"requestCameraPermission",// File commands
6
6
  PICK_FILE:"pickFile",OPEN_FILE_WITH_INTENT:"openFileWithIntent",// Haptic feedback commands
7
- REQUEST_HAPTIC_FEEDBACK:"requestHapticFeedback"};// Callback Interfaces: Native → Web (incoming events/callbacks)
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
10
  CAMERA_PERMISSION_STATUS:"CAMERA_PERMISSION_STATUS",ON_CAMERA_CAPTURE:"ON_CAMERA_CAPTURE",ON_CAMERA_ERROR:"ON_CAMERA_ERROR",// File picker callbacks
10
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
11
12
  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)
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
17
  const PERMISSION_STATUS=exports.PERMISSION_STATUS={GRANTED:"GRANTED",DENIED:"DENIED",NOT_DETERMINED:"NOT_DETERMINED",RESTRICTED:"RESTRICTED"};// Haptic feedback types
16
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
@@ -46,6 +46,11 @@ if(process.env.NODE_ENV==="development"){console.warn(`🌉 Development mode: Na
46
46
  * Request haptic feedback
47
47
  * @param {string} feedbackType - Type of haptic feedback
48
48
  */feedback:(feedbackType="light")=>this.call(_NativeInterfaces.NATIVE_COMMANDS.REQUEST_HAPTIC_FEEDBACK,feedbackType)};/**
49
+ * Device info methods
50
+ */device={/**
51
+ * Get device information
52
+ * @returns {Promise} - Returns device info through callback
53
+ */getDeviceInfo:()=>this.call(_NativeInterfaces.NATIVE_COMMANDS.GET_DEVICE_INFO)};/**
49
54
  * Get environment info
50
55
  */getEnvironmentInfo(){return{isAndroid:this.isAndroid,isIOS:this.isIOS,isNativeEnvironment:this.isNativeEnvironment,platform:this.isAndroid?"android":this.isIOS?"ios":"web"};}/**
51
56
  * Check if native bridge is available
@@ -43,7 +43,9 @@ if(!iconImageFound){progress.log("No custom app icon found (icon.png/jpg/jpeg),
43
43
  if(manifestContent.includes('android:icon="@drawable/icon"')){manifestContent=manifestContent.replace(/android:icon="@drawable\/icon"/,'android:icon="@mipmap/ic_launcher"');manifestContent=manifestContent.replace(/android:roundIcon="@drawable\/icon"/,'android:roundIcon="@mipmap/ic_launcher_round"');_fs.default.writeFileSync(manifestPath,manifestContent);progress.log("Reverted AndroidManifest.xml to use default launcher icon","info");}}return;}// Update AndroidManifest.xml if it exists
44
44
  if(_fs.default.existsSync(manifestPath)){let manifestContent=_fs.default.readFileSync(manifestPath,"utf8");// Look for the application tag and update the icon reference
45
45
  const applicationTagRegex=/<application([^>]*android:icon="[^"]*"[^>]*>)/;const iconAttributeRegex=/android:icon="[^"]*"/;const roundIconAttributeRegex=/android:roundIcon="[^"]*"/;if(applicationTagRegex.test(manifestContent)){// Replace existing icon attribute
46
- manifestContent=manifestContent.replace(iconAttributeRegex,'android:icon="@drawable/icon"');manifestContent=manifestContent.replace(roundIconAttributeRegex,'android:roundIcon="@drawable/icon"');progress.log("Updated app icon reference in AndroidManifest.xml","success");}_fs.default.writeFileSync(manifestPath,manifestContent);}progress.log("App icon assets processed successfully!","success");}catch(error){progress.log(`Warning: Error copying app icon assets: ${error.message}`,"warning");}}// Legacy function for backward compatibility
46
+ manifestContent=manifestContent.replace(iconAttributeRegex,'android:icon="@drawable/icon"');manifestContent=manifestContent.replace(roundIconAttributeRegex,'android:roundIcon="@drawable/icon"');progress.log("Updated app icon reference in AndroidManifest.xml","success");}_fs.default.writeFileSync(manifestPath,manifestContent);}progress.log("App icon assets processed successfully!","success");}catch(error){progress.log(`Warning: Error copying app icon assets: ${error.message}`,"warning");}}async function configureAppName(androidConfig){try{const destPath=`${pwd}/androidProject/app/src/main/res`;const stringsFile=`${destPath}/values/strings.xml`;let stringsContent=_fs.default.readFileSync(stringsFile,"utf8");// Check if androidConfig.appName exists
47
+ if(androidConfig.appName){stringsContent=stringsContent.replace(/<string name="app_name">.*?<\/string>/,`<string name="app_name">${androidConfig.appName}</string>`);_fs.default.writeFileSync(stringsFile,stringsContent);progress.log(`App display name configured: ${androidConfig.appName}`,"success");}else{// No appName configured, revert to default
48
+ stringsContent=stringsContent.replace(/<string name="app_name">.*?<\/string>/,`<string name="app_name">Catalyst Application</string>`);_fs.default.writeFileSync(stringsFile,stringsContent);progress.log("App display name reverted to default: Catalyst Application","info");}}catch(error){progress.log(`Warning: Error configuring app name: ${error.message}`,"warning");}}// Legacy function for backward compatibility
47
49
  async function installApp(ADB_PATH,androidConfig,buildOptimisation,buildType="debug"){await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType);await launchApp(ADB_PATH,buildType,{type:"emulator"});}async function createAABConfig(androidConfig){// Create AAB configuration based on WebView config with defaults applied
48
50
  const aabConfig={// Apply defaults here in buildAppAndroid.js
49
51
  projectPath:androidConfig.projectPath||DEFAULT_PROJECT_PATH,deploymentPath:DEFAULT_DEPLOYMENT_PATH,oldProjectName:DEFAULT_OLD_PROJECT_NAME,overwriteExisting:androidConfig.overwriteExisting!==undefined?androidConfig.overwriteExisting:DEFAULT_OVERWRITE_EXISTING,// Required field - use configured value or derive from other config
@@ -63,7 +65,7 @@ const canInstallOnPhysical=await testPhysicalDeviceInstallation(ADB_PATH,physica
63
65
  targetDevice={type:"physical",id:physicalDevice.id,model:physicalDevice.model};progress.log(`Using physical device: ${physicalDevice.model}`,"success");}else{// Physical device failed, fallback to emulator
64
66
  progress.log("Physical device installation test failed, falling back to emulator","warning");targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}}else{// No physical device, use emulator (current behavior)
65
67
  targetDevice=await handleEmulatorSetup(ADB_PATH,EMULATOR_PATH,androidConfig);}progress.complete("emulator");}else{progress.log("Skipping device setup for release build","info");}// Copy build assets
66
- progress.start("copyAssets");await copyBuildAssets(androidConfig,buildOptimisation);await copySplashscreenAssets();await copyIconAssets();progress.log(`Build optimization: ${buildOptimisation?"Enabled":"Disabled"}`,"info");progress.complete("copyAssets");// Build based on type
68
+ progress.start("copyAssets");await copyBuildAssets(androidConfig,buildOptimisation);await copySplashscreenAssets();await copyIconAssets();await configureAppName(androidConfig);progress.log(`Build optimization: ${buildOptimisation?"Enabled":"Disabled"}`,"info");progress.complete("copyAssets");// Build based on type
67
69
  if(buildType==="release"){// Build signed AAB for release
68
70
  progress.start("aab");await buildSignedAAB(androidConfig);progress.complete("aab");}else{// Install debug app for development
69
71
  progress.start("build");await buildApp(ADB_PATH,androidConfig,buildOptimisation,buildType,targetDevice);await launchApp(ADB_PATH,buildType,targetDevice);progress.complete("build");}// Print build summary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-core",
3
- "version": "0.0.3-canary.8",
3
+ "version": "0.0.3-canary.9",
4
4
  "main": "index.js",
5
5
  "description": "Web framework that provides great performance out of the box",
6
6
  "bin": {