From 1ca0c433c46d91bc1a2610f2f8d22b077187b233 Mon Sep 17 00:00:00 2001 From: crschnick Date: Wed, 30 Jul 2025 14:28:51 +0000 Subject: [PATCH] Rework --- .../java/io/xpipe/app/core/AppProperties.java | 2 + .../java/io/xpipe/app/core/AppSystemInfo.java | 32 ++++++++++ .../java/io/xpipe/app/ext/SetupProvider.java | 38 ++++++++++++ .../app/ext/SetupToolActionProvider.java | 59 +++++++++++++++++++ .../xpipe/app/hub/comp/StoreCreationMenu.java | 24 ++++++++ .../app/util/GithubReleaseDownloader.java | 13 ++++ app/src/main/java/module-info.java | 5 +- lang/strings/translations_en.properties | 1 + 8 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/io/xpipe/app/core/AppSystemInfo.java create mode 100644 app/src/main/java/io/xpipe/app/ext/SetupProvider.java create mode 100644 app/src/main/java/io/xpipe/app/ext/SetupToolActionProvider.java diff --git a/app/src/main/java/io/xpipe/app/core/AppProperties.java b/app/src/main/java/io/xpipe/app/core/AppProperties.java index f5ee5690d..09461c2bd 100644 --- a/app/src/main/java/io/xpipe/app/core/AppProperties.java +++ b/app/src/main/java/io/xpipe/app/core/AppProperties.java @@ -38,6 +38,7 @@ public class AppProperties { boolean debugThreads; Path dataDir; Path defaultDataDir; + Path dataBinDir; boolean showcase; AppVersion canonicalVersion; boolean locatePtb; @@ -124,6 +125,7 @@ public class AppProperties { return p; }) .orElse(defaultDataDir); + dataBinDir = dataDir.resolve("bin"); showcase = Optional.ofNullable(System.getProperty("io.xpipe.app.showcase")) .map(Boolean::parseBoolean) .orElse(false); diff --git a/app/src/main/java/io/xpipe/app/core/AppSystemInfo.java b/app/src/main/java/io/xpipe/app/core/AppSystemInfo.java new file mode 100644 index 000000000..acf0123ee --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppSystemInfo.java @@ -0,0 +1,32 @@ +package io.xpipe.app.core; + +import io.xpipe.core.OsType; + +import java.nio.file.Files; +import java.nio.file.Path; + +public class AppSystemInfo { + + public static class Windows { + + } + + public static Linux linux() { + if (OsType.getLocal() != OsType.LINUX) { + throw new IllegalStateException(); + } + + return new Linux(); + } + + public static class Linux { + + public boolean isDebianBased() { + return Files.exists(Path.of("/etc/debian_version")); + } + } + + public static class MacOS { + + } +} diff --git a/app/src/main/java/io/xpipe/app/ext/SetupProvider.java b/app/src/main/java/io/xpipe/app/ext/SetupProvider.java new file mode 100644 index 000000000..a470adfc6 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/SetupProvider.java @@ -0,0 +1,38 @@ +package io.xpipe.app.ext; + +import io.xpipe.app.util.LabelGraphic; +import io.xpipe.core.ModuleLayerLoader; + +import java.util.*; + +public interface SetupProvider { + + List ALL = new ArrayList<>(); + + static Optional byId(String id) { + return ALL.stream().filter(d -> d.getId().equalsIgnoreCase(id)).findAny(); + } + + class Loader implements ModuleLayerLoader { + + @Override + public void init(ModuleLayer layer) { + ALL.addAll(ServiceLoader.load(layer, SetupProvider.class).stream() + .sorted(Comparator.comparing(p -> p.type().getModule().getName())) + .map(p -> p.get()) + .toList()); + } + } + + String getId(); + + String getNameKey(); + + LabelGraphic getGraphic(); + + boolean checkInstalled() throws Exception; + + void execute() throws Exception; + + ScanProvider getScan(); +} diff --git a/app/src/main/java/io/xpipe/app/ext/SetupToolActionProvider.java b/app/src/main/java/io/xpipe/app/ext/SetupToolActionProvider.java new file mode 100644 index 000000000..1b1d4d5b4 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/ext/SetupToolActionProvider.java @@ -0,0 +1,59 @@ +package io.xpipe.app.ext; + +import io.xpipe.app.action.AbstractAction; +import io.xpipe.app.action.ActionProvider; +import io.xpipe.app.comp.base.ModalOverlay; +import io.xpipe.app.core.window.AppDialog; +import io.xpipe.app.issue.ErrorEventFactory; +import io.xpipe.app.process.ShellScript; +import io.xpipe.app.storage.DataStorage; +import io.xpipe.app.storage.DataStoreEntry; +import lombok.SneakyThrows; +import lombok.experimental.SuperBuilder; +import lombok.extern.jackson.Jacksonized; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class SetupToolActionProvider implements ActionProvider { + + @Override + public String getId() { + return "setupTool"; + } + + @Jacksonized + @SuperBuilder + public static class Action extends AbstractAction { + + private final String type; + + @Override + @SneakyThrows + public void executeImpl() throws Exception { + var provider = SetupProvider.byId(type); + if (provider.isEmpty()) { + throw ErrorEventFactory.expected(new IllegalArgumentException("Setup action not found: " + type)); + } + + if (!provider.get().checkInstalled()) { + provider.get().execute(); + } + + var local = DataStorage.get().local(); + var sc = ((ShellStore) local.getStore()).getOrStartSession(); + var op = provider.get().getScan().create(local, sc); + if (op != null && !op.isDisabled()) { + provider.get().getScan().scan(local, sc); + } + } + + @Override + public Map toDisplayMap() { + var map = new LinkedHashMap(); + map.put("Action", getDisplayName()); + map.put("Type", type); + return map; + } + } +} diff --git a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java index 135412890..6eeaa4b68 100644 --- a/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java +++ b/app/src/main/java/io/xpipe/app/hub/comp/StoreCreationMenu.java @@ -5,6 +5,8 @@ import io.xpipe.app.comp.base.PrettyImageHelper; import io.xpipe.app.core.AppI18n; import io.xpipe.app.ext.DataStoreCreationCategory; import io.xpipe.app.ext.DataStoreProviders; +import io.xpipe.app.ext.SetupProvider; +import io.xpipe.app.ext.SetupToolActionProvider; import io.xpipe.app.util.ScanDialog; import javafx.application.Platform; @@ -93,6 +95,8 @@ public class StoreCreationMenu { menu.getItems().add(categoryMenu("addSerial", "mdi2s-serial-port", DataStoreCreationCategory.SERIAL, "serial")); menu.getItems().add(actionMenu); + + menu.getItems().add(setupMenu()); } private static Menu categoryMenu( @@ -144,4 +148,24 @@ public class StoreCreationMenu { } return menu; } + + + private static Menu setupMenu() { + var menu = new Menu(); + menu.setGraphic(new FontIcon("mdi2t-toy-brick-plus-outline")); + menu.textProperty().bind(AppI18n.observable("addCloud")); + + for (var p : SetupProvider.ALL) { + var item = new MenuItem(); + item.textProperty().bind(AppI18n.observable(p.getNameKey())); + item.setGraphic(p.getGraphic().createGraphicNode()); + item.setOnAction(event -> { + var action = SetupToolActionProvider.Action.builder().type(p.getId()).build(); + action.executeAsync(); + event.consume(); + }); + menu.getItems().add(item); + } + return menu; + } } diff --git a/app/src/main/java/io/xpipe/app/util/GithubReleaseDownloader.java b/app/src/main/java/io/xpipe/app/util/GithubReleaseDownloader.java index 7dbfeaaf2..33061b699 100644 --- a/app/src/main/java/io/xpipe/app/util/GithubReleaseDownloader.java +++ b/app/src/main/java/io/xpipe/app/util/GithubReleaseDownloader.java @@ -1,5 +1,6 @@ package io.xpipe.app.util; +import io.xpipe.app.process.CommandBuilder; import io.xpipe.core.JacksonMapper; import java.io.IOException; @@ -34,6 +35,18 @@ public class GithubReleaseDownloader { return temp; } + public static void extractTarEntry(Path tarFile, String path, Path target) throws Exception { + var c = CommandBuilder.of().add("tar"); + c.add("-C").addFile(target); + var gz = tarFile.getFileName().toString().endsWith(".gz"); + c.add("-x").addIf(gz, "-z").add("-f"); + c.addFile(tarFile); + c.addFile(path); + + Files.createDirectories(target.getParent()); + LocalShell.getShell().command(c).execute(); + } + private static String getDownloadUrl(String repository, Predicate filter) throws Exception { var request = HttpRequest.newBuilder() .GET() diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index ef0e7f52e..36ffa744e 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -119,8 +119,10 @@ open module io.xpipe.app { uses DataStorageExtensionProvider; uses ProcessControlProvider; uses ShellDialect; + uses SetupProvider; provides ActionProvider with + SetupToolActionProvider, XPipeUrlProvider, OpenHubMenuLeafProvider, EditHubLeafProvider, @@ -198,7 +200,8 @@ open module io.xpipe.app { PrefsProvider.Loader, LicenseProvider.Loader, ScanProvider.Loader, - ShellDialects.Loader; + ShellDialects.Loader, + SetupProvider.Loader; provides SLF4JServiceProvider with AppLogs.Slf4jProvider; provides EventHandler with diff --git a/lang/strings/translations_en.properties b/lang/strings/translations_en.properties index 49f1cd18f..fd5297b26 100644 --- a/lang/strings/translations_en.properties +++ b/lang/strings/translations_en.properties @@ -1574,3 +1574,4 @@ metrics=Metrics publicKey=Public key identifier publicKeyDescription=The optional public key to force the agent to only offer the matching private key openInVsCode=Open in VsCode +addCloud=Cloud ...