From 4e1af001c2d4527173efe2f98d4aa10ddcbba86b Mon Sep 17 00:00:00 2001 From: octopus_yan Date: Thu, 26 Sep 2024 21:14:48 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=90=8E=E7=89=88=E6=9C=AC=E5=8F=B7=E6=98=BE=E7=A4=BA=E4=B8=BA?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=86=85=E6=97=A7=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/octopusyan/alistgui/manager/ConfigManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/cn/octopusyan/alistgui/manager/ConfigManager.java b/src/main/java/cn/octopusyan/alistgui/manager/ConfigManager.java index d3e1992..a5c87e6 100644 --- a/src/main/java/cn/octopusyan/alistgui/manager/ConfigManager.java +++ b/src/main/java/cn/octopusyan/alistgui/manager/ConfigManager.java @@ -278,6 +278,10 @@ public class ConfigManager { } public static String guiVersion() { + // 覆盖配置文件读取的版本号 + if (!Constants.APP_VERSION.equals(gui().getVersion())) { + guiVersion(Constants.APP_VERSION); + } return gui().getVersion(); } From 4988cdc31e07e23a3bdf81205b2859ded5c0d5bb Mon Sep 17 00:00:00 2001 From: octopus_yan Date: Thu, 26 Sep 2024 21:01:30 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat:=20GUI=E5=8D=95=E4=BE=8B=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cn/octopusyan/alistgui/Application.java | 92 ++++++++++++++++++- .../alistgui/controller/RootController.java | 3 +- 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/octopusyan/alistgui/Application.java b/src/main/java/cn/octopusyan/alistgui/Application.java index 26169e5..79c106f 100644 --- a/src/main/java/cn/octopusyan/alistgui/Application.java +++ b/src/main/java/cn/octopusyan/alistgui/Application.java @@ -18,11 +18,12 @@ import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.ProxySelector; +import java.io.*; +import java.net.*; import java.net.http.HttpClient; import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; public class Application extends javafx.application.Application { private static final Logger logger = LoggerFactory.getLogger(Application.class); @@ -32,6 +33,10 @@ public class Application extends javafx.application.Application { @Override public void init() { logger.info("application init ..."); + + // 单例模式检查 + makeSingle(); + // 初始化客户端配置 ConfigManager.load(); @@ -83,6 +88,7 @@ public class Application extends javafx.application.Application { primaryStage.setTitle(String.format("%s v%s", Constants.APP_TITLE, Constants.APP_VERSION)); Scene scene = Context.initScene(); primaryStage.setScene(scene); + primaryStage.show(); // 静默启动 @@ -113,4 +119,84 @@ public class Application extends javafx.application.Application { Platform.exit(); System.exit(0); } + + private static final int SINGLE_INSTANCE_LISTENER_PORT = 9009; + private static final String SINGLE_INSTANCE_FOCUS_MESSAGE = "focus"; + + private static final String instanceId = UUID.randomUUID().toString(); + + /** + * 我们在聚焦现有实例之前定义一个暂停 + * 因为有时启动实例的命令行或窗口 + * 可能会在第二个实例执行完成后重新获得焦点 + * 所以我们在聚焦原始窗口之前引入了一个短暂的延迟 + * 以便原始窗口可以保留焦点。 + */ + private static final int FOCUS_REQUEST_PAUSE_MILLIS = 500; + + /** + * 单实例检测 + * + * @see JavaFX单实例运行应用程序 + */ + public static void makeSingle() { + CountDownLatch instanceCheckLatch = new CountDownLatch(1); + + Thread instanceListener = new Thread(() -> { + try (ServerSocket serverSocket = new ServerSocket(SINGLE_INSTANCE_LISTENER_PORT, 10)) { + instanceCheckLatch.countDown(); + + while (true) { + try ( + Socket clientSocket = serverSocket.accept(); + BufferedReader in = new BufferedReader( + new InputStreamReader(clientSocket.getInputStream())) + ) { + String input = in.readLine(); + logger.info(STR."Received single instance listener message: \{input}"); + if (input.startsWith(SINGLE_INSTANCE_FOCUS_MESSAGE) && primaryStage != null) { + //noinspection BusyWait + Thread.sleep(FOCUS_REQUEST_PAUSE_MILLIS); + Platform.runLater(() -> { + logger.info(STR."To front \{instanceId}"); + primaryStage.setIconified(false); + primaryStage.show(); + primaryStage.toFront(); + }); + } + } catch (IOException e) { + logger.error("Single instance listener unable to process focus message from client"); + } + } + } catch (java.net.BindException b) { + logger.error("SingleInstanceApp already running"); + + try ( + Socket clientSocket = new Socket(InetAddress.getLocalHost(), SINGLE_INSTANCE_LISTENER_PORT); + PrintWriter out = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream())) + ) { + logger.info("Requesting existing app to focus"); + out.println(STR."\{SINGLE_INSTANCE_FOCUS_MESSAGE} requested by \{instanceId}"); + } catch (IOException e) { + logger.error("", e); + } + + logger.info(STR."Aborting execution for instance \{instanceId}"); + Platform.exit(); + } catch (Exception e) { + logger.error("", e); + } finally { + instanceCheckLatch.countDown(); + } + }, "instance-listener"); + instanceListener.setDaemon(true); + instanceListener.start(); + + try { + instanceCheckLatch.await(); + } catch (InterruptedException e) { + //noinspection ResultOfMethodCallIgnored + Thread.interrupted(); + } + } } \ No newline at end of file diff --git a/src/main/java/cn/octopusyan/alistgui/controller/RootController.java b/src/main/java/cn/octopusyan/alistgui/controller/RootController.java index 99c85e5..efd9e83 100644 --- a/src/main/java/cn/octopusyan/alistgui/controller/RootController.java +++ b/src/main/java/cn/octopusyan/alistgui/controller/RootController.java @@ -114,12 +114,13 @@ public class RootController extends BaseController { @Override public void initViewAction() { closeIcon.addEventHandler(MouseEvent.MOUSE_CLICKED, _ -> { + Platform.setImplicitExit(!ConfigManager.closeToTray()); if (ConfigManager.closeToTray()) { SystemTrayManager.show(); } else { SystemTrayManager.hide(); + Platform.exit(); } - Platform.setImplicitExit(!ConfigManager.closeToTray()); getWindow().close(); }); minimizeIcon.addEventHandler(MouseEvent.MOUSE_CLICKED, _ -> getWindow().setIconified(true)); From 115e672f2692e47c513986deb3f34ed1a54514a6 Mon Sep 17 00:00:00 2001 From: octopus_yan Date: Sat, 19 Oct 2024 17:14:41 +0800 Subject: [PATCH 3/6] =?UTF-8?q?ci:=20=E9=9D=9Erelease=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=B8=8D=E4=B8=8A=E4=BC=A0=E5=B7=A5=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3608f2..16a7cd5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -121,10 +121,12 @@ jobs: mkdir zipball && cp target/*.zip zipball - name: Upload AListGUI to Github + if: startsWith(github.ref, 'refs/tags/v') uses: actions/upload-artifact@v4 with: name: AListGUI-windows path: zipball + retention-days: 5 release: if: startsWith(github.ref, 'refs/tags/v') From 30f05240f9686b969f899daf3541e71c20be6922 Mon Sep 17 00:00:00 2001 From: octopus_yan Date: Sat, 19 Oct 2024 17:33:05 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=85=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E8=AF=AD=E8=A8=80=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../octopusyan/alistgui/controller/SetupController.java | 8 +++++++- src/main/resources/fxml/setup-view.fxml | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/cn/octopusyan/alistgui/controller/SetupController.java b/src/main/java/cn/octopusyan/alistgui/controller/SetupController.java index 5e2f529..1f12631 100644 --- a/src/main/java/cn/octopusyan/alistgui/controller/SetupController.java +++ b/src/main/java/cn/octopusyan/alistgui/controller/SetupController.java @@ -37,8 +37,14 @@ public class SetupController extends BaseController implements I public CheckBox silentStartupCheckBox; @I18n(key = "setup.close-to-tray.label") public CheckBox closeToTrayCheckBox; - public ComboBox languageComboBox; + @I18n(key = "setup.theme") + public Label themeLabel; public ComboBox themeComboBox; + @I18n(key = "setup.language") + public Label languageLabel; + public ComboBox languageComboBox; + @I18n(key = "setup.proxy") + public Label proxySetupLabel; public ComboBox proxySetupComboBox; public Pane proxySetupPane; @I18n(key = "setup.proxy.test") diff --git a/src/main/resources/fxml/setup-view.fxml b/src/main/resources/fxml/setup-view.fxml index eb3888e..4ea0c23 100644 --- a/src/main/resources/fxml/setup-view.fxml +++ b/src/main/resources/fxml/setup-view.fxml @@ -14,15 +14,15 @@ - - -