11 Commits
17 changed files with 378 additions and 92 deletions
+15
View File
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.5] - 2026-03-12
### Added
- More OS download links
- Sub-categories for OS downloads (Desktop, Server, etc.)
### Changed
- Replaced refresh button with pull-to-refresh gesture
- Disable ISO directory change when root access is not available
- Status card now shows "Tap to request root" and updates when granted
- Improved monet/dynamic theme support for icons and text colors
### Fixed
- Sanitize user input for folder creation, IMG file creation, and file renaming to prevent shell escape vulnerabilities
## [1.4] - 2026-03-11 ## [1.4] - 2026-03-11
### Added ### Added
+2 -2
View File
@@ -12,8 +12,8 @@ android {
applicationId = "sh.sar.isodroid" applicationId = "sh.sar.isodroid"
minSdk = 26 minSdk = 26
targetSdk = 36 targetSdk = 36
versionCode = 4 versionCode = 5
versionName = "1.4" versionName = "1.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
} }
+156 -12
View File
@@ -67,10 +67,50 @@
"name": "Ubuntu", "name": "Ubuntu",
"category": "Linux", "category": "Linux",
"subcategory": "Desktop", "subcategory": "Desktop",
"description": "", "description": "Ubuntu Desktop delivers new tools and enhancements for developers, creators, gamers, and administrators.",
"icon": "ubuntu.svg", "icon": "ubuntu.svg",
"url": "https://ubuntu.com/download" "url": "https://ubuntu.com/download"
}, },
{
"name": "openSUSE",
"category": "Linux",
"subcategory": "Desktop",
"description": "openSUSE makes open source Linux operating systems for desktops, servers and containers.",
"icon": "opensuse.svg",
"url": "https://get.opensuse.org/"
},
{
"name": "Rocky Linux",
"category": "Linux",
"subcategory": "Server",
"description": "Rocky Linux is an open-source enterprise operating system designed to be 100% bug-for-bug compatible with Red Hat Enterprise Linux®.",
"icon": "rockylinux.svg",
"url": "https://rockylinux.org/download"
},
{
"name": "AlmaLinux",
"category": "Linux",
"subcategory": "Server",
"description": "An Open Source, community owned and governed, forever-free enterprise Linux distribution, focused on long-term stability, providing a robust production-grade platform. AlmaLinux OS is binary compatible with RHEL®.",
"icon": "almalinux.svg",
"url": "https://almalinux.org/get-almalinux/"
},
{
"name": "Ubuntu Server",
"category": "Linux",
"subcategory": "Server",
"description": "Whether you want to configure a simple file server or build a fifty thousand-node cloud, you can rely on Ubuntu Server and its five years of free updates.",
"icon": "ubuntu.svg",
"url": "https://ubuntu.com/download/server"
},
{
"name": "Alpine Linux",
"category": "Linux",
"subcategory": "Server",
"description": "Alpine Linux is a security-oriented, lightweight Linux distribution based on musl libc and busybox.",
"icon": "alpinelinux.svg",
"url": "https://alpinelinux.org/downloads/"
},
{ {
"name": "FreeBSD", "name": "FreeBSD",
"category": "BSD", "category": "BSD",
@@ -79,6 +119,30 @@
"icon": "freebsd.svg", "icon": "freebsd.svg",
"url": "https://www.freebsd.org/where/" "url": "https://www.freebsd.org/where/"
}, },
{
"name": "OpenBSD",
"category": "BSD",
"subcategory": null,
"description": "A free, multi-platform 4.4BSD-based UNIX-like operating system emphasizing security and correctness.",
"icon": "openbsd.svg",
"url": "https://www.openbsd.org/faq/faq4.html#Download"
},
{
"name": "GhostBSD",
"category": "BSD",
"subcategory": "Desktop",
"description": "A simple, elegant desktop BSD Operating System",
"icon": null,
"url": "https://ghostbsd.org/download"
},
{
"name": "TrueNAS",
"category": "BSD",
"subcategory": "Networking",
"description": "The world's most popular software-defined storage operating system.",
"icon": "truenas.svg",
"url": "https://www.truenas.com/download-truenas-core/"
},
{ {
"name": "OPNsense", "name": "OPNsense",
"category": "BSD", "category": "BSD",
@@ -114,7 +178,7 @@
{ {
"name": "Hiren's BootCD PE", "name": "Hiren's BootCD PE",
"category": "Recovery", "category": "Recovery",
"subcategory": null, "subcategory": "Toolkit",
"description": "Hiren's BootCD PE (Preinstallation Environment) is a restored edition of Hiren's BootCD based on Windows PE", "description": "Hiren's BootCD PE (Preinstallation Environment) is a restored edition of Hiren's BootCD based on Windows PE",
"icon": null, "icon": null,
"url": "https://www.hirensbootcd.org/download/" "url": "https://www.hirensbootcd.org/download/"
@@ -122,11 +186,59 @@
{ {
"name": "Clonezilla", "name": "Clonezilla",
"category": "Recovery", "category": "Recovery",
"subcategory": null, "subcategory": "Disk",
"description": "Clonezilla is a partition and disk imaging/cloning program.", "description": "Clonezilla is a partition and disk imaging/cloning program.",
"icon": null, "icon": null,
"url": "https://clonezilla.org/downloads.php" "url": "https://clonezilla.org/downloads.php"
}, },
{
"name": "SystemRescue",
"category": "Recovery",
"subcategory": "Toolkit",
"description": "A Linux system rescue toolkit for administering or repairing your system and data.",
"icon": null,
"url": "https://www.system-rescue.org/Download/"
},
{
"name": "GParted Live",
"category": "Recovery",
"subcategory": "Disk",
"description": "A small bootable GNU/Linux distribution for x86 based computers with the GParted partition editor.",
"icon": null,
"url": "https://gparted.org/download.php"
},
{
"name": "Rescuezilla",
"category": "Recovery",
"subcategory": "Disk",
"description": "The Swiss Army Knife of system recovery. Easy-to-use disk imaging and cloning with a friendly GUI.",
"icon": null,
"url": "https://rescuezilla.com/download"
},
{
"name": "Memtest86+",
"category": "Recovery",
"subcategory": "Hardware",
"description": "Free, open-source memory testing software for x86 and x86-64 computers.",
"icon": null,
"url": "https://memtest.org/"
},
{
"name": "ShredOS",
"category": "Recovery",
"subcategory": "Disk",
"description": "A USB bootable small linux distro for securely erasing your disks using nwipe.",
"icon": null,
"url": "https://github.com/PartialVolume/shredos.x86_64/releases"
},
{
"name": "Super Grub2",
"category": "Recovery",
"subcategory": "Boot",
"description": "Get back to your GNU/Linux & Windows computers !",
"icon": null,
"url": "https://www.supergrubdisk.org/category/download/"
},
{ {
"name": "Bazzite", "name": "Bazzite",
"category": "Linux", "category": "Linux",
@@ -143,14 +255,6 @@
"icon": "manjaro.svg", "icon": "manjaro.svg",
"url": "https://manjaro.org/products" "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", "name": "Kali Linux",
"category": "Linux", "category": "Linux",
@@ -195,8 +299,48 @@
"name": "TempleOS", "name": "TempleOS",
"category": "Other", "category": "Other",
"subcategory": null, "subcategory": null,
"description": "", "description": "A biblical-themed lightweight operating system designed to be the Third Temple.",
"icon": null, "icon": null,
"url": "https://templeos.org/Downloads/" "url": "https://templeos.org/Downloads/"
},
{
"name": "Haiku",
"category": "Other",
"subcategory": null,
"description": "An open-source operating system inspired by BeOS, targeting personal computing.",
"icon": null,
"url": "https://www.haiku-os.org/get-haiku/"
},
{
"name": "ReactOS",
"category": "Other",
"subcategory": null,
"description": "A free, open-source operating system aiming for Windows compatibility.",
"icon": "reactos.svg",
"url": "https://reactos.org/download/"
},
{
"name": "ChromeOS Flex",
"category": "ChromeOS",
"subcategory": null,
"description": "Google's cloud-first operating system for PCs and Macs.",
"icon": "chromeos.svg",
"url": "https://chromeenterprise.google/os/chromeosflex/"
},
{
"name": "FydeOS",
"category": "ChromeOS",
"subcategory": null,
"description": "A ChromiumOS-based operating system with Android app support.",
"icon": null,
"url": "https://fydeos.io/download"
},
{
"name": "Brunch",
"category": "ChromeOS",
"subcategory": null,
"description": "A framework to boot ChromeOS on generic x86 hardware.",
"icon": null,
"url": "https://github.com/sebanc/brunch/releases"
} }
] ]
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>AlmaLinux</title><path d="M23.994 15.133c.079 1.061-.668 1.927-1.69 2.005a1.8 1.8 0 0 1-1.928-1.651c-.078-1.062.63-1.849 1.691-1.967 1.023-.078 1.849.59 1.927 1.613zm-12.623 4.955c-.944 0-1.73.786-1.73 1.809 0 1.14.747 1.848 1.887 1.848.904-.04 1.691-.865 1.691-1.809 0-.983-.904-1.848-1.848-1.848zm1.061-9.675c-.039-.865-.078-1.73.08-2.556.156-.944.314-1.887.904-2.674.707-.983 1.809-.944 2.399.118.314.511.432 1.062.471 1.652 0 .354.158.432.472.393.944-.157 1.888-.157 2.792.197.118.039.236.118.394 0 .314-.276.393-1.652.196-2.006-.354-.63-.904-.55-1.455-.55-.629.039-1.18-.158-1.612-.67-.393-.471-.511-1.06-.59-1.65-.04-.276-.079-.512-.315-.709-.55-.55-1.809-.432-2.477.118-2.556 2.045-2.989 5.467-1.534 8.18.04.118.118.236.275.157zm7.984 3.658c.354-.511.865-.747 1.415-.983a.973.973 0 0 0 .59-.472c.354-.669-.078-1.81-.747-2.36-2.595-2.006-5.938-1.612-8.18.433-.118.078-.157.196-.078.314.786-.236 1.612-.472 2.477-.51.905-.08 1.848-.158 2.753.235 1.14.472 1.337 1.534.472 2.36-.393.393-.905.668-1.455.825-.315.08-.354.236-.236.551.354.865.59 1.77.472 2.753-.04.157-.079.275.078.393.354.236 1.691 0 1.967-.275.511-.472.314-1.023.196-1.534-.157-.63-.078-1.219.276-1.73zm-7.197-2.045c-.118-.079-.197-.118-.315 0 .472.708.905 1.455 1.259 2.241.314.866.668 1.73.55 2.714-.118 1.18-1.1 1.69-2.123 1.101-.511-.275-.905-.669-1.22-1.14-.196-.276-.393-.276-.629-.08-.747.63-1.533 1.102-2.516 1.26-.158 0-.315 0-.394.157-.118.393.472 1.612.826 1.809.59.354 1.062 0 1.534-.276.55-.314 1.101-.432 1.73-.236.59.197.983.63 1.337 1.102.158.196.315.353.63.432.747.197 1.77-.59 2.084-1.376 1.18-3.028-.157-6.135-2.753-7.708zm-2.556 2.438c.472-.669.826-1.416.983-2.202-.157-.04-.197.04-.315.078-.904.944-1.848 1.849-3.067 2.478-.472.236-.983.433-1.534.433-.865 0-1.376-.551-1.298-1.416a2.92 2.92 0 0 1 .787-1.849c.236-.275.236-.432-.04-.668-.786-.55-1.494-1.22-1.848-2.124-.078-.275-.275-.275-.51-.157a4.293 4.293 0 0 0-.434.236c-1.022.63-1.14 1.416-.275 2.28.63.63.944 1.338.708 2.203-.118.433-.354.747-.63 1.101a.95.95 0 0 0-.235.787c.079.747.826 1.494 1.73 1.573 2.517.236 4.562-.63 5.978-2.753zm-4.68-5.152c1.376 1.18 3.067 1.455 4.837 1.377.157 0 .315 0 .354-.118.04-.197-.157-.197-.275-.236-.826-.354-1.691-.63-2.438-1.14S6.848 8.25 6.534 7.266c-.236-.747.078-1.415.825-1.651.669-.236 1.337-.236 1.967 0 .393.157.55.078.629-.354.118-.747.354-1.455.826-2.085.55-.786.55-.865-.354-1.376-.04 0-.04-.04-.079-.04-.865-.471-1.534-.196-1.848.709-.472 1.376-1.377 1.887-2.832 1.612-.196-.04-.393-.079-.472-.079-.747.118-1.18.55-1.297 1.14-.158 1.81.786 3.107 2.084 4.17zm-2.32 3.658c-.079-.944-1.023-1.652-2.045-1.534-.905.079-1.691 1.022-1.613 1.966.08.983 1.023 1.77 1.967 1.652 1.14-.079 1.73-1.18 1.69-2.084zm15.18-8.298c.943-.079 1.73-.983 1.651-1.927-.078-.983-1.022-1.77-2.005-1.691-1.023.079-1.73.983-1.652 1.966s.983 1.73 2.006 1.652zm-12.27-.826c1.062-.157 1.77-1.023 1.652-2.045C8.107.897 7.163.149 6.18.267c-1.062.118-1.691.944-1.573 2.085.118.865 1.061 1.612 1.966 1.494z"/></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Asahi Linux</title><path d="m13.835 0-1.72 1.323v.97h2.178zm-1.95.057L9.81 1.095l2.076 4.153zm.23 3.768V6.22l-1.057-2.113L6.43 5.678 12 8.009l5.57-2.331zM6.21 5.835.533 15.957 11.885 24V8.21L6.222 5.84Zm11.58 0-.012.004-5.6 2.345 7.512 10.449 3.777-2.675zm-3.955 7.926v5.422l1.952-2.711zm2.864 3.981-4.411 6.135 5.846-4.14z"/></svg>

After

Width:  |  Height:  |  Size: 410 B

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Google Chrome</title><path d="M12 0C8.21 0 4.831 1.757 2.632 4.501l3.953 6.848A5.454 5.454 0 0 1 12 6.545h10.691A12 12 0 0 0 12 0zM1.931 5.47A11.943 11.943 0 0 0 0 12c0 6.012 4.42 10.991 10.189 11.864l3.953-6.847a5.45 5.45 0 0 1-6.865-2.29zm13.342 2.166a5.446 5.446 0 0 1 1.45 7.09l.002.001h-.002l-5.344 9.257c.206.01.413.016.621.016 6.627 0 12-5.373 12-12 0-1.54-.29-3.011-.818-4.364zM12 16.364a4.364 4.364 0 1 1 0-8.728 4.364 4.364 0 0 1 0 8.728Z"/></svg>

After

Width:  |  Height:  |  Size: 535 B

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>FreeNAS</title><path d="M19.598 2.707h.143c.06 0 .101.01.124.032a.107.107 0 0 1 .034.083c0 .045-.017.077-.051.097a.307.307 0 0 1-.153.029h-.098v-.241zm0 .391h.09l.214.337h.203l-.242-.356v-.008a.29.29 0 0 0 .161-.081.237.237 0 0 0 .059-.168.254.254 0 0 0-.03-.125.241.241 0 0 0-.08-.082.349.349 0 0 0-.114-.045.645.645 0 0 0-.133-.013h-.303v.879h.175v-.338m-.485-.368a.628.628 0 0 1 .348-.367.688.688 0 0 1 .277-.053.64.64 0 0 1 .625.42.735.735 0 0 1 .049.27.74.74 0 0 1-.049.271.642.642 0 0 1-.348.366.675.675 0 0 1-.277.054.646.646 0 0 1-.624-.421.712.712 0 0 1-.049-.27c0-.095.015-.185.048-.27zm.014.884a.835.835 0 0 0 .273.179.884.884 0 0 0 .338.064c.12 0 .233-.021.339-.064A.83.83 0 0 0 20.598 3a.852.852 0 0 0-.249-.613.815.815 0 0 0-.272-.179c-.105-.043-.218-.064-.339-.064s-.233.021-.338.064a.82.82 0 0 0-.454.45.838.838 0 0 0-.067.342c0 .125.021.239.067.343a.796.796 0 0 0 .181.271m-1.864 12.361a3.268 3.268 0 0 1-.931 1.215 3.203 3.203 0 0 1-2.008.695 3.199 3.199 0 0 1-2.423-1.085 1.989 1.989 0 0 1-.439-.855 2.223 2.223 0 0 1-.06-.519c.002-.854.428-1.71.845-2.362.21-.326.418-.602.575-.794l.208.254.036.046a7.499 7.499 0 0 0 1.126 1.083c.766.597 1.85 1.197 3.126 1.229.012 0 .023.003.035.004a.172.172 0 0 1 .064 0 .16.16 0 0 1 .126.189c-.061.33-.158.628-.28.9zm6.719-7.025a5.339 5.339 0 0 1-.821.905c-.752.664-1.936 1.343-3.649 1.435l-.505.926a.173.173 0 0 1-.299.008l-.581-.954c-.275.051-.984.168-1.808.168-1.376-.03-1.807-.241-2.263-.532l1.538-2.072-3.297-.764 4.136-.795c1.208-2.437 1.583-4.521 1.675-5.157-4.638.514-8.102 1.666-10.329 2.632l-.179.079-.034.014-.249-.241a9.292 9.292 0 0 0-1.459-.985 9.404 9.404 0 0 0-4.516-1.175 8.05 8.05 0 0 0-.894.043c-.491.031-.253.153-.194.203.225.184.544.573.753 1.112.211.541.354 1.27.354 2.254 0 .275-.012.579-.036.896-.195.362-.376.741-.539 1.132C.311 9.227 0 10.479 0 11.767c0 2.291.9 4.378 2.181 6.074l.173.262-1.355 2.7a.063.063 0 0 0 .021.08c.011.007.023.01.035.01a.067.067 0 0 0 .047-.02l2.117-1.863.248.24a10.1 10.1 0 0 0 6.812 2.63c4.516 0 8.342-2.953 9.652-7.032l.288-.124a6.314 6.314 0 0 0 1.132-.555c.684-.424 1.502-1.107 2.045-2.141.362-.687.604-1.534.604-2.576 0-.163-.006-.33-.018-.502"/></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>NetBSD</title><path d="M22.686 10.681c0-.181.064-.336.193-.464a.634.634 0 0 1 .464-.193c.182 0 .336.065.465.193a.633.633 0 0 1 .192.464.635.635 0 0 1-.192.465.632.632 0 0 1-.465.193.634.634 0 0 1-.464-.193.634.634 0 0 1-.193-.465zm1.206 0a.53.53 0 0 0-.16-.388.53.53 0 0 0-.389-.16.53.53 0 0 0-.388.16.528.528 0 0 0-.161.388.53.53 0 0 0 .16.389.53.53 0 0 0 .39.161.529.529 0 0 0 .388-.161.53.53 0 0 0 .16-.389zm-.344.396-.207-.349h-.117v.349h-.114v-.808h.207c.194 0 .292.074.292.223 0 .104-.053.177-.157.22l.221.365zm-.324-.71v.27l.076.001c.075 0 .126-.01.151-.028.026-.02.04-.056.04-.11 0-.09-.059-.134-.175-.134h-.092m-3.892 3.28c0 .403.014.667.146.82.132.147.344.213.607.213 1.266 0 1.698-1.127 1.698-2.122 0-1.318-.695-2.1-2.02-2.1-.197 0-.336.036-.38.095-.044.058-.051.197-.051.424v2.67zm-1.046-2.319c0-.695-.015-.834-.352-.87l-.139-.015c-.073-.037-.073-.25.015-.257a30.521 30.521 0 0 1 1.96-.065c.6 0 1.2.059 1.706.241.958.344 1.485 1.208 1.485 2.122 0 .981-.468 1.771-1.31 2.188-.497.25-1.097.344-1.85.344-.345 0-.71-.044-.974-.044-.351 0-.724.008-1.141.022-.059-.044-.059-.22 0-.256l.226-.036c.33-.059.374-.11.374-.783v-2.59m-2.405 3.76c-.673 0-1.09-.19-1.244-.277-.139-.161-.234-.688-.234-1.186.051-.095.22-.102.278-.022.146.476.636 1.149 1.258 1.149.542 0 .79-.373.79-.74 0-.592-.555-.943-.994-1.163-.527-.263-1.098-.702-1.105-1.427 0-.827.636-1.398 1.697-1.398.242 0 .542.03.834.118.095.029.161.043.25.058.057.161.13.556.13 1.047-.036.087-.219.095-.285.022-.124-.374-.439-.908-.965-.908-.483 0-.747.315-.747.68 0 .337.3.645.666.835l.483.256c.454.242 1.032.666 1.032 1.471 0 .9-.74 1.486-1.844 1.486m-4.2-1.354c0 .57.072.93.643.93.542 0 .827-.418.827-1.01 0-.637-.366-1.084-1.068-1.084-.403 0-.403.007-.403.3v.864zm0-1.69c0 .19.007.204.387.204.63 0 .863-.402.863-.841 0-.637-.395-.952-.9-.952-.343 0-.35.06-.35.381zm-1.01-.71c0-.74-.015-.82-.322-.857l-.198-.03c-.066-.036-.08-.255.03-.263.555-.036 1.09-.065 1.821-.065.703 0 1.171.08 1.493.27.314.19.505.498.505.93 0 .615-.52.856-.747.915-.073.014-.146.044-.146.08 0 .022.037.044.103.059.578.124 1.068.504 1.075 1.214.007.673-.395 1.069-.856 1.23-.461.16-1.01.183-1.456.183-.263 0-.541-.03-.754-.03-.358 0-.717.008-1.134.022-.058-.044-.058-.234 0-.256l.213-.044c.329-.065.373-.117.373-.775v-2.584M9.038 12.44c-.095 0-.102.007-.102.168v1.097c0 .41 0 .864.512.864.102 0 .22-.051.307-.11.073.022.117.103.102.19-.204.22-.6.425-1.053.425-.607 0-.82-.351-.82-.834v-1.632c0-.154-.007-.168-.139-.168H7.62c-.08-.03-.103-.176-.044-.227.226-.08.431-.213.607-.33.132-.095.315-.248.541-.57.051-.03.183-.022.22.036v.549c0 .139.007.146.139.146h.651c.037.03.059.074.059.14 0 .08-.022.211-.095.256h-.66m-2.627.475c.103 0 .22-.015.3-.066.037-.022.051-.095.051-.168 0-.241-.139-.402-.388-.402-.307 0-.57.292-.57.526 0 .103.102.11.336.11zm-.483.322c-.168 0-.183.015-.183.132 0 .549.351 1.083 1.032 1.083.205 0 .483-.044.68-.38.08-.015.19.043.19.168-.3.622-.84.834-1.28.834-.988 0-1.522-.695-1.522-1.493 0-.922.666-1.64 1.58-1.64.762 0 1.171.491 1.171 1.055 0 .139-.036.241-.263.241H5.927m-1.255.49c0 .476 0 .937.014 1.179-.05.087-.256.168-.431.168-.008 0-.25-.373-.593-.798l-1.69-2.093c-.417-.527-.666-.826-.798-.936-.036.073-.036.197-.036.468v1.42c0 .593.029 1.141.11 1.339.065.154.234.198.424.234l.205.03c.058.058.044.212 0 .255a26.585 26.585 0 0 0-.98-.022c-.271 0-.542.008-.82.022-.044-.043-.059-.197 0-.256l.124-.022c.198-.044.337-.087.403-.241.073-.198.102-.746.102-1.34v-1.858c0-.402 0-.52-.051-.622-.051-.124-.161-.198-.417-.249l-.205-.029c-.051-.059-.044-.234.03-.256.343.014.709.022 1.009.022.249 0 .46-.008.615-.022.073.33.548.885 1.207 1.668l.614.725c.33.38.535.636.703.805.03-.073.03-.198.03-.33v-1.01c0-.592-.03-1.141-.11-1.339-.067-.153-.227-.197-.425-.234l-.198-.029c-.058-.059-.043-.212 0-.256.396.014.68.022.98.022.272 0 .535-.008.82-.022.044.044.059.197 0 .256l-.131.022c-.19.044-.33.088-.396.242-.08.197-.11.746-.11 1.339v1.749M21.537 3.59c-2.848-1.367-5.425-.715-8.306.148-2.902.868-5.482 1.337-8.381.154l.79 1.41.87 1.557.79 1.41c2.309.652 4.22-.194 6.271-1.22 2.463-1.23 4.688-2.337 7.502-1.696-2.378-1.19-4.534-.895-7.02-.22 2.434-1.24 4.726-2.204 7.484-1.543M13.16 20.478l-2.272-4.385H9.91l2.283 4.826s.23.455.724.203c.493-.25.245-.644.245-.644M4.634 4.025s-.068-.159-.26-.053c-.16.089-.077.253-.077.253l3.004 6.351h.728L4.634 4.025"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>ReactOS</title><path d="M20.21 24c-1.148-.007-2.477-.334-3.89-.915-2.823-1.163-6.04-3.372-9.036-6.367C4.289 13.723 2.075 10.505.913 7.68-.25 4.857-.383 2.36.988.989 2.358-.38 4.855-.248 7.679.915c.306.125.617.265.932.415-.331.113-.658.24-.974.383l-.141-.058C4.832.558 2.698.519 1.607 1.609.517 2.7.557 4.83 1.653 7.494c1.097 2.663 3.235 5.793 6.147 8.704 2.91 2.911 6.044 5.05 8.708 6.147 2.664 1.097 4.79 1.136 5.88.045 1.091-1.09 1.056-3.22-.041-5.884-.108-.263-.23-.531-.358-.803.134-.317.25-.642.354-.973.282.54.53 1.07.744 1.589 1.163 2.823 1.292 5.32-.079 6.691-.685.685-1.651.997-2.799.99zM3.79 24c-1.148.008-2.117-.305-2.802-.99-1.37-1.37-1.238-3.868-.075-6.691.235-.572.517-1.16.836-1.76.098.333.212.66.34.978a17.67 17.67 0 00-.436.969C.556 19.169.521 21.3 1.611 22.39c1.091 1.091 3.221 1.051 5.885-.045.922-.38 3.021-1.69 4.026-2.308.216.162.433.32.649.474-1.157.733-3.415 2.13-4.492 2.574-1.412.581-2.74.907-3.888.915zm9.753-4.458c-.214-.14-.429-.282-.645-.433a34.547 34.547 0 003.302-2.911c2.912-2.911 5.05-6.04 6.147-8.704 1.097-2.664 1.132-4.794.042-5.885-1.091-1.09-3.217-1.055-5.88.042l-.072.029a10.726 10.726 0 00-.99-.379c.295-.14.587-.272.874-.39 2.824-1.163 5.321-1.292 6.691.078s1.238 3.864.075 6.688c-1.162 2.823-3.376 6.046-6.37 9.04a35.747 35.747 0 01-3.174 2.825zm1.95 1.156c-.325-.17-1.798-1.073-2.135-1.273 1.002-.806 2.423-1.97 3.396-2.944 1.718-1.718 3.981-4.787 5.162-6.555-.008.111-.093 2.49-.105 2.6a9.802 9.802 0 01-6.318 8.172zm-6.928-.034c-3.407-1.308-6.043-4.71-6.287-8.198-.01-.151-.06-.399-.054-.984.007-.602.056-1.423.159-1.283 1.036 1.42 3.976 5.455 5.352 6.83.973.973 1.927 1.624 2.929 2.43a112.45 112.45 0 01-2.1 1.205zm3.43-2.208a33.27 33.27 0 01-3.443-3.01c-2.54-2.54-4.462-5.254-5.568-7.582 1.45-3.597 4.973-6.138 9.087-6.138 4.051 0 7.53 2.465 9.02 5.976-1.093 2.363-3.045 5.145-5.643 7.743a33.161 33.161 0 01-3.452 3.011z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

+1
View File
@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>TrueNAS</title><path d="M24 10.049v5.114l-10.949 6.324v-5.114L24 10.049zm-24 0v5.114l10.956 6.324v-5.114L0 10.049zm12.004-.605l-4.433 2.559 4.433 2.559 4.429-2.559-4.429-2.559zm10.952-1.207l-9.905-5.723v5.118l5.473 3.164 4.432-2.559zm-12-.605V2.513L1.044 8.236l4.432 2.555 5.48-3.159z"/></svg>

After

Width:  |  Height:  |  Size: 371 B

@@ -134,7 +134,7 @@ fun FileBrowser(
// Parent directory item // Parent directory item
if (canNavigateUp) { if (canNavigateUp) {
item { item {
FileItem( FileItemCard(
name = "..", name = "..",
size = "", size = "",
isDirectory = true, isDirectory = true,
@@ -145,7 +145,7 @@ fun FileBrowser(
} }
items(files) { file -> items(files) { file ->
FileItem( FileItemCard(
name = file.name, name = file.name,
size = file.formattedSize, size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"), isIso = file.name.lowercase().endsWith(".iso"),
@@ -164,7 +164,7 @@ fun FileBrowser(
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
private fun FileItem( fun FileItemCard(
name: String, name: String,
size: String, size: String,
isDirectory: Boolean = false, isDirectory: Boolean = false,
@@ -6,6 +6,7 @@
package sh.sar.isodroid.ui.components package sh.sar.isodroid.ui.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -42,9 +43,6 @@ import coil.decode.SvgDecoder
import coil.request.ImageRequest import coil.request.ImageRequest
import sh.sar.isodroid.data.MountStatus import sh.sar.isodroid.data.MountStatus
import sh.sar.isodroid.data.MountType import sh.sar.isodroid.data.MountType
import sh.sar.isodroid.ui.theme.ErrorRed
import sh.sar.isodroid.ui.theme.MountedGreen
import sh.sar.isodroid.ui.theme.UnmountedGray
import java.io.File import java.io.File
@Composable @Composable
@@ -52,7 +50,9 @@ fun StatusCard(
mountStatus: MountStatus, mountStatus: MountStatus,
rootAvailable: Boolean?, rootAvailable: Boolean?,
deviceSupported: Boolean?, deviceSupported: Boolean?,
modifier: Modifier = Modifier rootDenied: Boolean = false,
modifier: Modifier = Modifier,
onRequestRoot: (() -> Unit)? = null
) { ) {
val context = LocalContext.current val context = LocalContext.current
@@ -73,7 +73,15 @@ fun StatusCard(
val isImg = fileName?.lowercase()?.endsWith(".img") == true val isImg = fileName?.lowercase()?.endsWith(".img") == true
Card( Card(
modifier = modifier.fillMaxWidth(), modifier = modifier
.fillMaxWidth()
.then(
if (showRootError && onRequestRoot != null) {
Modifier.clickable(onClick = onRequestRoot)
} else {
Modifier
}
),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = when { containerColor = when {
hasError -> MaterialTheme.colorScheme.errorContainer hasError -> MaterialTheme.colorScheme.errorContainer
@@ -101,9 +109,9 @@ fun StatusCard(
}, },
contentDescription = null, contentDescription = null,
tint = when { tint = when {
hasError -> ErrorRed hasError -> MaterialTheme.colorScheme.onErrorContainer
mountStatus.mounted -> MountedGreen mountStatus.mounted -> MaterialTheme.colorScheme.onPrimaryContainer
else -> UnmountedGray else -> MaterialTheme.colorScheme.onSurfaceVariant
}, },
modifier = Modifier.size(32.dp) modifier = Modifier.size(32.dp)
) )
@@ -121,7 +129,7 @@ fun StatusCard(
) )
if (showRootError) { if (showRootError) {
Text( Text(
text = "Grant root access to use ISODroid", text = if (rootDenied) "Grant root access to use ISO Droid" else "Tap to grant root access",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
@@ -190,20 +198,20 @@ fun StatusCard(
text = fileName, text = fileName,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface color = MaterialTheme.colorScheme.onPrimaryContainer
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Row { Row {
Text( Text(
text = if (mountStatus.type == MountType.CDROM) "CD-ROM" else "Mass Storage", text = if (mountStatus.type == MountType.CDROM) "CD-ROM" else "Mass Storage",
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f)
) )
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(8.dp))
Text( Text(
text = if (mountStatus.readOnly) "Read-Only" else "Read-Write", text = if (mountStatus.readOnly) "Read-Only" else "Read-Write",
style = MaterialTheme.typography.labelSmall, style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.secondary color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f)
) )
} }
} }
@@ -114,7 +114,8 @@ fun DownloadsScreen(
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
) )
) )
} }
@@ -223,27 +224,12 @@ private fun DownloadCategory(
@Composable @Composable
private fun SubcategoryHeader(title: String) { private fun SubcategoryHeader(title: String) {
Row( Text(
modifier = Modifier text = title,
.fillMaxWidth() style = MaterialTheme.typography.labelMedium,
.padding(horizontal = 16.dp, vertical = 12.dp), color = MaterialTheme.colorScheme.primary,
verticalAlignment = Alignment.CenterVertically modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
) { )
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 @Composable
@@ -5,18 +5,28 @@
package sh.sar.isodroid.ui.screens package sh.sar.isodroid.ui.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Album
import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Eject import androidx.compose.material.icons.filled.Eject
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Settings
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.ExtendedFloatingActionButton
@@ -42,10 +52,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import sh.sar.isodroid.data.IsoFile import sh.sar.isodroid.data.IsoFile
import sh.sar.isodroid.data.MountOptions
import sh.sar.isodroid.ui.components.CreateImgDialog import sh.sar.isodroid.ui.components.CreateImgDialog
import sh.sar.isodroid.ui.components.FileContextMenu import sh.sar.isodroid.ui.components.FileContextMenu
import sh.sar.isodroid.ui.components.FileBrowser import sh.sar.isodroid.ui.components.FileItemCard
import sh.sar.isodroid.ui.components.MountDialog import sh.sar.isodroid.ui.components.MountDialog
import sh.sar.isodroid.ui.components.StatusCard import sh.sar.isodroid.ui.components.StatusCard
import sh.sar.isodroid.viewmodel.MainViewModel import sh.sar.isodroid.viewmodel.MainViewModel
@@ -66,6 +75,22 @@ fun MainScreen(
var showCreateImgDialog by remember { mutableStateOf(false) } var showCreateImgDialog by remember { mutableStateOf(false) }
var contextMenuFile by remember { mutableStateOf<IsoFile?>(null) } var contextMenuFile by remember { mutableStateOf<IsoFile?>(null) }
val pullToRefreshState = rememberPullToRefreshState()
// Handle pull-to-refresh
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
viewModel.refresh()
}
}
// Stop refreshing when loading completes
LaunchedEffect(uiState.isLoading) {
if (!uiState.isLoading) {
pullToRefreshState.endRefresh()
}
}
// Show error messages // Show error messages
LaunchedEffect(uiState.errorMessage) { LaunchedEffect(uiState.errorMessage) {
uiState.errorMessage?.let { message -> uiState.errorMessage?.let { message ->
@@ -92,7 +117,8 @@ fun MainScreen(
title = { Text("ISO Droid") }, title = { Text("ISO Droid") },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
), ),
actions = { actions = {
IconButton( IconButton(
@@ -104,12 +130,6 @@ fun MainScreen(
contentDescription = "Create IMG" contentDescription = "Create IMG"
) )
} }
IconButton(onClick = { viewModel.refresh() }) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "Refresh"
)
}
IconButton(onClick = onNavigateToDownloads) { IconButton(onClick = onNavigateToDownloads) {
Icon( Icon(
imageVector = Icons.Default.Download, imageVector = Icons.Default.Download,
@@ -147,43 +167,118 @@ fun MainScreen(
} }
} }
) { paddingValues -> ) { paddingValues ->
Column( val hapticFeedback = LocalHapticFeedback.current
Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.padding(16.dp) .nestedScroll(pullToRefreshState.nestedScrollConnection)
) { ) {
StatusCard( if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
mountStatus = uiState.mountStatus,
rootAvailable = uiState.hasRoot,
deviceSupported = uiState.isSupported
)
Spacer(modifier = Modifier.height(16.dp))
if (uiState.isLoading) {
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
} }
} else if (uiState.hasRoot == true && uiState.isSupported == true) { } else {
FileBrowser( LazyColumn(
files = uiState.isoFiles, modifier = Modifier.fillMaxSize(),
currentPath = uiState.currentPath, contentPadding = PaddingValues(16.dp),
onFileClick = { file -> verticalArrangement = Arrangement.spacedBy(8.dp)
selectedFile = file ) {
showMountDialog = true // Status card
}, item {
onFileLongClick = { file -> StatusCard(
contextMenuFile = file mountStatus = uiState.mountStatus,
}, rootAvailable = uiState.hasRoot,
onNavigateUp = { viewModel.navigateUp() }, deviceSupported = uiState.isSupported,
canNavigateUp = viewModel.canNavigateUp(), rootDenied = uiState.rootDenied,
modifier = Modifier.weight(1f) onRequestRoot = { viewModel.requestRootAccess() }
) )
}
// File browser content
if (uiState.hasRoot == true && uiState.isSupported == true) {
if (uiState.isoFiles.isEmpty()) {
// Empty state
item {
Column(
modifier = Modifier
.fillParentMaxSize()
.padding(32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Default.Album,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f)
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "No ISO/IMG files found",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Place ISO or IMG files in:\n${uiState.currentPath}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Tap + to create an empty IMG file\nChange directory in Settings",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.5f),
textAlign = androidx.compose.ui.text.style.TextAlign.Center
)
}
}
} else {
// Parent directory navigation
if (viewModel.canNavigateUp()) {
item {
FileItemCard(
name = "..",
size = "",
isDirectory = true,
onClick = { viewModel.navigateUp() },
onLongClick = null
)
}
}
// File list
items(uiState.isoFiles) { file ->
FileItemCard(
name = file.name,
size = file.formattedSize,
isIso = file.name.lowercase().endsWith(".iso"),
isImg = file.name.lowercase().endsWith(".img"),
onClick = {
selectedFile = file
showMountDialog = true
},
onLongClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
contextMenuFile = file
}
)
}
}
}
}
} }
PullToRefreshContainer(
state = pullToRefreshState,
modifier = Modifier.align(Alignment.TopCenter)
)
} }
} }
@@ -158,7 +158,8 @@ fun SettingsScreen(
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
) )
) )
} }
@@ -172,11 +173,20 @@ fun SettingsScreen(
// Storage section // Storage section
SectionHeader(title = "Storage") SectionHeader(title = "Storage")
val hasRootForStorage = uiState.hasRoot ?: false
SettingsItem( SettingsItem(
icon = Icons.Default.Folder, icon = Icons.Default.Folder,
title = "ISO Directory", title = "ISO Directory",
subtitle = uiState.isoDirectory, subtitle = uiState.isoDirectory,
onClick = { showPathDialog = true } enabled = hasRootForStorage,
disabledHint = if (!uiState.rootDenied) "Tap to grant root access" else null,
onClick = {
if (hasRootForStorage) {
showPathDialog = true
} else {
viewModel.requestRootAccess()
}
}
) )
HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp))
@@ -814,8 +824,12 @@ private fun SettingsItem(
icon: ImageVector, icon: ImageVector,
title: String, title: String,
subtitle: String, subtitle: String,
enabled: Boolean = true,
disabledHint: String? = null,
onClick: () -> Unit onClick: () -> Unit
) { ) {
val contentAlpha = if (enabled) 1f else 0.38f
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -826,19 +840,28 @@ private fun SettingsItem(
Icon( Icon(
imageVector = icon, imageVector = icon,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = contentAlpha)
) )
Spacer(modifier = Modifier.width(16.dp)) Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.bodyLarge style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = contentAlpha)
) )
Text( Text(
text = subtitle, text = subtitle,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = contentAlpha)
) )
if (!enabled && disabledHint != null) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = disabledHint,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error.copy(alpha = 0.7f)
)
}
} }
} }
} }
@@ -0,0 +1,7 @@
* More OS download links
* Sub-categories for OS downloads (Desktop, Server, etc.)
* Replaced refresh button with pull-to-refresh gesture
* Disable ISO directory change when root access is not available
* Status card now shows "Tap to request root" and updates when granted
* Improved monet/dynamic theme support for icons and text colors
* Fixed: Sanitize user input for folder creation, IMG file creation, and file renaming