Skip to content

Commit 10f49f8

Browse files
committed
Merge main into flatpak-pr
1 parent ae326d1 commit 10f49f8

1 file changed

Lines changed: 15 additions & 43 deletions

File tree

core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DesktopInstaller(
3939
private val isRunningInFlatpak: Boolean by lazy {
4040
try {
4141
File("/.flatpak-info").exists() ||
42-
System.getenv("FLATPAK_ID") != null
42+
System.getenv("FLATPAK_ID") != null
4343
} catch (_: Exception) {
4444
false
4545
}
@@ -67,22 +67,16 @@ class DesktopInstaller(
6767
}
6868

6969
override fun openApp(packageName: String): Boolean {
70-
// Desktop apps are launched differently per platform
7170
Logger.d { "Open app not supported on desktop for: $packageName" }
7271
return false
7372
}
7473

7574
override fun openWithExternalInstaller(filePath: String) {
76-
// Not applicable on desktop
7775
}
7876

7977
override fun isAssetInstallable(assetName: String): Boolean {
8078
val name = assetName.lowercase()
8179

82-
// In Flatpak, only allow downloading — we can't actually install system packages.
83-
// AppImages also can't run inside the sandbox (no FUSE).
84-
// We still mark them as "installable" so the user can download them,
85-
// but installation will hand off to the host system.
8680
val hasValidExtension =
8781
when (platform) {
8882
Platform.ANDROID -> {
@@ -126,9 +120,6 @@ class DesktopInstaller(
126120

127121
Platform.LINUX -> {
128122
if (isRunningInFlatpak) {
129-
// In Flatpak, prefer native packages over AppImages since AppImages
130-
// need FUSE (unavailable in sandbox). The user will install from
131-
// their file manager on the host, where DEB/RPM work natively.
132123
when (linuxPackageType) {
133124
LinuxPackageType.DEB -> listOf(".deb", ".appimage", ".rpm")
134125
LinuxPackageType.RPM -> listOf(".rpm", ".appimage", ".deb")
@@ -202,8 +193,6 @@ class DesktopInstaller(
202193
private fun determineLinuxPackageType(): LinuxPackageType {
203194
if (platform != Platform.LINUX) return LinuxPackageType.UNIVERSAL
204195

205-
// Inside Flatpak, /etc/os-release belongs to the runtime (org.freedesktop.Platform),
206-
// not the host OS. We need to read the host's os-release instead.
207196
if (isRunningInFlatpak) {
208197
return try {
209198
detectHostLinuxPackageType()
@@ -394,9 +383,6 @@ class DesktopInstaller(
394383
withContext(Dispatchers.IO) {
395384
val ext = extOrMime.lowercase().removePrefix(".")
396385

397-
// In Flatpak we don't need to check executable permissions — we won't be
398-
// running anything ourselves. The file is downloaded to xdg-download and
399-
// the user installs from their host file manager.
400386
if (isRunningInFlatpak) {
401387
Logger.d { "Running in Flatpak — skipping permission checks for .$ext" }
402388
return@withContext
@@ -410,7 +396,7 @@ class DesktopInstaller(
410396
if (!canSetExecutable) {
411397
throw IllegalStateException(
412398
"Unable to set executable permissions. AppImage installation requires " +
413-
"the ability to make files executable.",
399+
"the ability to make files executable.",
414400
)
415401
}
416402
} finally {
@@ -431,7 +417,6 @@ class DesktopInstaller(
431417
}
432418

433419
override fun uninstall(packageName: String) {
434-
// Desktop doesn't have a unified uninstall mechanism
435420
Logger.d { "Uninstall not supported on desktop for: $packageName" }
436421
}
437422

@@ -447,17 +432,10 @@ class DesktopInstaller(
447432

448433
val ext = extOrMime.lowercase().removePrefix(".")
449434

450-
// Inside the Flatpak sandbox we cannot:
451-
// - Run pkexec/sudo (no privilege escalation)
452-
// - Access system package managers (apt, dnf, rpm, etc.)
453-
// - Mount AppImages (no FUSE / /dev/fuse)
454-
// - Open terminal emulators (not in the sandbox)
455-
// Instead, we open the downloaded file in the host's default file manager
456-
// via the xdg-open portal, so the user can install it natively.
457-
if (isRunningInFlatpak) {
458-
installFromFlatpak(file, ext)
459-
return@withContext
460-
}
435+
if (isRunningInFlatpak) {
436+
installFromFlatpak(file, ext)
437+
return@withContext InstallOutcome.DELEGATED_TO_SYSTEM
438+
}
461439

462440
when (platform) {
463441
Platform.WINDOWS -> installWindows(file, ext)
@@ -467,8 +445,8 @@ class DesktopInstaller(
467445
}
468446

469447
InstallOutcome.DELEGATED_TO_SYSTEM
470-
}
471448

449+
}
472450

473451
/**
474452
* Flatpak-sandboxed installation flow.
@@ -486,9 +464,6 @@ class DesktopInstaller(
486464

487465
when (ext) {
488466
"deb", "rpm" -> {
489-
// xdg-open goes through the Flatpak portal → opens on the host.
490-
// On most distros, .deb/.rpm files open in GNOME Software, KDE Discover,
491-
// or the default package installer.
492467
Logger.d { "Opening .$ext package via xdg-open portal for host installation" }
493468
try {
494469
val process = ProcessBuilder("xdg-open", file.absolutePath).start()
@@ -498,7 +473,7 @@ class DesktopInstaller(
498473
showFlatpakNotification(
499474
title = "Package Ready to Install",
500475
message = "The ${ext.uppercase()} package has been opened in your system's " +
501-
"software installer. Follow the prompts to complete installation.",
476+
"software installer. Follow the prompts to complete installation.",
502477
)
503478
} else {
504479
Logger.w { "xdg-open exited with code $exitCode" }
@@ -519,12 +494,8 @@ class DesktopInstaller(
519494
}
520495

521496
"appimage" -> {
522-
// AppImages can't run inside Flatpak (no FUSE), and there's no point
523-
// moving them to ~/Applications from within the sandbox.
524-
// Instead, set executable and open the file manager so the user can find it.
525497
Logger.d { "AppImage downloaded in Flatpak — preparing for host launch" }
526498

527-
// Try to make it executable (may work if it's on a filesystem we can chmod)
528499
try {
529500
file.setExecutable(true, false)
530501
Logger.d { "Set executable permission on AppImage" }
@@ -537,7 +508,6 @@ class DesktopInstaller(
537508
message = "Right-click → Properties → mark as executable, then double-click to run.",
538509
)
539510

540-
// Open the file manager highlighting the downloaded file
541511
openInFileManager(file)
542512
}
543513

@@ -586,8 +556,6 @@ class DesktopInstaller(
586556
*/
587557
private fun openInFileManager(file: File) {
588558
try {
589-
// D-Bus call to org.freedesktop.FileManager1.ShowItems — this highlights
590-
// the specific file in the file manager. Works via Flatpak portal.
591559
val fileUri = "file://${file.absolutePath}"
592560
val process = ProcessBuilder(
593561
"gdbus", "call",
@@ -608,7 +576,6 @@ class DesktopInstaller(
608576
Logger.w { "D-Bus ShowItems not available: ${e.message}" }
609577
}
610578

611-
// Fallback: open the parent directory
612579
try {
613580
val parentDir = file.parentFile ?: return
614581
ProcessBuilder("xdg-open", parentDir.absolutePath).start()
@@ -752,7 +719,12 @@ class DesktopInstaller(
752719
val installMethods =
753720
listOf(
754721
listOf("pkexec", "apt", "install", "-y", file.absolutePath),
755-
listOf("pkexec", "sh", "-c", "dpkg -i '${file.absolutePath}' || apt-get install -f -y"),
722+
listOf(
723+
"pkexec",
724+
"sh",
725+
"-c",
726+
"dpkg -i '${file.absolutePath}' || apt-get install -f -y"
727+
),
756728
listOf("gdebi-gtk", file.absolutePath),
757729
null,
758730
)
@@ -1144,7 +1116,7 @@ class DesktopInstaller(
11441116
e.printStackTrace()
11451117
throw IllegalStateException(
11461118
"Failed to install AppImage: ${e.message}. " +
1147-
"Please ensure you have write permissions to ~/Applications folder.",
1119+
"Please ensure you have write permissions to ~/Applications folder.",
11481120
e,
11491121
)
11501122
} catch (e: SecurityException) {

0 commit comments

Comments
 (0)