pref: 优化GUI更新功能;项目分为gui和upgrade两个模块

This commit is contained in:
2024-10-30 00:11:41 +08:00
parent 4dc13bb8f1
commit 9bbc3f488f
94 changed files with 1056 additions and 313 deletions

174
upgrade/pom.xml Normal file
View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.octopusyan</groupId>
<artifactId>alist-gui</artifactId>
<version>1.0.1</version>
</parent>
<artifactId>upgrade</artifactId>
<version>${parent.version}</version>
<name>upgrade</name>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<java.version>21</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<exec.mainClass>cn.octopusyan.upgrade.AppLauncher</exec.mainClass>
</properties>
<dependencies>
<!-- javafx -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
</dependency>
<!-- https://mkpaz.github.io/atlantafx/ -->
<dependency>
<groupId>io.github.mkpaz</groupId>
<artifactId>atlantafx-base</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>21</source>
<target>21</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.8</version>
<configuration>
<stripDebug>true</stripDebug>
<compress>2</compress>
<noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages>
<launcher>upgrade</launcher>
<jlinkImageName>app</jlinkImageName>
<jlinkZipName>app</jlinkZipName>
<mainClass>cn.octopusyan.upgrade/${exec.mainClass}</mainClass>
</configuration>
<executions>
<execution>
<!-- Default configuration for running with: mvn clean javafx:run -->
<id>default-cli</id>
<configuration>
<stripDebug>true</stripDebug>
<compress>2</compress>
<noHeaderFiles>true</noHeaderFiles>
<noManPages>true</noManPages>
<launcher>upgrade</launcher>
<jlinkImageName>app</jlinkImageName>
<jlinkZipName>app</jlinkZipName>
<mainClass>cn.octopusyan.upgrade/${exec.mainClass}</mainClass>
</configuration>
</execution>
</executions>
</plugin>
<!-- https://github.com/fvarrui/JavaPackager -->
<plugin>
<groupId>io.github.fvarrui</groupId>
<artifactId>javapackager</artifactId>
<version>1.7.7-SNAPSHOT</version>
<configuration>
<mainClass>${exec.mainClass}</mainClass>
<bundleJre>true</bundleJre>
<generateInstaller>false</generateInstaller>
<copyDependencies>true</copyDependencies>
<vmArgs>
<arg>-Xmx100m</arg>
</vmArgs>
</configuration>
<executions>
<execution>
<id>windows</id>
<phase>package</phase>
<goals>
<goal>package</goal>
</goals>
<configuration>
<platform>windows</platform>
<createZipball>false</createZipball>
<winConfig>
<headerType>gui</headerType>
<generateMsi>false</generateMsi>
</winConfig>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<encoding>UTF-8</encoding>
<!--打成jar包后复制到的路径-->
<outputDirectory>../gui/src/main/resources/static</outputDirectory>
<resources>
<resource>
<!--项目中需要复制的文件路径-->
<directory>./target/upgrade</directory>
<includes>
<include>upgrade.exe</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
<!--可配置多个提取复制路径只需要 “<id>”名字不一样即可-->
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,13 @@
package cn.octopusyan.upgrade;
/**
* 启动类
*
* @author octopus_yan@foxmail.com
*/
public class AppLauncher {
public static void main(String[] args) {
Application.launch(Application.class, args);
}
}

View File

@ -0,0 +1,35 @@
package cn.octopusyan.upgrade;
import atlantafx.base.theme.PrimerLight;
import cn.octopusyan.upgrade.util.FxmlUtil;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ResourceBundle;
public class Application extends javafx.application.Application {
@Override
public void start(Stage primaryStage) throws IOException {
// 主题样式
Application.setUserAgentStylesheet(new PrimerLight().getUserAgentStylesheet());
// 启动主界面
Pane root = FxmlUtil.init("/fxml/hello-view.fxml").load();
Scene scene = new Scene(root, 420, 240);
primaryStage.setTitle(ResourceBundle.getBundle("language/language").getString("title"));
primaryStage.setScene(scene);
primaryStage.show();
}
@Override
public void stop() {
// 关闭主界面
Platform.exit();
System.exit(0);
}
}

View File

