diff --git a/app/src/main/assets/os.json b/app/src/main/assets/os.json
index aa42330..c26c47b 100644
--- a/app/src/main/assets/os.json
+++ b/app/src/main/assets/os.json
@@ -2,13 +2,23 @@
{
"name": "Arch Linux",
"category": "Linux",
+ "subcategory": "Enthusiast",
"description": "A simple, lightweight distribution",
"icon": "archlinux.svg",
"url": "https://archlinux.org/download/"
},
+ {
+ "name": "Gentoo Linux",
+ "category": "Linux",
+ "subcategory": "Enthusiast",
+ "description": "A highly flexible, source-based Linux distribution.",
+ "icon": "gentoo.svg",
+ "url": "https://www.gentoo.org/downloads/"
+ },
{
"name": "Debian",
"category": "Linux",
+ "subcategory": "Server",
"description": "Debian is a complete Free Operating System!",
"icon": "debian.svg",
"url": "https://www.debian.org/download"
@@ -16,6 +26,7 @@
{
"name": "Fedora",
"category": "Linux",
+ "subcategory": "Desktop",
"description": "The Fedora Project is a community of people working together to build a free and open source software platform and to collaborate on and share user-focused solutions built on that platform.",
"icon": "fedora.svg",
"url": "https://www.fedoraproject.org/"
@@ -23,6 +34,7 @@
{
"name": "Linux Mint",
"category": "Linux",
+ "subcategory": "Desktop",
"description": "Linux Mint is an operating system for desktop and laptop computers. It is designed to work 'out of the box' and comes fully equipped with the apps most people need.",
"icon": "linuxmint.svg",
"url": "https://linuxmint.com/download.php"
@@ -30,6 +42,7 @@
{
"name": "NixOS",
"category": "Linux",
+ "subcategory": "Enthusiast",
"description": "Declarative builds and deployments.",
"icon": "nixos.svg",
"url": "https://nixos.org/download/#nixos-iso"
@@ -37,6 +50,7 @@
{
"name": "Pop!_OS",
"category": "Linux",
+ "subcategory": "Desktop",
"description": "Unleash your potential on a Linux operating system made to be productive and personal.",
"icon": "popos.svg",
"url": "https://system76.com/pop/download/"
@@ -44,6 +58,7 @@
{
"name": "Tails",
"category": "Linux",
+ "subcategory": "Security",
"description": "Tails is a portable operating system that protects against surveillance and censorship.",
"icon": "tails.svg",
"url": "https://tails.net/install/download/index.en.html"
@@ -51,6 +66,7 @@
{
"name": "Ubuntu",
"category": "Linux",
+ "subcategory": "Desktop",
"description": "",
"icon": "ubuntu.svg",
"url": "https://ubuntu.com/download"
@@ -58,6 +74,7 @@
{
"name": "FreeBSD",
"category": "BSD",
+ "subcategory": null,
"description": "FreeBSD is an operating system for a variety of platforms which focuses on features, speed, and stability.",
"icon": "freebsd.svg",
"url": "https://www.freebsd.org/where/"
@@ -65,22 +82,121 @@
{
"name": "OPNsense",
"category": "BSD",
+ "subcategory": "Networking",
"description": "OPNsense® is an open source, feature rich firewall and routing platform, offering cutting-edge network protection.",
"icon": "opnsense.svg",
"url": "https://opnsense.org/download/"
},
+ {
+ "name": "pfSense",
+ "category": "BSD",
+ "subcategory": "Networking",
+ "description": "Secure networks start here.™",
+ "icon": "pfsense.svg",
+ "url": "https://www.pfsense.org/download/"
+ },
{
"name": "Windows 11",
"category": "Windows",
+ "subcategory": null,
"description": "",
"icon": "windows11.svg",
"url": "https://www.microsoft.com/en-us/software-download/windows11"
},
+ {
+ "name": "Windows 10",
+ "category": "Windows",
+ "subcategory": null,
+ "description": "",
+ "icon": "windows10.svg",
+ "url": "https://www.microsoft.com/en-us/software-download/windows10ISO"
+ },
{
"name": "Hiren's BootCD PE",
"category": "Recovery",
+ "subcategory": null,
"description": "Hiren's BootCD PE (Preinstallation Environment) is a restored edition of Hiren's BootCD based on Windows PE",
"icon": null,
"url": "https://www.hirensbootcd.org/download/"
+ },
+ {
+ "name": "Clonezilla",
+ "category": "Recovery",
+ "subcategory": null,
+ "description": "Clonezilla is a partition and disk imaging/cloning program.",
+ "icon": null,
+ "url": "https://clonezilla.org/downloads.php"
+ },
+ {
+ "name": "Bazzite",
+ "category": "Linux",
+ "subcategory": "Desktop",
+ "description": "Bazzite makes gaming and everyday use smoother and simpler across desktop PCs, handhelds, tablets, and home theater PCs.",
+ "icon": null,
+ "url": "https://bazzite.gg/#image-picker"
+ },
+ {
+ "name": "Manjaro",
+ "category": "Linux",
+ "subcategory": "Desktop",
+ "description": "Taking the raw power and flexibility of Arch Linux and making it more accessible for a greater audience.",
+ "icon": "manjaro.svg",
+ "url": "https://manjaro.org/products"
+ },
+ {
+ "name": "Super Grub2",
+ "category": "Recovery",
+ "subcategory": null,
+ "description": "Get back to your GNU/Linux & Windows computers !",
+ "icon": null,
+ "url": "https://www.supergrubdisk.org/category/download/"
+ },
+ {
+ "name": "Kali Linux",
+ "category": "Linux",
+ "subcategory": "Security",
+ "description": "The most advanced penetration testing platform ever made.",
+ "icon": "kalilinux.svg",
+ "url": "https://www.kali.org/get-kali/#kali-installer-images"
+ },
+ {
+ "name": "ParrotOS",
+ "category": "Linux",
+ "subcategory": "Security",
+ "description": "The ultimate framework for your Cyber Security operations",
+ "icon": "parrotsecurity.svg",
+ "url": "https://www.parrotsec.org/download/"
+ },
+ {
+ "name": "ProxmoxVE",
+ "category": "Linux",
+ "subcategory": "Server",
+ "description": "Proxmox Virtual Environment is a complete open-source platform for enterprise virtualization.",
+ "icon": "proxmox.svg",
+ "url": "https://www.proxmox.com/en/downloads/proxmox-virtual-environment/iso"
+ },
+ {
+ "name": "Android-x86",
+ "category": "Android",
+ "subcategory": null,
+ "description": "This is a project to port Android Open Source Project to x86 platform",
+ "icon": "android.svg",
+ "url": "https://www.android-x86.org/download"
+ },
+ {
+ "name": "RemixOS",
+ "category": "Android",
+ "subcategory": null,
+ "description": "RemixOS brings Android to laptops and PCs with a powerful desktop-like experience.",
+ "icon": null,
+ "url": "https://www.fosshub.com/Remix-OS.html"
+ },
+ {
+ "name": "TempleOS",
+ "category": "Other",
+ "subcategory": null,
+ "description": "",
+ "icon": null,
+ "url": "https://templeos.org/Downloads/"
}
]
diff --git a/app/src/main/assets/osicons/android.svg b/app/src/main/assets/osicons/android.svg
new file mode 100644
index 0000000..3f44ef6
--- /dev/null
+++ b/app/src/main/assets/osicons/android.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/elementary.svg b/app/src/main/assets/osicons/elementary.svg
new file mode 100644
index 0000000..f94da86
--- /dev/null
+++ b/app/src/main/assets/osicons/elementary.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/gentoo.svg b/app/src/main/assets/osicons/gentoo.svg
new file mode 100644
index 0000000..7be106f
--- /dev/null
+++ b/app/src/main/assets/osicons/gentoo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/manjaro.svg b/app/src/main/assets/osicons/manjaro.svg
new file mode 100644
index 0000000..b9cbc06
--- /dev/null
+++ b/app/src/main/assets/osicons/manjaro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/opensuse.svg b/app/src/main/assets/osicons/opensuse.svg
new file mode 100644
index 0000000..8ac1e8d
--- /dev/null
+++ b/app/src/main/assets/osicons/opensuse.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/parrot-security.svg b/app/src/main/assets/osicons/parrot-security.svg
new file mode 120000
index 0000000..ec8ea38
--- /dev/null
+++ b/app/src/main/assets/osicons/parrot-security.svg
@@ -0,0 +1 @@
+./parrotsecurity.svg
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/parrotos.svg b/app/src/main/assets/osicons/parrotos.svg
new file mode 120000
index 0000000..ec8ea38
--- /dev/null
+++ b/app/src/main/assets/osicons/parrotos.svg
@@ -0,0 +1 @@
+./parrotsecurity.svg
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/parrotsecurity.svg b/app/src/main/assets/osicons/parrotsecurity.svg
new file mode 100644
index 0000000..8eefdbd
--- /dev/null
+++ b/app/src/main/assets/osicons/parrotsecurity.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/pfsense.svg b/app/src/main/assets/osicons/pfsense.svg
new file mode 100644
index 0000000..1e53046
--- /dev/null
+++ b/app/src/main/assets/osicons/pfsense.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/proxmox-ve.svg b/app/src/main/assets/osicons/proxmox-ve.svg
new file mode 120000
index 0000000..335cf9d
--- /dev/null
+++ b/app/src/main/assets/osicons/proxmox-ve.svg
@@ -0,0 +1 @@
+proxmox.svg
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/slackware.svg b/app/src/main/assets/osicons/slackware.svg
new file mode 100644
index 0000000..611bf80
--- /dev/null
+++ b/app/src/main/assets/osicons/slackware.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/solus.svg b/app/src/main/assets/osicons/solus.svg
new file mode 100644
index 0000000..8d18781
--- /dev/null
+++ b/app/src/main/assets/osicons/solus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/assets/osicons/zorin.svg b/app/src/main/assets/osicons/zorin.svg
new file mode 100644
index 0000000..e867f61
--- /dev/null
+++ b/app/src/main/assets/osicons/zorin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/sh/sar/isodroid/ui/screens/DownloadsScreen.kt b/app/src/main/java/sh/sar/isodroid/ui/screens/DownloadsScreen.kt
index b994cd6..8413da8 100644
--- a/app/src/main/java/sh/sar/isodroid/ui/screens/DownloadsScreen.kt
+++ b/app/src/main/java/sh/sar/isodroid/ui/screens/DownloadsScreen.kt
@@ -50,6 +50,7 @@ import org.json.JSONArray
data class OsDownload(
val name: String,
val category: String,
+ val subcategory: String?,
val description: String,
val icon: String?,
val url: String
@@ -67,6 +68,7 @@ private fun loadOsDownloads(context: Context): List {
OsDownload(
name = obj.getString("name"),
category = obj.getString("category"),
+ subcategory = if (obj.isNull("subcategory")) null else obj.optString("subcategory", null),
description = obj.optString("description", ""),
icon = if (obj.isNull("icon")) null else obj.optString("icon", null),
url = obj.getString("url")
@@ -88,11 +90,9 @@ fun DownloadsScreen(
val context = LocalContext.current
val downloads = remember { loadOsDownloads(context) }
- // Group by category and maintain order: Linux, BSD, Windows, Recovery
- val categoryOrder = listOf("Linux", "BSD", "Windows", "Recovery")
+ // Group by category alphabetically
val groupedDownloads = remember(downloads) {
- downloads.groupBy { it.category }
- .toSortedMap(compareBy { categoryOrder.indexOf(it).takeIf { i -> i >= 0 } ?: Int.MAX_VALUE })
+ downloads.groupBy { it.category }.toSortedMap()
}
fun openUrl(url: String) {
@@ -166,22 +166,86 @@ private fun DownloadCategory(
)
) {
Column {
- downloads.forEachIndexed { index, os ->
- DownloadItem(
- os = os,
- onClick = { onItemClick(os) }
- )
- if (index < downloads.lastIndex) {
- HorizontalDivider(
- modifier = Modifier.padding(horizontal = 16.dp),
- color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
+ // Check if any items have subcategories
+ val hasSubcategories = downloads.any { it.subcategory != null }
+
+ if (hasSubcategories) {
+ // Group by subcategory alphabetically
+ val groupedBySubcategory = downloads
+ .groupBy { it.subcategory ?: "" }
+ .toSortedMap()
+
+ var isFirstGroup = true
+ groupedBySubcategory.forEach { (subcategory, osList) ->
+ if (subcategory.isNotEmpty()) {
+ // Subcategory header
+ if (!isFirstGroup) {
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
+ )
+ }
+ SubcategoryHeader(subcategory)
+ }
+ isFirstGroup = false
+
+ osList.sortedBy { it.name.lowercase() }.forEachIndexed { index, os ->
+ DownloadItem(
+ os = os,
+ onClick = { onItemClick(os) }
+ )
+ if (index < osList.lastIndex) {
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.15f)
+ )
+ }
+ }
+ }
+ } else {
+ // No subcategories, show flat list
+ downloads.forEachIndexed { index, os ->
+ DownloadItem(
+ os = os,
+ onClick = { onItemClick(os) }
)
+ if (index < downloads.lastIndex) {
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
+ )
+ }
}
}
}
}
}
+@Composable
+private fun SubcategoryHeader(title: String) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ HorizontalDivider(
+ modifier = Modifier.weight(1f),
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f)
+ )
+ Text(
+ text = title,
+ style = MaterialTheme.typography.labelMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.padding(horizontal = 12.dp)
+ )
+ HorizontalDivider(
+ modifier = Modifier.weight(1f),
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.5f)
+ )
+ }
+}
+
@Composable
private fun DownloadItem(
os: OsDownload,