Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 142 additions & 90 deletions config.xml
Original file line number Diff line number Diff line change
@@ -1,90 +1,142 @@
<?xml version='1.0' encoding='utf-8'?>
<widget id="com.foxdebug.acode" android-versionCode="1001" version="1.12.6"
xmlns="http://www.w3.org/ns/widgets"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Acode</name>
<description>
Light weight code editor and web IDE for android.
</description>
<author email="ajit@foxdebug.com" href="https://foxdebug.com">
Foxdebug
</author>
<content src="index.html" />
<access launch-external="yes" origin="*" />
<allow-navigation href="https://*/*" />
<allow-navigation href="http://*/*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />

<platform name="android">
<allow-intent href="market:*" />
<preference name="fullscreen" value="false" />
<preference name="SplashScreen" value="none" />
<preference name="ShowTitle" value="true" />
<preference name="DisallowOverscroll" value="true" />
<preference name="BackgroundColor" value="0xFF313131" />
<preference name="AndroidWindowSplashScreenBackground" value="@color/ic_splash_background" />
<preference name="AndroidWindowSplashScreenAnimatedIcon"
value="res/android/drawable/ic_launcher_foreground.xml" />
<preference name="AndroidPostSplashScreenTheme" value="@style/Theme.App.Activity" />
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidLaunchMode" value="singleTask" />
<preference name="prerendered-icon" value="false" />
<preference name="androidxEnabled" value="true" />
<preference name="GradlePluginKotlinEnabled" value="true" />
<preference name="android-targetSdkVersion" value="36" />
<preference name="android-minSdkVersion" value="26" />


<edit-config file="app/src/main/AndroidManifest.xml" mode="merge"
target="/manifest/application">
<application
android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
android:enableOnBackInvokedCallback="false" />
</edit-config>

<edit-config file="app/src/main/AndroidManifest.xml" mode="merge"
target="/manifest/application/activity[@android:name='MainActivity']">
<activity android:resizeableActivity="true" />
</edit-config>

<config-file parent="./application/activity" target="AndroidManifest.xml">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<data android:scheme="file" android:mimeType="*/*" />
<data android:scheme="content" android:mimeType="*/*" />
</intent-filter>
<!-- Allow app to open using url from browser -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="acode" />
</intent-filter>
</config-file>

<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
</config-file>

<hook type="before_prepare" src="hooks/restore-cordova-resources.js" />
<hook type="before_prepare" src="hooks/modify-java-files.js" />
<hook type="after_prepare" src="hooks/post-process.js" />
</platform>
<preference name="AndroidBlacklistSecureSocketProtocols" value="SSLv3,TLSv1" />
</widget>
<?xml version='1.0' encoding='utf-8' ?>
<widget
id="com.foxdebug.acode"
android-versionCode="1001"
version="1.12.6"
xmlns="http://www.w3.org/ns/widgets"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cdv="http://cordova.apache.org/ns/1.0"
>
<name>Acode</name>
<description>
Light weight code editor and web IDE for android.
</description>
<author email="ajit@foxdebug.com" href="https://foxdebug.com">
Foxdebug
</author>
<content src="index.html" />
<access launch-external="yes" origin="*" />
<allow-navigation href="https://*/*" />
<allow-navigation href="http://*/*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />

<platform name="android">
<allow-intent href="market:*" />
<preference name="fullscreen" value="false" />
<preference name="SplashScreen" value="none" />
<preference name="ShowTitle" value="true" />
<preference name="DisallowOverscroll" value="true" />
<preference name="BackgroundColor" value="0xFF313131" />
<preference
name="AndroidWindowSplashScreenBackground"
value="@color/ic_splash_background"
/>
<preference
name="AndroidWindowSplashScreenAnimatedIcon"
value="res/android/drawable/ic_launcher_foreground.xml"
/>
<preference
name="AndroidPostSplashScreenTheme"
value="@style/Theme.App.Activity"
/>
<preference
name="AndroidPersistentFileLocation"
value="Compatibility"
/>
<preference name="AndroidLaunchMode" value="singleTask" />
<preference name="prerendered-icon" value="false" />
<preference name="androidxEnabled" value="true" />
<preference name="GradlePluginKotlinEnabled" value="true" />
<preference name="android-targetSdkVersion" value="36" />
<preference name="android-minSdkVersion" value="26" />


<edit-config
file="app/src/main/AndroidManifest.xml"
mode="merge"
target="/manifest/application"
>
<application
android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"
android:enableOnBackInvokedCallback="false"
/>
</edit-config>

<edit-config
file="app/src/main/AndroidManifest.xml"
mode="merge"
target="/manifest/application/activity[@android:name='MainActivity']"
>
<activity android:resizeableActivity="true" />
</edit-config>

<config-file
parent="./application/activity"
target="AndroidManifest.xml"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<data android:scheme="file" android:mimeType="*/*" />
<data android:scheme="content" android:mimeType="*/*" />
</intent-filter>
<!-- Allow app to open using url from browser -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="acode" />
</intent-filter>
</config-file>

<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name="android.permission.WRITE_MEDIA_STORAGE"
/>
<uses-permission android:name="android.permission.VIBRATE" />
</config-file>

<config-file
target="AndroidManifest.xml"
parent="/manifest/application"
>
<activity-alias
android:name=".MainActivityAlt1"
android:enabled="false"
android:exported="true"
android:icon="@drawable/ic_alt"
android:targetActivity=".MainActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</config-file>