@ -0,0 +1,75 @@
package cn.octopusyan.upgrade.alert;
import cn.octopusyan.upgrade.alert.builder.*;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.stage.Stage;
import javafx.stage.Window;
/**
* 弹窗工具
*
* @author octopus_yan@foxmail.com
*/
public class AlertUtil {
private static Window mOwner;
public static void initOwner(Stage stage) {
AlertUtil.mOwner = stage;
}
public static AlertBuilder info(String content) {
return info().content(content).header(null);
}
public static AlertBuilder info() {
return alert(Alert.AlertType.INFORMATION);
}
public static AlertBuilder error(String message) {
return alert(Alert.AlertType.ERROR).header(null).content(message);
}
public static AlertBuilder warning() {
return alert(Alert.AlertType.WARNING);
}
public static AlertBuilder exception(Exception ex) {
return alert(Alert.AlertType.ERROR).exception(ex);
}
/**
* 确认对话框
*/
public static AlertBuilder confirm() {
return alert(Alert.AlertType.CONFIRMATION);
}
/**
* 自定义确认对话框 <p>
*
* @param buttons <code>"Cancel"</code> OR <code>"取消"</code> 为取消按钮
*/
public static AlertBuilder confirm(String... buttons) {
return confirm().buttons(buttons);
}
public static AlertBuilder confirm(ButtonType... buttons) {
return confirm().buttons(buttons);
}
public static AlertBuilder alert(Alert.AlertType type) {
return new AlertBuilder(mOwner, type);
}
public interface OnChoseListener {
void confirm();
default void cancelOrClose(ButtonType buttonType) {
}
}
public interface OnClickListener {
void onClicked(String result);
}
}

View File

@ -0,0 +1,108 @@
package cn.octopusyan.upgrade.alert.builder;
import cn.octopusyan.upgrade.alert.AlertUtil;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.stage.Window;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @author octopus_yan
*/
public class AlertBuilder extends BaseBuilder<AlertBuilder, Alert> {
public AlertBuilder(Window owner, Alert.AlertType alertType) {
super(new Alert(alertType), owner);
}
public AlertBuilder buttons(String... buttons) {
dialog.getButtonTypes().addAll(getButtonList(buttons));
return this;
}
public AlertBuilder buttons(ButtonType... buttons) {
dialog.getButtonTypes().addAll(buttons);
return this;
}
public AlertBuilder exception(Exception ex) {
dialog.setTitle("Exception Dialog");
dialog.setHeaderText(ex.getClass().getSimpleName());
dialog.setContentText(ex.getMessage());
// 创建可扩展的异常。
var sw = new StringWriter();
var pw = new PrintWriter(sw);
ex.printStackTrace(pw);
var exceptionText = sw.toString();
var label = new Label("The exception stacktrace was :");
var textArea = new TextArea(exceptionText);
textArea.setEditable(false);
textArea.setWrapText(true);
textArea.setMaxWidth(Double.MAX_VALUE);
textArea.setMaxHeight(Double.MAX_VALUE);
GridPane.setVgrow(textArea, Priority.ALWAYS);
GridPane.setHgrow(textArea, Priority.ALWAYS);
var expContent = new GridPane();
expContent.setMaxWidth(Double.MAX_VALUE);
expContent.add(label, 0, 0);
expContent.add(textArea, 0, 1);
// 将可扩展异常设置到对话框窗格中。
dialog.getDialogPane().setExpandableContent(expContent);
return this;
}
/**
* 获取按钮列表
*
* @param buttons "Cancel" / "取消" 为取消按钮
*/
private List<ButtonType> getButtonList(String[] buttons) {
if (ArrayUtils.isEmpty(buttons)) return Collections.emptyList();
return Arrays.stream(buttons).map((type) -> {
ButtonBar.ButtonData buttonData = ButtonBar.ButtonData.OTHER;
if ("cancel".equals(StringUtils.lowerCase(type)) || "取消".equals(type)) {
return ButtonType.CANCEL;
}
return new ButtonType(type, buttonData);
}).collect(Collectors.toList());
}
/**
* AlertUtil.confirm
*/
public void show(AlertUtil.OnClickListener listener) {
Optional<ButtonType> result = dialog.showAndWait();
result.ifPresent(r -> listener.onClicked(r.getText()));
}
/**
* AlertUtil.confirm
*/
public void show(AlertUtil.OnChoseListener listener) {
Optional<ButtonType> result = dialog.showAndWait();
result.ifPresent(r -> {
if (r == ButtonType.OK) {
listener.confirm();
} else {
listener.cancelOrClose(r);
}
});
}
}

View File

