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 +6 -0
- package/dist/native/androidProject/.idea/gradle.xml +0 -1
- package/dist/native/androidProject/.idea/misc.xml +6 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +20 -15
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +3 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +11 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt +4 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/DeviceInfoUtils.kt +60 -0
- package/dist/native/androidProject/app/src/main/res/values/strings.xml +1 -1
- package/dist/native/androidProject/settings.gradle.kts +1 -1
- package/dist/native/bridge/WebBridge.js +7 -3
- package/dist/native/bridge/constants/NativeInterfaces.js +5 -3
- package/dist/native/bridge/utils/NativeBridge.js +5 -0
- package/dist/native/buildAppAndroid.js +4 -2
- package/package.json +1 -1
package/changelog.md
CHANGED
|
@@ -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>
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt
CHANGED
|
@@ -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
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
if (
|
|
325
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
}
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt
CHANGED
|
@@ -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
|
-
|
|
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", "")
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt
CHANGED
|
@@ -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)
|
package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/BridgeUtils.kt
CHANGED
|
@@ -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,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
|
|
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);}
|
|
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"
|
|
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"
|
|
13
|
-
|
|
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");}}
|
|
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
|