<hook type="before_prepare" src="hooks/restore-cordova-resources.js" />
<hook type="before_prepare" src="hooks/modify-java-files.js" />
<hook type="after_prepare" src="hooks/post-process.js" />
</platform>
<preference
name="AndroidBlacklistSecureSocketProtocols"
value="SSLv3,TLSv1"
/>
</widget>
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@
"cordova-plugin-iap": {},
"com.foxdebug.acode.rk.customtabs": {},
"cordova-plugin-system": {},
"cordova-plugin-advanced-http": {
"ANDROIDBLACKLISTSECURESOCKETPROTOCOLS": "SSLv3,TLSv1"
}
"cordova-plugin-advanced-http": {}
},
"platforms": [
"android"
Expand Down
Binary file added res/android/drawable/ic_alt.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
130 changes: 127 additions & 3 deletions src/plugins/system/android/com/foxdebug/system/System.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,8 @@ public boolean execute(
case "compare-texts":
case "extractAsset":
case "pin-file-shortcut":
case "get-app-icons":
case "set-app-icon":
break;
case "get-configuration":
getConfiguration(callbackContext);
Expand Down Expand Up @@ -574,9 +576,15 @@ public void run() {
case "is-powersave-mode":
isPowerSaveMode(callbackContext);
break;
case "get-app-info":
getAppInfo(callbackContext);
break;
case "get-app-info":
getAppInfo(callbackContext);
break;
case "get-app-icons":
getAppIcons(callbackContext);
break;
case "set-app-icon":
setAppIcon(arg1, callbackContext);
break;
Comment on lines +579 to +587

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The three new/modified case blocks have an extra leading space compared to the surrounding case "pin-file-shortcut" and others (29 spaces vs 28). This creates an inconsistent indentation in an otherwise uniform switch block.

Suggested change
case "get-app-info":
getAppInfo(callbackContext);
break;
case "get-app-icons":
getAppIcons(callbackContext);
break;
case "set-app-icon":
setAppIcon(arg1, callbackContext);
break;
case "get-app-info":
getAppInfo(callbackContext);
break;
case "get-app-icons":
getAppIcons(callbackContext);
break;
case "set-app-icon":
setAppIcon(arg1, callbackContext);
break;

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

case "pin-file-shortcut":
pinFileShortcut(args.optJSONObject(0), callbackContext);
break;
Expand Down Expand Up @@ -2184,4 +2192,120 @@ private void extractAsset(String assetName, String destinationPath, CallbackCont
callback.error(sw.toString());
}
}

private void getAppIcons(CallbackContext callbackContext) {
try {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage(context.getPackageName());

int flags = PackageManager.GET_META_DATA;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
} else {
flags |= PackageManager.GET_DISABLED_COMPONENTS;
}

List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, flags);
JSONArray result = new JSONArray();

for (ResolveInfo info : resolveInfos) {
JSONObject iconInfo = new JSONObject();
String name = info.activityInfo.name;
String label = info.loadLabel(pm).toString();

int iconRes = info.activityInfo.getIconResource();
String iconName = "";
if (iconRes != 0) {
try {
iconName = context.getResources().getResourceEntryName(iconRes);
} catch (Exception e) {
// ignore
}
}

// Check if currently enabled
ComponentName componentName = new ComponentName(context, name);
int enabledSetting = pm.getComponentEnabledSetting(componentName);
boolean isEnabled;
if (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
isEnabled = info.activityInfo.enabled;
} else {
isEnabled = (enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
}

iconInfo.put("name", name);
iconInfo.put("label", label);
iconInfo.put("icon", iconName);
iconInfo.put("enabled", isEnabled);
result.put(iconInfo);
}

callbackContext.success(result);
} catch (Exception e) {
callbackContext.error(e.getMessage());
}
}

private void setAppIcon(String targetComponentName, CallbackContext callbackContext) {
try {
PackageManager pm = context.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setPackage(context.getPackageName());

int flags = PackageManager.GET_META_DATA;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
} else {
flags |= PackageManager.GET_DISABLED_COMPONENTS;
}

List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, flags);

String target = targetComponentName;
if (target.startsWith(".")) {
target = context.getPackageName() + target;
}

boolean found = false;

// First pass: verify if targetComponentName is a valid launcher component
for (ResolveInfo info : resolveInfos) {
if (info.activityInfo.name.equals(target)) {
found = true;
break;
Comment on lines +2261 to +2278

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 setAppIcon disables MainActivity, making the app unlaunchable when an alias is active

The second pass iterates every entry returned by queryIntentActivities(MAIN/LAUNCHER) and disables all components that are not the target. MainActivity itself has a MAIN+LAUNCHER intent-filter (standard Cordova setup), so it appears in this list. When .MainActivityAlt1 is selected, MainActivity gets COMPONENT_ENABLED_STATE_DISABLED. Because an activity-alias always delegates to its targetActivity, Android requires the target to be enabled — a disabled MainActivity means MainActivityAlt1 can no longer launch the app, effectively making it unlaunchable from the home screen.

The standard pattern for icon switching is: (1) remove the MAIN/LAUNCHER intent-filter from MainActivity itself, (2) add a "default" activity-alias for the original icon, and (3) only ever toggle aliases — never touch MainActivity directly with setComponentEnabledSetting.

}
}

if (!found) {
callbackContext.error("Component " + target + " not found as a launcher activity or alias in the manifest");
return;
}

// Second pass: enable targetComponentName and disable others
for (ResolveInfo info : resolveInfos) {
String name = info.activityInfo.name;
ComponentName componentName = new ComponentName(context, name);
if (name.equals(target)) {
pm.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
);
} else {
pm.setComponentEnabledSetting(
componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
);
}
}

callbackContext.success();
} catch (Exception e) {
callbackContext.error(e.getMessage());
}
}
}
Loading