@ -0,0 +1,44 @@
package cn.octopusyan.upgrade.alert.builder;
import javafx.application.Platform;
import javafx.scene.control.Dialog;
import javafx.stage.Window;
import lombok.Getter;
/**
* @author octopus_yan
*/
@Getter
public abstract class BaseBuilder<T extends BaseBuilder<T, ?>, D extends Dialog<?>> {
protected D dialog;
public BaseBuilder(D dialog, Window mOwner) {
this.dialog = dialog;
if (mOwner != null)
this.dialog.initOwner(mOwner);
}
public T title(String title) {
dialog.setTitle(title);
return (T) this;
}
public T header(String header) {
dialog.setHeaderText(header);
return (T) this;
}
public T content(String content) {
dialog.setContentText(content);
return (T) this;
}
public void show() {
Platform.runLater(() -> dialog.showAndWait());
}
public void close() {
if (dialog.isShowing())
dialog.close();
}
}

View File

@ -0,0 +1,19 @@
package cn.octopusyan.upgrade.util;
import javafx.fxml.FXMLLoader;
import javafx.fxml.JavaFXBuilderFactory;
import java.nio.charset.StandardCharsets;
import java.util.ResourceBundle;
public class FxmlUtil {
public static FXMLLoader init(String path) {
return new FXMLLoader(
FxmlUtil.class.getResource(path),
ResourceBundle.getBundle("language/language"),
new JavaFXBuilderFactory(),
null,
StandardCharsets.UTF_8
);
}
}

View File

@ -0,0 +1,83 @@
package cn.octopusyan.upgrade.view;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.octopusyan.upgrade.alert.AlertUtil;
import javafx.application.Platform;
import javafx.fxml.FXML;
import java.io.*;
import java.nio.file.Paths;
import java.util.zip.ZipFile;
public class HelloController {
public static final String APP_NAME = "alist-gui";
public static final String PARENT_PATH = Paths.get("").toFile().getAbsolutePath();
@FXML
public void onUpdate() {
// 更新检查
File file = new File(PARENT_PATH + File.separator + APP_NAME + "-windows-nojre.zip");
if (!file.exists()) {
AlertUtil.error("The upgrade file does not exist!").show();
return;
}
// 解压
unzip();
// 启动GUI
startGui();
// 退出
onExit();
}
@FXML
public void onExit() {
Platform.exit();
}
private void startGui() {
Runtime runtime = Runtime.getRuntime(); //获取Runtime实例
//执行命令
String[] command = {APP_NAME + ".exe"};
try {
Process process = runtime.exec(command);
} catch (IOException ignored) {
}
}
private void unzip() {
File file = new File(PARENT_PATH + File.separator + APP_NAME + "-windows-nojre.zip");
ZipFile zipFile = ZipUtil.toZipFile(file, CharsetUtil.defaultCharset());
ZipUtil.read(zipFile, zipEntry -> {
String path = zipEntry.getName();
if (FileUtil.isWindows()) {
// Win系统下
path = StrUtil.replace(path, "*", "_");
}
// 打包后文件都在alist-gui文件夹下解压时去掉
path = path.replaceFirst(APP_NAME, "");
final File outItemFile = FileUtil.file(PARENT_PATH, path);
if (zipEntry.isDirectory()) {
// 目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
InputStream in = ZipUtil.getStream(zipFile, zipEntry);
// 文件
FileUtil.writeFromStream(in, outItemFile, false);
}
});
// 解压完成后删除
FileUtil.del(file);
}
}

View File

@ -0,0 +1,16 @@
module cn.octopusyan.upgrade {
requires javafx.controls;
requires javafx.fxml;
requires atlantafx.base;
requires cn.hutool.core;
requires static lombok;
requires org.apache.commons.lang3;
opens cn.octopusyan.upgrade to javafx.fxml;
exports cn.octopusyan.upgrade;
exports cn.octopusyan.upgrade.util;
opens cn.octopusyan.upgrade.util to javafx.fxml;
exports cn.octopusyan.upgrade.view;
opens cn.octopusyan.upgrade.view to javafx.fxml;
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.HBox?>
<HBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/11.0.14-internal"
fx:controller="cn.octopusyan.upgrade.view.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Button onAction="#onUpdate" styleClass="large" text="%update"/>
<Button onAction="#onExit" styleClass="large" text="%exit"/>
</HBox>

View File

@ -0,0 +1,7 @@
exit=\u9000\u51FA
title=\u66F4\u65B0
update=\u66F4\u65B0

View File

@ -0,0 +1,6 @@
exit=Exit
title=Upgrade App
update=Upgrade

View File

@ -0,0 +1,6 @@
exit=\u9000\u51FA
title=\u66F4\u65B0
update=\u66F4\u65B0