From 84fda70315fe02760096319e15b565f0a20c8f32 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sat, 28 Jun 2025 15:43:07 +0200 Subject: [PATCH 01/16] Start FlatpakUpdater --- pom.xml | 8 +- src/main/java/module-info.java | 7 +- .../linux/update/FlatpakUpdater.java | 90 +++++++++++++++++++ ...ptomator.integrations.update.UpdateService | 1 + 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java create mode 100644 src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService diff --git a/pom.xml b/pom.xml index 79558bd..edc979a 100644 --- a/pom.xml +++ b/pom.xml @@ -40,9 +40,10 @@ - 1.6.0 + 1.7.0-SNAPSHOT 2.0.1-alpha 1.4.0 + 1.0-SNAPSHOT 2.0.17 1.4.2 @@ -88,6 +89,11 @@ libappindicator-gtk3-java-minimal ${appindicator.version} + + org.purejava + flatpak-update-portal + ${flatpakupdateportal.version} + org.junit.jupiter junit-jupiter diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index c728d22..cd4f319 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,13 +3,15 @@ import org.cryptomator.integrations.quickaccess.QuickAccessService; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.tray.TrayMenuController; +import org.cryptomator.integrations.update.UpdateService; import org.cryptomator.linux.autostart.FreedesktopAutoStartService; -import org.cryptomator.linux.keychain.KDEWalletKeychainAccess; import org.cryptomator.linux.keychain.GnomeKeyringKeychainAccess; +import org.cryptomator.linux.keychain.KDEWalletKeychainAccess; import org.cryptomator.linux.quickaccess.DolphinPlaces; import org.cryptomator.linux.quickaccess.NautilusBookmarks; import org.cryptomator.linux.revealpath.DBusSendRevealPathService; import org.cryptomator.linux.tray.AppindicatorTrayMenuController; +import org.cryptomator.linux.update.FlatpakUpdater; module org.cryptomator.integrations.linux { requires org.cryptomator.integrations.api; @@ -17,6 +19,7 @@ requires org.freedesktop.dbus; requires org.purejava.appindicator; requires org.purejava.kwallet; + requires org.purejava.portal; requires de.swiesend.secretservice; requires java.xml; @@ -25,8 +28,10 @@ provides RevealPathService with DBusSendRevealPathService; provides TrayMenuController with AppindicatorTrayMenuController; provides QuickAccessService with NautilusBookmarks, DolphinPlaces; + provides UpdateService with FlatpakUpdater; opens org.cryptomator.linux.tray to org.cryptomator.integrations.api; opens org.cryptomator.linux.quickaccess to org.cryptomator.integrations.api; opens org.cryptomator.linux.autostart to org.cryptomator.integrations.api; + opens org.cryptomator.linux.update to org.cryptomator.integrations.api; } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java new file mode 100644 index 0000000..81ad216 --- /dev/null +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -0,0 +1,90 @@ +package org.cryptomator.linux.update; + +import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.OperatingSystem; +import org.cryptomator.integrations.common.Priority; +import org.cryptomator.integrations.update.UpdateFailedException; +import org.cryptomator.integrations.update.UpdateService; +import org.freedesktop.dbus.exceptions.DBusException; +import org.purejava.portal.Flatpak; +import org.purejava.portal.UpdatePortal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Priority(1000) +@CheckAvailability +@OperatingSystem(OperatingSystem.Value.LINUX) +public class FlatpakUpdater implements UpdateService, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(FlatpakUpdater.class); + + private final UpdatePortal portal; + private Flatpak.UpdateMonitor updateMonitor; + + public FlatpakUpdater() { + this.portal = new UpdatePortal(); + } + + @Override + public boolean isSupported() { + return portal.isAvailable(); + } + + @Override + public String isUpdateAvailable(DistributionChannel channel) { + return ""; + } + + @Override + public void triggerUpdate() throws UpdateFailedException { + getUpdateMonitor(); + //var monitor = getUpdateMonitor(); + //portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY); + } + + @Override + public boolean doesRequireElevatedPermissions() { + return false; + } + + @Override + public String getDisplayName() { + return "Update via Flatpak update"; + } + + @Override + public void close() throws Exception { + try { + if (null != updateMonitor) { + updateMonitor.Close(); + } + portal.close(); + } catch (Exception e) { + LOG.error(e.toString(), e.getCause()); + } + } + + private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { + if (updateMonitor == null) { + var updateMonitorPath = portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY); + if (updateMonitorPath != null) { + LOG.debug("UpdateMonitor successful created at {}", updateMonitorPath); + updateMonitor = portal.getUpdateMonitor(updateMonitorPath.toString()); + try { + portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.UpdateAvailable.class, signal -> { + notifyOnUpdateAvailable(signal); + }); + } catch (DBusException e) { + LOG.error(e.toString(), e.getCause()); + } + } else { + LOG.error("Failed to create UpdateMonitor on DBus"); + } + } + return updateMonitor; + } + + public void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { + LOG.info("Update available to remote-commit {}", signal.update_info.get("remote-commit").getValue()); + } +} diff --git a/src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService b/src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService new file mode 100644 index 0000000..f8d5490 --- /dev/null +++ b/src/main/resources/META-INF/services/org.cryptomator.integrations.update.UpdateService @@ -0,0 +1 @@ +org.cryptomator.linux.update.FlatpakUpdater \ No newline at end of file From 12ee5d26724650e2c7db61097d0074a8dd82dec4 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sat, 5 Jul 2025 18:34:07 +0200 Subject: [PATCH 02/16] Re-spawn application --- .../linux/update/FlatpakUpdater.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 81ad216..1782638 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -5,12 +5,20 @@ import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.update.UpdateFailedException; import org.cryptomator.integrations.update.UpdateService; +import org.freedesktop.dbus.FileDescriptor; import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.types.UInt32; +import org.freedesktop.dbus.types.Variant; import org.purejava.portal.Flatpak; import org.purejava.portal.UpdatePortal; +import org.purejava.portal.Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.List; +import java.util.Map; + @Priority(1000) @CheckAvailability @OperatingSystem(OperatingSystem.Value.LINUX) @@ -37,9 +45,16 @@ public String isUpdateAvailable(DistributionChannel channel) { @Override public void triggerUpdate() throws UpdateFailedException { - getUpdateMonitor(); - //var monitor = getUpdateMonitor(); - //portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY); + var cwdPath = Util.stringToByteList(System.getProperty("user.dir")); + List> argv = List.of( + Util.stringToByteList("org.cryptomator.Cryptomator")); + Map fds = Collections.emptyMap(); + Map envs = Map.of(); + var flags = new UInt32(0); + Map> options = UpdatePortal.OPTIONS_DUMMY; + + spawnApp(cwdPath, argv, fds, envs, flags, options); + } @Override @@ -56,7 +71,7 @@ public String getDisplayName() { public void close() throws Exception { try { if (null != updateMonitor) { - updateMonitor.Close(); + portal.cancelUpdateMonitor(updateMonitor); } portal.close(); } catch (Exception e) { @@ -87,4 +102,14 @@ private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { public void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { LOG.info("Update available to remote-commit {}", signal.update_info.get("remote-commit").getValue()); } + + private UInt32 spawnApp(List cwdPath, List> argv, Map fds, Map envs, UInt32 flags, Map> options) { + var pid = portal.Spawn(cwdPath, argv, fds, envs, flags, options); + if (null != pid) { + LOG.error("Spawning new application failed"); + } else { + LOG.debug("New application spawned with PID {}", pid); + } + return pid; + } } From 10c9d25221b9b37008b6be07c6defeecdd3245b7 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 6 Jul 2025 16:49:21 +0200 Subject: [PATCH 03/16] CreateUpdateMonitor on startup --- src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 1782638..05d1e80 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -31,6 +31,7 @@ public class FlatpakUpdater implements UpdateService, AutoCloseable { public FlatpakUpdater() { this.portal = new UpdatePortal(); + portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY); } @Override From 40d148109a6324aaeb459f97300f0da35b8d1081 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sat, 12 Jul 2025 08:50:09 +0200 Subject: [PATCH 04/16] Run UpdateCheckerTask --- .../linux/update/FlatpakUpdater.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 05d1e80..450c147 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -12,12 +12,15 @@ import org.purejava.portal.Flatpak; import org.purejava.portal.UpdatePortal; import org.purejava.portal.Util; +import org.purejava.portal.rest.UpdateCheckerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; @Priority(1000) @CheckAvailability @@ -25,6 +28,7 @@ public class FlatpakUpdater implements UpdateService, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(FlatpakUpdater.class); + private static final String APP_NAME = "org.cryptomator.Cryptomator"; private final UpdatePortal portal; private Flatpak.UpdateMonitor updateMonitor; @@ -41,6 +45,18 @@ public boolean isSupported() { @Override public String isUpdateAvailable(DistributionChannel channel) { + if (channel != DistributionChannel.LINUX_FLATPAK) { + return ""; + } + try (ExecutorService executor = Executors.newFixedThreadPool(10)) { + var task = new UpdateCheckerTask(APP_NAME); + executor.submit(task); + try { + return task.get(); + } catch (Exception e) { + LOG.error(e.toString(), e.getCause()); + } + } return ""; } @@ -105,12 +121,6 @@ public void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal } private UInt32 spawnApp(List cwdPath, List> argv, Map fds, Map envs, UInt32 flags, Map> options) { - var pid = portal.Spawn(cwdPath, argv, fds, envs, flags, options); - if (null != pid) { - LOG.error("Spawning new application failed"); - } else { - LOG.debug("New application spawned with PID {}", pid); - } - return pid; + return portal.Spawn(cwdPath, argv, fds, envs, flags, options); } } From e28d20a422dfffbde4b1fd998ea357219f6f03fe Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 13 Jul 2025 16:56:27 +0200 Subject: [PATCH 05/16] Use @DistributionChannel --- .../org/cryptomator/linux/update/FlatpakUpdater.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 450c147..948e2f5 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -1,6 +1,7 @@ package org.cryptomator.linux.update; import org.cryptomator.integrations.common.CheckAvailability; +import org.cryptomator.integrations.common.DistributionChannel; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.update.UpdateFailedException; @@ -24,6 +25,7 @@ @Priority(1000) @CheckAvailability +@DistributionChannel(DistributionChannel.Value.LINUX_FLATPAK) @OperatingSystem(OperatingSystem.Value.LINUX) public class FlatpakUpdater implements UpdateService, AutoCloseable { @@ -44,9 +46,10 @@ public boolean isSupported() { } @Override - public String isUpdateAvailable(DistributionChannel channel) { - if (channel != DistributionChannel.LINUX_FLATPAK) { - return ""; + public String isUpdateAvailable(DistributionChannel.Value channel) { + if (channel != DistributionChannel.Value.LINUX_FLATPAK) { + LOG.error("Wrong channel provided: {}", channel); + return null; } try (ExecutorService executor = Executors.newFixedThreadPool(10)) { var task = new UpdateCheckerTask(APP_NAME); @@ -57,7 +60,7 @@ public String isUpdateAvailable(DistributionChannel channel) { LOG.error(e.toString(), e.getCause()); } } - return ""; + return null; } @Override From 096bacd7a4a8c11fc52e2ba0f6ba547d1b3e7a34 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 13 Jul 2025 17:57:26 +0200 Subject: [PATCH 06/16] Use getlatestReleaseFor --- .../cryptomator/linux/update/FlatpakUpdater.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 948e2f5..c9b9c7c 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -13,15 +13,12 @@ import org.purejava.portal.Flatpak; import org.purejava.portal.UpdatePortal; import org.purejava.portal.Util; -import org.purejava.portal.rest.UpdateCheckerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; @Priority(1000) @CheckAvailability @@ -51,16 +48,7 @@ public String isUpdateAvailable(DistributionChannel.Value channel) { LOG.error("Wrong channel provided: {}", channel); return null; } - try (ExecutorService executor = Executors.newFixedThreadPool(10)) { - var task = new UpdateCheckerTask(APP_NAME); - executor.submit(task); - try { - return task.get(); - } catch (Exception e) { - LOG.error(e.toString(), e.getCause()); - } - } - return null; + return portal.getlatestReleaseFor(APP_NAME); } @Override From 7dfa971d5546253cc2aa090f807c58e147a38dc4 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 20 Jul 2025 07:34:54 +0200 Subject: [PATCH 07/16] Use getLatestReleaseChecker from integrations-api --- .../java/org/cryptomator/linux/update/FlatpakUpdater.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index c9b9c7c..4171d4a 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -13,6 +13,7 @@ import org.purejava.portal.Flatpak; import org.purejava.portal.UpdatePortal; import org.purejava.portal.Util; +import org.purejava.portal.rest.UpdateCheckerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,12 +44,13 @@ public boolean isSupported() { } @Override - public String isUpdateAvailable(DistributionChannel.Value channel) { + public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) { if (channel != DistributionChannel.Value.LINUX_FLATPAK) { LOG.error("Wrong channel provided: {}", channel); return null; } - return portal.getlatestReleaseFor(APP_NAME); + portal.setUpdateCheckerTaskFor(APP_NAME); + return portal.getUpdateCheckerTaskFor(APP_NAME); } @Override From 746af268d11b73e643d40e5adb51682c0bb5bb91 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 20 Jul 2025 11:01:07 +0200 Subject: [PATCH 08/16] Trigger updating the app and spawn a new instance of the app --- .../org/cryptomator/linux/update/FlatpakUpdater.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 4171d4a..c528dc0 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -55,16 +55,21 @@ public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value chann @Override public void triggerUpdate() throws UpdateFailedException { + var monitor = getUpdateMonitor(); + portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY); + } + + @Override + public long spawnApp() { var cwdPath = Util.stringToByteList(System.getProperty("user.dir")); List> argv = List.of( - Util.stringToByteList("org.cryptomator.Cryptomator")); + Util.stringToByteList(APP_NAME)); Map fds = Collections.emptyMap(); Map envs = Map.of(); var flags = new UInt32(0); Map> options = UpdatePortal.OPTIONS_DUMMY; - spawnApp(cwdPath, argv, fds, envs, flags, options); - + return spawnApp(cwdPath, argv, fds, envs, flags, options).longValue(); } @Override From f1d12e8907ab2c2c2ed65ac311637c934d188af1 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 20 Jul 2025 19:23:06 +0200 Subject: [PATCH 09/16] Use Listeners from integrations-api --- .../linux/update/FlatpakUpdater.java | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index c528dc0..10c485b 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -4,6 +4,10 @@ import org.cryptomator.integrations.common.DistributionChannel; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; +import org.cryptomator.integrations.update.Progress; +import org.cryptomator.integrations.update.ProgressListener; +import org.cryptomator.integrations.update.UpdateAvailable; +import org.cryptomator.integrations.update.UpdateAvailableListener; import org.cryptomator.integrations.update.UpdateFailedException; import org.cryptomator.integrations.update.UpdateService; import org.freedesktop.dbus.FileDescriptor; @@ -20,6 +24,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; @Priority(1000) @CheckAvailability @@ -30,6 +35,9 @@ public class FlatpakUpdater implements UpdateService, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(FlatpakUpdater.class); private static final String APP_NAME = "org.cryptomator.Cryptomator"; + private final List updateAvailableListeners = new CopyOnWriteArrayList<>(); + private final List progressListeners = new CopyOnWriteArrayList<>(); + private final UpdatePortal portal; private Flatpak.UpdateMonitor updateMonitor; @@ -104,6 +112,9 @@ private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.UpdateAvailable.class, signal -> { notifyOnUpdateAvailable(signal); }); + portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.Progress.class, signal -> { + notifyOnUpdateProceeds(signal); + }); } catch (DBusException e) { LOG.error(e.toString(), e.getCause()); } @@ -114,8 +125,40 @@ private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { return updateMonitor; } - public void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { - LOG.info("Update available to remote-commit {}", signal.update_info.get("remote-commit").getValue()); + @Override + public void addUpdateAvailableListener(UpdateAvailableListener listener) { + updateAvailableListeners.add(listener); + } + + @Override + public void removeUpdateAvailableListener(UpdateAvailableListener listener) { + updateAvailableListeners.remove(listener); + } + + private void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { + UpdateAvailable updateAvailable = new UpdateAvailable((String) signal.update_info.get("remote-commit").getValue()); + for (UpdateAvailableListener listener : updateAvailableListeners) { + listener.onUpdateAvailable(updateAvailable); + } + } + + @Override + public void addProgressListener(ProgressListener listener) { + progressListeners.add(listener); + } + + @Override + public void removeProgressListener(ProgressListener listener) { + progressListeners.remove(listener); + } + + private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { + long status = ((UInt32) signal.info.get("status").getValue()).longValue(); + long progress = ((UInt32) signal.info.get("progress").getValue()).longValue(); + Progress p = new Progress(status, progress); + for (ProgressListener listener : progressListeners) { + listener.onProgress(p); + } } private UInt32 spawnApp(List cwdPath, List> argv, Map fds, Map envs, UInt32 flags, Map> options) { From 467a86b721a411ce226e4ee92d1a569e8e8c71f0 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Tue, 22 Jul 2025 18:57:05 +0200 Subject: [PATCH 10/16] Guard process, as it is not always set --- .../org/cryptomator/linux/update/FlatpakUpdater.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 10c485b..4bdb8de 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -153,9 +153,13 @@ public void removeProgressListener(ProgressListener listener) { } private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { - long status = ((UInt32) signal.info.get("status").getValue()).longValue(); - long progress = ((UInt32) signal.info.get("progress").getValue()).longValue(); - Progress p = new Progress(status, progress); + long status = ((UInt32) signal.info.get("status").getValue()).longValue(); + long progress = 0; + Variant progressVariant = signal.info.get("progress"); + if (null != progressVariant) { + progress = ((UInt32) progressVariant.getValue()).longValue(); + } + Progress p = new Progress(status, progress); for (ProgressListener listener : progressListeners) { listener.onProgress(p); } From 77edc0df99f924f34b9e8e41e0de177a283538d5 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sun, 27 Jul 2025 17:15:33 +0200 Subject: [PATCH 11/16] Spawn latest update --- src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 4bdb8de..70997a4 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -15,6 +15,7 @@ import org.freedesktop.dbus.types.UInt32; import org.freedesktop.dbus.types.Variant; import org.purejava.portal.Flatpak; +import org.purejava.portal.FlatpakSpawnFlag; import org.purejava.portal.UpdatePortal; import org.purejava.portal.Util; import org.purejava.portal.rest.UpdateCheckerTask; @@ -74,7 +75,7 @@ public long spawnApp() { Util.stringToByteList(APP_NAME)); Map fds = Collections.emptyMap(); Map envs = Map.of(); - var flags = new UInt32(0); + UInt32 flags = new UInt32(FlatpakSpawnFlag.LATEST_VERSION.getValue()); Map> options = UpdatePortal.OPTIONS_DUMMY; return spawnApp(cwdPath, argv, fds, envs, flags, options).longValue(); From 549c46f61498d00a70ef7aa01cb616f04192c7fa Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Wed, 30 Jul 2025 06:31:20 +0200 Subject: [PATCH 12/16] Complete signals --- .../linux/update/FlatpakUpdater.java | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 70997a4..ad8213c 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -6,6 +6,8 @@ import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.update.Progress; import org.cryptomator.integrations.update.ProgressListener; +import org.cryptomator.integrations.update.SpawnExitedListener; +import org.cryptomator.integrations.update.SpawnStartedListener; import org.cryptomator.integrations.update.UpdateAvailable; import org.cryptomator.integrations.update.UpdateAvailableListener; import org.cryptomator.integrations.update.UpdateFailedException; @@ -38,6 +40,8 @@ public class FlatpakUpdater implements UpdateService, AutoCloseable { private final List updateAvailableListeners = new CopyOnWriteArrayList<>(); private final List progressListeners = new CopyOnWriteArrayList<>(); + private final List spawnStartedListeners = new CopyOnWriteArrayList<>(); + private final List spawnExitedListeners = new CopyOnWriteArrayList<>(); private final UpdatePortal portal; private Flatpak.UpdateMonitor updateMonitor; @@ -136,13 +140,6 @@ public void removeUpdateAvailableListener(UpdateAvailableListener listener) { updateAvailableListeners.remove(listener); } - private void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { - UpdateAvailable updateAvailable = new UpdateAvailable((String) signal.update_info.get("remote-commit").getValue()); - for (UpdateAvailableListener listener : updateAvailableListeners) { - listener.onUpdateAvailable(updateAvailable); - } - } - @Override public void addProgressListener(ProgressListener listener) { progressListeners.add(listener); @@ -153,6 +150,48 @@ public void removeProgressListener(ProgressListener listener) { progressListeners.remove(listener); } + @Override + public void addSpawnStartedListener(SpawnStartedListener listener) { + spawnStartedListeners.add(listener); + } + + @Override + public void removeSpawnStartedListener(SpawnStartedListener listener) { + spawnStartedListeners.remove(listener); + } + + @Override + public void addSpawnExitedListener(SpawnExitedListener listener) { + spawnExitedListeners.add(listener); + } + + @Override + public void removeSpawnExitedListener(SpawnExitedListener listener) { + spawnExitedListeners.remove(listener); + } + + private void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { + String remoteCommit = ""; + Variant remoteCommitVariant = signal.update_info.get("remote-commit"); + if (null != remoteCommitVariant) { + remoteCommit = (String) remoteCommitVariant.getValue(); + } + String runningCommit = ""; + Variant runningCommitVariant = signal.update_info.get("running-commit"); + if (null != runningCommitVariant) { + runningCommit = (String) runningCommitVariant.getValue(); + } + String localCommit = ""; + Variant localCommitVariant = signal.update_info.get("local-commit"); + if (null != localCommitVariant) { + localCommit = (String) localCommitVariant.getValue(); + } + UpdateAvailable updateAvailable = new UpdateAvailable(runningCommit, localCommit, remoteCommit); + for (UpdateAvailableListener listener : updateAvailableListeners) { + listener.onUpdateAvailable(updateAvailable); + } + } + private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { long status = ((UInt32) signal.info.get("status").getValue()).longValue(); long progress = 0; @@ -160,7 +199,27 @@ private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { if (null != progressVariant) { progress = ((UInt32) progressVariant.getValue()).longValue(); } - Progress p = new Progress(status, progress); + long nOps = -1; + Variant nOpsVariant = signal.info.get("n_ops"); + if (null != nOpsVariant) { + nOps = ((UInt32) nOpsVariant.getValue()).longValue(); + } + long oP = -1; + Variant oPVariant = signal.info.get("op"); + if (null != oPVariant) { + oP = ((UInt32) oPVariant.getValue()).longValue(); + } + String error = ""; + Variant errorVariant = signal.info.get("error"); + if (null != errorVariant) { + error = (String) errorVariant.getValue(); + } + String errorMessage = ""; + Variant errorMessageVariant = signal.info.get("error_message"); + if (null != errorMessageVariant) { + errorMessage = (String) errorMessageVariant.getValue(); + } + Progress p = new Progress(nOps, oP, status, progress, error, errorMessage); for (ProgressListener listener : progressListeners) { listener.onProgress(p); } From a2607d7f4806785502085ebfb01df3738e518fe1 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Wed, 30 Jul 2025 19:18:05 +0200 Subject: [PATCH 13/16] Change flatpak-update-portal version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index edc979a..506900e 100644 --- a/pom.xml +++ b/pom.xml @@ -43,7 +43,7 @@ 1.7.0-SNAPSHOT 2.0.1-alpha 1.4.0 - 1.0-SNAPSHOT + 1.0.0 2.0.17 1.4.2 From 4976ddedadba1b7edffcdda4986c0923bff8acb3 Mon Sep 17 00:00:00 2001 From: Ralph Plawetzki Date: Sat, 2 Aug 2025 11:31:11 +0200 Subject: [PATCH 14/16] Correlate with API as suggested in a separate PoC: UpdateMechanism and UpdateProcess --- .../linux/update/FlatpakUpdater.java | 99 +------------------ 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index ad8213c..0017671 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -1,15 +1,11 @@ package org.cryptomator.linux.update; import org.cryptomator.integrations.common.CheckAvailability; -import org.cryptomator.integrations.common.DistributionChannel; +import org.cryptomator.integrations.common.DisplayName; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; import org.cryptomator.integrations.update.Progress; import org.cryptomator.integrations.update.ProgressListener; -import org.cryptomator.integrations.update.SpawnExitedListener; -import org.cryptomator.integrations.update.SpawnStartedListener; -import org.cryptomator.integrations.update.UpdateAvailable; -import org.cryptomator.integrations.update.UpdateAvailableListener; import org.cryptomator.integrations.update.UpdateFailedException; import org.cryptomator.integrations.update.UpdateService; import org.freedesktop.dbus.FileDescriptor; @@ -31,17 +27,14 @@ @Priority(1000) @CheckAvailability -@DistributionChannel(DistributionChannel.Value.LINUX_FLATPAK) +@DisplayName("Update via Flatpak update") @OperatingSystem(OperatingSystem.Value.LINUX) public class FlatpakUpdater implements UpdateService, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(FlatpakUpdater.class); private static final String APP_NAME = "org.cryptomator.Cryptomator"; - private final List updateAvailableListeners = new CopyOnWriteArrayList<>(); private final List progressListeners = new CopyOnWriteArrayList<>(); - private final List spawnStartedListeners = new CopyOnWriteArrayList<>(); - private final List spawnExitedListeners = new CopyOnWriteArrayList<>(); private final UpdatePortal portal; private Flatpak.UpdateMonitor updateMonitor; @@ -57,11 +50,7 @@ public boolean isSupported() { } @Override - public UpdateCheckerTask getLatestReleaseChecker(DistributionChannel.Value channel) { - if (channel != DistributionChannel.Value.LINUX_FLATPAK) { - LOG.error("Wrong channel provided: {}", channel); - return null; - } + public UpdateCheckerTask getLatestReleaseChecker() { portal.setUpdateCheckerTaskFor(APP_NAME); return portal.getUpdateCheckerTaskFor(APP_NAME); } @@ -90,11 +79,6 @@ public boolean doesRequireElevatedPermissions() { return false; } - @Override - public String getDisplayName() { - return "Update via Flatpak update"; - } - @Override public void close() throws Exception { try { @@ -114,9 +98,6 @@ private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { LOG.debug("UpdateMonitor successful created at {}", updateMonitorPath); updateMonitor = portal.getUpdateMonitor(updateMonitorPath.toString()); try { - portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.UpdateAvailable.class, signal -> { - notifyOnUpdateAvailable(signal); - }); portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.Progress.class, signal -> { notifyOnUpdateProceeds(signal); }); @@ -130,16 +111,6 @@ private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { return updateMonitor; } - @Override - public void addUpdateAvailableListener(UpdateAvailableListener listener) { - updateAvailableListeners.add(listener); - } - - @Override - public void removeUpdateAvailableListener(UpdateAvailableListener listener) { - updateAvailableListeners.remove(listener); - } - @Override public void addProgressListener(ProgressListener listener) { progressListeners.add(listener); @@ -150,48 +121,6 @@ public void removeProgressListener(ProgressListener listener) { progressListeners.remove(listener); } - @Override - public void addSpawnStartedListener(SpawnStartedListener listener) { - spawnStartedListeners.add(listener); - } - - @Override - public void removeSpawnStartedListener(SpawnStartedListener listener) { - spawnStartedListeners.remove(listener); - } - - @Override - public void addSpawnExitedListener(SpawnExitedListener listener) { - spawnExitedListeners.add(listener); - } - - @Override - public void removeSpawnExitedListener(SpawnExitedListener listener) { - spawnExitedListeners.remove(listener); - } - - private void notifyOnUpdateAvailable(Flatpak.UpdateMonitor.UpdateAvailable signal) { - String remoteCommit = ""; - Variant remoteCommitVariant = signal.update_info.get("remote-commit"); - if (null != remoteCommitVariant) { - remoteCommit = (String) remoteCommitVariant.getValue(); - } - String runningCommit = ""; - Variant runningCommitVariant = signal.update_info.get("running-commit"); - if (null != runningCommitVariant) { - runningCommit = (String) runningCommitVariant.getValue(); - } - String localCommit = ""; - Variant localCommitVariant = signal.update_info.get("local-commit"); - if (null != localCommitVariant) { - localCommit = (String) localCommitVariant.getValue(); - } - UpdateAvailable updateAvailable = new UpdateAvailable(runningCommit, localCommit, remoteCommit); - for (UpdateAvailableListener listener : updateAvailableListeners) { - listener.onUpdateAvailable(updateAvailable); - } - } - private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { long status = ((UInt32) signal.info.get("status").getValue()).longValue(); long progress = 0; @@ -199,27 +128,7 @@ private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { if (null != progressVariant) { progress = ((UInt32) progressVariant.getValue()).longValue(); } - long nOps = -1; - Variant nOpsVariant = signal.info.get("n_ops"); - if (null != nOpsVariant) { - nOps = ((UInt32) nOpsVariant.getValue()).longValue(); - } - long oP = -1; - Variant oPVariant = signal.info.get("op"); - if (null != oPVariant) { - oP = ((UInt32) oPVariant.getValue()).longValue(); - } - String error = ""; - Variant errorVariant = signal.info.get("error"); - if (null != errorVariant) { - error = (String) errorVariant.getValue(); - } - String errorMessage = ""; - Variant errorMessageVariant = signal.info.get("error_message"); - if (null != errorMessageVariant) { - errorMessage = (String) errorMessageVariant.getValue(); - } - Progress p = new Progress(nOps, oP, status, progress, error, errorMessage); + Progress p = new Progress(status, progress); for (ProgressListener listener : progressListeners) { listener.onProgress(p); } From 2aa7b15f8def3f00a0dcf51cd49d3e34c00f4ec6 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 8 Aug 2025 18:19:11 +0200 Subject: [PATCH 15/16] hide internals behind new API facade --- src/main/java/module-info.java | 4 +- .../linux/update/FlatpakUpdater.java | 210 ++++++++++++------ 2 files changed, 138 insertions(+), 76 deletions(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index cd4f319..9d98f16 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,7 +3,7 @@ import org.cryptomator.integrations.quickaccess.QuickAccessService; import org.cryptomator.integrations.revealpath.RevealPathService; import org.cryptomator.integrations.tray.TrayMenuController; -import org.cryptomator.integrations.update.UpdateService; +import org.cryptomator.integrations.update.UpdateMechanism; import org.cryptomator.linux.autostart.FreedesktopAutoStartService; import org.cryptomator.linux.keychain.GnomeKeyringKeychainAccess; import org.cryptomator.linux.keychain.KDEWalletKeychainAccess; @@ -28,7 +28,7 @@ provides RevealPathService with DBusSendRevealPathService; provides TrayMenuController with AppindicatorTrayMenuController; provides QuickAccessService with NautilusBookmarks, DolphinPlaces; - provides UpdateService with FlatpakUpdater; + provides UpdateMechanism with FlatpakUpdater; opens org.cryptomator.linux.tray to org.cryptomator.integrations.api; opens org.cryptomator.linux.quickaccess to org.cryptomator.integrations.api; diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 0017671..6f5118c 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -4,10 +4,9 @@ import org.cryptomator.integrations.common.DisplayName; import org.cryptomator.integrations.common.OperatingSystem; import org.cryptomator.integrations.common.Priority; -import org.cryptomator.integrations.update.Progress; -import org.cryptomator.integrations.update.ProgressListener; import org.cryptomator.integrations.update.UpdateFailedException; -import org.cryptomator.integrations.update.UpdateService; +import org.cryptomator.integrations.update.UpdateMechanism; +import org.cryptomator.integrations.update.UpdateProcess; import org.freedesktop.dbus.FileDescriptor; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.types.UInt32; @@ -16,125 +15,188 @@ import org.purejava.portal.FlatpakSpawnFlag; import org.purejava.portal.UpdatePortal; import org.purejava.portal.Util; -import org.purejava.portal.rest.UpdateCheckerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; @Priority(1000) @CheckAvailability @DisplayName("Update via Flatpak update") @OperatingSystem(OperatingSystem.Value.LINUX) -public class FlatpakUpdater implements UpdateService, AutoCloseable { +public class FlatpakUpdater implements UpdateMechanism { private static final Logger LOG = LoggerFactory.getLogger(FlatpakUpdater.class); private static final String APP_NAME = "org.cryptomator.Cryptomator"; - private final List progressListeners = new CopyOnWriteArrayList<>(); - private final UpdatePortal portal; - private Flatpak.UpdateMonitor updateMonitor; public FlatpakUpdater() { this.portal = new UpdatePortal(); portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY); } - @Override + @CheckAvailability public boolean isSupported() { return portal.isAvailable(); } @Override - public UpdateCheckerTask getLatestReleaseChecker() { + public boolean isUpdateAvailable() { + var cdl = new CountDownLatch(1); portal.setUpdateCheckerTaskFor(APP_NAME); - return portal.getUpdateCheckerTaskFor(APP_NAME); + var checkTask = portal.getUpdateCheckerTaskFor(APP_NAME); + var updateAvailable = new AtomicBoolean(false); + checkTask.setOnSucceeded(latestVersion -> { + updateAvailable.set(true); // TODO: compare version strings before setting this to true + cdl.countDown(); + }); + checkTask.setOnFailed(error -> { + LOG.warn("Error while checking for updates.", error); + cdl.countDown(); + }); + try { + cdl.await(); + return updateAvailable.get(); + } catch (InterruptedException e) { + checkTask.cancel(); + Thread.currentThread().interrupt(); + return false; + } } @Override - public void triggerUpdate() throws UpdateFailedException { - var monitor = getUpdateMonitor(); - portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY); - } + public UpdateProcess prepareUpdate() throws UpdateFailedException { + var monitorPath = portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY); + if (monitorPath == null) { + throw new UpdateFailedException("Failed to create UpdateMonitor on DBus"); + } - @Override - public long spawnApp() { - var cwdPath = Util.stringToByteList(System.getProperty("user.dir")); - List> argv = List.of( - Util.stringToByteList(APP_NAME)); - Map fds = Collections.emptyMap(); - Map envs = Map.of(); - UInt32 flags = new UInt32(FlatpakSpawnFlag.LATEST_VERSION.getValue()); - Map> options = UpdatePortal.OPTIONS_DUMMY; - - return spawnApp(cwdPath, argv, fds, envs, flags, options).longValue(); + return new FlatpakUpdateProcess(portal.getUpdateMonitor(monitorPath.toString())); } - @Override - public boolean doesRequireElevatedPermissions() { - return false; - } + private class FlatpakUpdateProcess implements UpdateProcess { - @Override - public void close() throws Exception { - try { - if (null != updateMonitor) { - portal.cancelUpdateMonitor(updateMonitor); + private final CountDownLatch latch = new CountDownLatch(1); + private final Flatpak.UpdateMonitor monitor; + private volatile double progress = 0.0; + private volatile UpdateFailedException error; + private AutoCloseable signalHandler; + + private FlatpakUpdateProcess(Flatpak.UpdateMonitor monitor) { + this.monitor = monitor; + startUpdate(); + } + + private void startUpdate() { + try { + this.signalHandler = portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.Progress.class, this::handleProgressSignal); + } catch (DBusException e) { + LOG.error("DBus error", e); + latch.countDown(); + } + portal.updateApp("x11:0", monitor, UpdatePortal.OPTIONS_DUMMY); + } + + private void handleProgressSignal(Flatpak.UpdateMonitor.Progress signal) { + int status = ((UInt32) signal.info.get("status").getValue()).intValue(); + switch (status) { + case 0 -> { // In progress + Variant progressVariant = signal.info.get("progress"); + if (progressVariant != null) { + progress = ((UInt32) progressVariant.getValue()).doubleValue() / 100.0; // progress reported as int in range [0, 100] + } + } + case 1 -> { // No update available + error = new UpdateFailedException("No update available"); + latch.countDown(); + } + case 2 -> { // Update complete + progress = 1.0; + latch.countDown(); + } + case 3 -> { // Update failed + error = new UpdateFailedException("Update preparation failed"); + latch.countDown(); + } + default -> { + error = new UpdateFailedException("Unknown update status " + status); + latch.countDown(); + } } - portal.close(); - } catch (Exception e) { - LOG.error(e.toString(), e.getCause()); } - } - private synchronized Flatpak.UpdateMonitor getUpdateMonitor() { - if (updateMonitor == null) { - var updateMonitorPath = portal.CreateUpdateMonitor(UpdatePortal.OPTIONS_DUMMY); - if (updateMonitorPath != null) { - LOG.debug("UpdateMonitor successful created at {}", updateMonitorPath); - updateMonitor = portal.getUpdateMonitor(updateMonitorPath.toString()); + private void stopReceivingSignals() { + if (signalHandler != null) { try { - portal.getDBusConnection().addSigHandler(Flatpak.UpdateMonitor.Progress.class, signal -> { - notifyOnUpdateProceeds(signal); - }); - } catch (DBusException e) { - LOG.error(e.toString(), e.getCause()); + signalHandler.close(); + } catch (Exception e) { + LOG.error("Failed to close signal handler", e); } - } else { - LOG.error("Failed to create UpdateMonitor on DBus"); + signalHandler = null; } } - return updateMonitor; - } - @Override - public void addProgressListener(ProgressListener listener) { - progressListeners.add(listener); - } + @Override + public double preparationProgress() { + return progress; + } - @Override - public void removeProgressListener(ProgressListener listener) { - progressListeners.remove(listener); - } + @Override + public void cancel() { + portal.cancelUpdateMonitor(monitor); + stopReceivingSignals(); + portal.close(); // TODO: is this right? belongs to parent class. update can not be retried afterwards. or should each process have its own portal instance? + error = new UpdateFailedException("Update cancelled by user"); + } - private void notifyOnUpdateProceeds(Flatpak.UpdateMonitor.Progress signal) { - long status = ((UInt32) signal.info.get("status").getValue()).longValue(); - long progress = 0; - Variant progressVariant = signal.info.get("progress"); - if (null != progressVariant) { - progress = ((UInt32) progressVariant.getValue()).longValue(); + @Override + public void await() throws InterruptedException { + latch.await(); } - Progress p = new Progress(status, progress); - for (ProgressListener listener : progressListeners) { - listener.onProgress(p); + + @Override + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return latch.await(timeout, unit); + } + + private boolean isDone() { + try { + return latch.await(0, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } } - } - private UInt32 spawnApp(List cwdPath, List> argv, Map fds, Map envs, UInt32 flags, Map> options) { - return portal.Spawn(cwdPath, argv, fds, envs, flags, options); + @Override + public ProcessHandle applyUpdate() throws IllegalStateException, IOException { + if (!isDone()) { + throw new IllegalStateException("Update preparation is not complete"); + } + stopReceivingSignals(); + if (error != null) { + throw error; + } + + // spawn new Cryptomator process: + var cwdPath = Util.stringToByteList(System.getProperty("user.dir")); + List> argv = List.of( + Util.stringToByteList(APP_NAME)); + Map fds = Collections.emptyMap(); + Map envs = Map.of(); + UInt32 flags = new UInt32(FlatpakSpawnFlag.LATEST_VERSION.getValue()); + Map> options = UpdatePortal.OPTIONS_DUMMY; + var pid = portal.Spawn(cwdPath, argv, fds, envs, flags, options).longValue(); + return ProcessHandle.of(pid).orElseThrow(); + } } + } From 08a77420adb574e4279abd4bfeabdb89561ec19f Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Sat, 9 Aug 2025 12:36:05 +0200 Subject: [PATCH 16/16] compare versions --- .../java/org/cryptomator/linux/update/FlatpakUpdater.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java index 6f5118c..f622e28 100644 --- a/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java +++ b/src/main/java/org/cryptomator/linux/update/FlatpakUpdater.java @@ -49,13 +49,13 @@ public boolean isSupported() { } @Override - public boolean isUpdateAvailable() { + public boolean isUpdateAvailable(String installedVersion) { var cdl = new CountDownLatch(1); portal.setUpdateCheckerTaskFor(APP_NAME); var checkTask = portal.getUpdateCheckerTaskFor(APP_NAME); var updateAvailable = new AtomicBoolean(false); - checkTask.setOnSucceeded(latestVersion -> { - updateAvailable.set(true); // TODO: compare version strings before setting this to true + checkTask.setOnSucceeded(updateVersion -> { + updateAvailable.set(UpdateMechanism.isUpdateAvailable(updateVersion, installedVersion)); cdl.countDown(); }); checkTask.setOnFailed(error -> {