优化 Java 下载功能 (#3371)

* update

* update

* update

* update
This commit is contained in:
Glavo 2024-10-23 21:49:35 +08:00 committed by GitHub
parent 99c3ef8a57
commit 5aac4b82fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 134 additions and 157 deletions

View File

@ -21,7 +21,6 @@ import com.jfoenix.controls.*;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Label;
@ -46,7 +45,6 @@ import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.DialogPane;
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
import org.jackhuang.hmcl.ui.wizard.SinglePageWizardProvider;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.gson.JsonUtils;
@ -56,6 +54,7 @@ import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
import static org.jackhuang.hmcl.util.Lang.resolveException;
@ -118,6 +117,8 @@ public final class JavaDownloadDialog extends StackPane {
button.setUserData(version);
vbox.getChildren().add(button);
toggleGroup.getToggles().add(button);
if (JavaManager.REPOSITORY.isInstalled(platform, version))
button.setDisable(true);
}
setBody(vbox);
@ -166,19 +167,6 @@ public final class JavaDownloadDialog extends StackPane {
}
private final class DownloadDiscoJava extends JFXDialogLayout {
private boolean isLTS(int major) {
if (major <= 8) {
return true;
}
if (major < 21) {
return major == 11 || major == 17;
}
return major % 4 == 1;
}
private final JFXComboBox<DiscoJavaDistribution> distributionBox;
private final JFXComboBox<DiscoJavaRemoteVersion> remoteVersionBox;
private final JFXComboBox<JavaPackageType> packageTypeBox;
@ -189,11 +177,8 @@ public final class JavaDownloadDialog extends StackPane {
private final DownloadProvider downloadProvider = DownloadProviders.getDownloadProvider();
private final ObjectProperty<DiscoJavaVersionList> currentDiscoJavaVersionList = new SimpleObjectProperty<>();
private final Map<Pair<DiscoJavaDistribution, JavaPackageType>, DiscoJavaVersionList> javaVersionLists = new HashMap<>();
private boolean changingDistribution = false;
private final Map<DiscoJavaDistribution, DiscoJavaVersionList> javaVersionLists = new HashMap<>();
private final ObjectProperty<DiscoJavaVersionList> currentJavaVersionList = new SimpleObjectProperty<>();
DownloadDiscoJava() {
assert !distributions.isEmpty();
@ -204,7 +189,7 @@ public final class JavaDownloadDialog extends StackPane {
this.remoteVersionBox = new JFXComboBox<>();
this.remoteVersionBox.setConverter(FXUtils.stringConverter(JavaRemoteVersion::getDistributionVersion));
this.packageTypeBox = new JFXComboBox<>();
this.packageTypeBox = new JFXComboBox<>(FXCollections.observableArrayList());
this.downloadButton = new JFXButton(i18n("download"));
downloadButton.setOnAction(e -> onDownload());
@ -227,66 +212,91 @@ public final class JavaDownloadDialog extends StackPane {
body.addRow(2, new Label(i18n("java.download.packageType")), packageTypeBox);
distributionBox.setItems(FXCollections.observableList(new ArrayList<>(distributions)));
ChangeListener<DiscoJavaVersionList.Status> updateStatusListener = (observable, oldValue, newValue) -> updateStatus(newValue);
this.currentDiscoJavaVersionList.addListener((observable, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.status.removeListener(updateStatusListener);
FXUtils.onChange(packageTypeBox.getSelectionModel().selectedItemProperty(), packageType -> {
ObservableList<DiscoJavaRemoteVersion> versions;
if (packageType == null
|| currentJavaVersionList.get() == null
|| (versions = currentJavaVersionList.get().versions.get(packageType)) == null) {
remoteVersionBox.setItems(null);
return;
}
remoteVersionBox.setItems(versions);
for (int i = 0; i < versions.size(); i++) {
DiscoJavaRemoteVersion version = versions.get(i);
if (version.getJdkVersion() == GameJavaVersion.LATEST.getMajorVersion()) {
remoteVersionBox.getSelectionModel().select(i);
return;
}
}
for (int i = 0; i < versions.size(); i++) {
DiscoJavaRemoteVersion version = versions.get(i);
if (version.isLTS()) {
remoteVersionBox.getSelectionModel().select(i);
return;
}
}
remoteVersionBox.getSelectionModel().selectFirst();
});
Consumer<DiscoJavaVersionList> updateListStatus = list -> {
remoteVersionBox.setItems(null);
packageTypeBox.getItems().clear();
remoteVersionBox.setDisable(true);
packageTypeBox.setDisable(true);
warningLabel.setText(null);
if (list == null || (list.versions != null && list.versions.isEmpty()))
downloadButtonPane.getChildren().setAll(downloadButton);
else if (list.status == DiscoJavaVersionList.Status.LOADING)
downloadButtonPane.getChildren().setAll(new JFXSpinner());
else {
downloadButtonPane.getChildren().setAll(downloadButton);
if (list.status == DiscoJavaVersionList.Status.SUCCESS) {
packageTypeBox.getItems().setAll(list.versions.keySet());
packageTypeBox.getSelectionModel().selectFirst();
remoteVersionBox.setDisable(false);
packageTypeBox.setDisable(false);
} else
warningLabel.setText(i18n("java.download.load_list.failed"));
}
};
currentJavaVersionList.addListener((observable, oldValue, newValue) -> {
if (oldValue != null)
oldValue.listener = null;
if (newValue != null) {
newValue.status.addListener(updateStatusListener);
updateStatus(newValue.status.get());
updateListStatus.accept(newValue);
if (newValue.status == DiscoJavaVersionList.Status.LOADING)
newValue.listener = updateListStatus;
} else {
updateStatus(null);
currentJavaVersionList.set(null);
updateListStatus.accept(null);
}
});
packageTypeBox.getSelectionModel().selectedItemProperty().addListener(ignored -> updateVersions());
FXUtils.onChangeAndOperate(distributionBox.getSelectionModel().selectedItemProperty(), distribution -> {
if (distribution != null) {
changingDistribution = true;
packageTypeBox.setItems(FXCollections.observableList(new ArrayList<>(distribution.getSupportedPackageTypes())));
packageTypeBox.getSelectionModel().select(0);
changingDistribution = false;
updateVersions();
packageTypeBox.setDisable(false);
remoteVersionBox.setDisable(false);
} else {
packageTypeBox.setItems(null);
updateVersions();
remoteVersionBox.setItems(null);
packageTypeBox.setDisable(true);
remoteVersionBox.setDisable(true);
}
});
FXUtils.onChange(distributionBox.getSelectionModel().selectedItemProperty(),
it -> currentJavaVersionList.set(getJavaVersionList(it)));
setHeading(new Label(i18n("java.download")));
setBody(body);
setActions(warningLabel, downloadButtonPane, cancelButton);
}
private void updateStatus(DiscoJavaVersionList.Status status) {
if (status == DiscoJavaVersionList.Status.LOADING) {
downloadButtonPane.getChildren().setAll(new JFXSpinner());
remoteVersionBox.setDisable(true);
warningLabel.setText(null);
} else {
downloadButtonPane.getChildren().setAll(downloadButton);
if (status == DiscoJavaVersionList.Status.SUCCESS || status == null) {
remoteVersionBox.setDisable(false);
warningLabel.setText(null);
} else if (status == DiscoJavaVersionList.Status.FAILED) {
remoteVersionBox.setDisable(true);
warningLabel.setText(i18n("java.download.load_list.failed"));
}
}
}
private void onDownload() {
fireEvent(new DialogCloseEvent());
DiscoJavaDistribution distribution = distributionBox.getSelectionModel().getSelectedItem();
DiscoJavaRemoteVersion version = remoteVersionBox.getSelectionModel().getSelectedItem();
JavaPackageType packageType = packageTypeBox.getSelectionModel().getSelectedItem();
if (version == null)
return;
@ -343,7 +353,7 @@ public final class JavaDownloadDialog extends StackPane {
if (idx > 0) {
javaVersion = javaVersion.substring(0, idx);
}
String defaultName = distribution.getApiParameter() + "-" + javaVersion;
String defaultName = distribution.getApiParameter() + "-" + javaVersion + "-" + packageType.name().toLowerCase(Locale.ROOT);
Controllers.getDecorator().startWizard(new SinglePageWizardProvider(controller ->
new JavaInstallPage(controller::onFinish, info, version, updateInfo, defaultName, result)));
} else {
@ -357,65 +367,39 @@ public final class JavaDownloadDialog extends StackPane {
}
private void updateVersions() {
if (changingDistribution) return;
private DiscoJavaVersionList getJavaVersionList(DiscoJavaDistribution distribution) {
if (distribution == null)
return null;
return javaVersionLists.computeIfAbsent(distribution, it -> {
DiscoJavaVersionList versionList = new DiscoJavaVersionList(it);
new DiscoFetchJavaListTask(downloadProvider, it, platform).setExecutor(Schedulers.io()).thenApplyAsync(versions -> {
EnumMap<JavaPackageType, ObservableList<DiscoJavaRemoteVersion>> result = new EnumMap<>(JavaPackageType.class);
if (versions.isEmpty())
return result;
DiscoJavaDistribution distribution = distributionBox.getSelectionModel().getSelectedItem();
if (distribution == null) {
this.currentDiscoJavaVersionList.set(null);
return;
}
for (Map.Entry<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>> entry : versions.entrySet())
for (DiscoJavaRemoteVersion version : entry.getValue().values())
if (version.isLTS()
|| version.getJdkVersion() == entry.getValue().lastKey() // latest version
|| version.getJdkVersion() == 16)
result.computeIfAbsent(entry.getKey(), ignored -> FXCollections.observableArrayList())
.add(version);
JavaPackageType packageType = packageTypeBox.getSelectionModel().getSelectedItem();
DiscoJavaVersionList list = javaVersionLists.computeIfAbsent(Pair.pair(distribution, packageType), pair -> {
DiscoJavaVersionList res = new DiscoJavaVersionList();
new DiscoFetchJavaListTask(downloadProvider, distribution, platform, packageType).setExecutor(Schedulers.io()).thenApplyAsync(versions -> {
if (versions.isEmpty()) return Collections.<DiscoJavaRemoteVersion>emptyList();
int lastLTS = -1;
for (int v : versions.keySet()) {
if (isLTS(v)) {
lastLTS = v;
}
}
ArrayList<DiscoJavaRemoteVersion> remoteVersions = new ArrayList<>();
for (Map.Entry<Integer, DiscoJavaRemoteVersion> entry : versions.entrySet()) {
int v = entry.getKey();
if (v >= lastLTS || isLTS(v) || v == 16) {
remoteVersions.add(entry.getValue());
}
}
Collections.reverse(remoteVersions);
return remoteVersions;
for (List<DiscoJavaRemoteVersion> l : result.values())
Collections.reverse(l);
return result;
}).whenComplete(Schedulers.javafx(), ((result, exception) -> {
if (exception == null) {
res.status.set(DiscoJavaVersionList.Status.SUCCESS);
res.versions.setAll(result);
selectLTS(res);
versionList.status = DiscoJavaVersionList.Status.SUCCESS;
versionList.versions = result;
} else {
LOG.warning("Failed to load java list", exception);
res.status.set(DiscoJavaVersionList.Status.FAILED);
versionList.status = DiscoJavaVersionList.Status.FAILED;
}
versionList.invalidate();
})).start();
return res;
return versionList;
});
this.currentDiscoJavaVersionList.set(list);
this.remoteVersionBox.setItems(list.versions);
selectLTS(list);
}
private void selectLTS(DiscoJavaVersionList list) {
if (remoteVersionBox.getItems() == list.versions) {
for (int i = 0; i < list.versions.size(); i++) {
JavaRemoteVersion item = list.versions.get(i);
if (item.getJdkVersion() == GameJavaVersion.LATEST.getMajorVersion()) {
remoteVersionBox.getSelectionModel().select(i);
break;
}
}
}
}
}
@ -424,7 +408,19 @@ public final class JavaDownloadDialog extends StackPane {
LOADING, SUCCESS, FAILED
}
final ObservableList<DiscoJavaRemoteVersion> versions = FXCollections.observableArrayList();
final ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.LOADING);
final DiscoJavaDistribution distribution;
Status status = Status.LOADING;
EnumMap<JavaPackageType, ObservableList<DiscoJavaRemoteVersion>> versions;
Consumer<DiscoJavaVersionList> listener;
DiscoJavaVersionList(DiscoJavaDistribution distribution) {
this.distribution = distribution;
}
void invalidate() {
if (listener != null)
listener.accept(this);
}
}
}

View File

@ -17,12 +17,7 @@
*/
package org.jackhuang.hmcl.download.java;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.platform.Platform;
import java.util.Set;
import java.util.TreeMap;
/**
* @author Glavo
@ -31,6 +26,4 @@ public interface JavaDistribution<V extends JavaRemoteVersion> {
String getDisplayName();
Set<JavaPackageType> getSupportedPackageTypes();
Task<TreeMap<Integer, V>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType);
}

View File

@ -34,6 +34,13 @@ public enum JavaPackageType {
this.javafx = javafx;
}
public static JavaPackageType of(boolean jdk, boolean javafx) {
if (jdk)
return javafx ? JDKFX : JDK;
else
return javafx ? JREFX : JRE;
}
public boolean isJDK() {
return jdk;
}

View File

@ -33,7 +33,7 @@ import java.util.*;
/**
* @author Glavo
*/
public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJavaRemoteVersion>> {
public final class DiscoFetchJavaListTask extends Task<EnumMap<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>>> {
public static final String API_ROOT = System.getProperty("hmcl.discoapi.override", "https://api.foojay.io/disco/v3.0");
@ -46,21 +46,15 @@ public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJav
}
private final DiscoJavaDistribution distribution;
private final String packageType;
private final boolean isJavaFXBundled;
private final String archiveType;
private final Task<String> fetchPackagesTask;
public DiscoFetchJavaListTask(DownloadProvider downloadProvider, DiscoJavaDistribution distribution, Platform platform, JavaPackageType packageType) {
public DiscoFetchJavaListTask(DownloadProvider downloadProvider, DiscoJavaDistribution distribution, Platform platform) {
this.distribution = distribution;
this.packageType = packageType.isJDK() ? "jdk" : "jre";
this.isJavaFXBundled = packageType.isJavaFXBundled();
this.archiveType = platform.getOperatingSystem() == OperatingSystem.WINDOWS ? "zip" : "tar.gz";
HashMap<String, String> params = new HashMap<>();
params.put("distribution", distribution.getApiParameter());
params.put("package", this.packageType);
params.put("javafx_bundled", Boolean.toString(isJavaFXBundled));
params.put("operating_system", getOperatingSystemName(platform.getOperatingSystem()));
params.put("architecture", getArchitectureName(platform.getArchitecture()));
params.put("archive_type", archiveType);
@ -79,29 +73,28 @@ public final class DiscoFetchJavaListTask extends Task<TreeMap<Integer, DiscoJav
@Override
public void execute() throws Exception {
String json = fetchPackagesTask.getResult();
List<DiscoJavaRemoteVersion> result = JsonUtils.fromNonNullJson(json, DiscoResult.typeOf(DiscoJavaRemoteVersion.class)).getResult();
List<DiscoJavaRemoteVersion> list = JsonUtils.fromNonNullJson(json, DiscoResult.typeOf(DiscoJavaRemoteVersion.class)).getResult();
EnumMap<JavaPackageType, TreeMap<Integer, DiscoJavaRemoteVersion>> result = new EnumMap<>(JavaPackageType.class);
TreeMap<Integer, DiscoJavaRemoteVersion> map = new TreeMap<>();
for (DiscoJavaRemoteVersion version : result) {
for (DiscoJavaRemoteVersion version : list) {
if (!distribution.getApiParameter().equals(version.getDistribution())
|| !version.isDirectlyDownloadable()
|| !packageType.equals(version.getPackageType())
|| !archiveType.equals(version.getArchiveType())
|| isJavaFXBundled != version.isJavaFXBundled())
|| !archiveType.equals(version.getArchiveType()))
continue;
if (!distribution.testVersion(version))
continue;
JavaPackageType packageType = JavaPackageType.of("jdk".equals(version.getPackageType()), version.isJavaFXBundled());
TreeMap<Integer, DiscoJavaRemoteVersion> map = result.computeIfAbsent(packageType, ignored -> new TreeMap<>());
int jdkVersion = version.getJdkVersion();
DiscoJavaRemoteVersion oldVersion = map.get(jdkVersion);
if (oldVersion == null || VersionNumber.compare(version.getDistributionVersion(), oldVersion.getDistributionVersion()) > 0) {
if (oldVersion == null || VersionNumber.compare(version.getDistributionVersion(), oldVersion.getDistributionVersion()) > 0)
map.put(jdkVersion, version);
}
}
setResult(map);
setResult(result);
}
}

View File

@ -17,10 +17,8 @@
*/
package org.jackhuang.hmcl.download.java.disco;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.java.JavaDistribution;
import org.jackhuang.hmcl.download.java.JavaPackageType;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.platform.Architecture;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@ -131,11 +129,6 @@ public enum DiscoJavaDistribution implements JavaDistribution<DiscoJavaRemoteVer
return architectures != null && architectures.contains(platform.getArchitecture());
}
@Override
public Task<TreeMap<Integer, DiscoJavaRemoteVersion>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType) {
return new DiscoFetchJavaListTask(provider, this, platform, packageType);
}
public boolean testVersion(DiscoJavaRemoteVersion version) {
return this.getApiParameter().equals(version.getDistribution());
}

View File

@ -171,6 +171,10 @@ public final class DiscoJavaRemoteVersion implements JavaRemoteVersion {
return termOfSupport;
}
public boolean isLTS() {
return "lts".equals(termOfSupport);
}
public String getOperatingSystem() {
return operatingSystem;
}

View File

@ -17,16 +17,12 @@
*/
package org.jackhuang.hmcl.download.java.mojang;
import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.download.java.JavaDistribution;
import org.jackhuang.hmcl.download.java.JavaPackageType;
import org.jackhuang.hmcl.download.java.JavaRemoteVersion;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.util.platform.Platform;
import java.util.Collections;
import java.util.Set;
import java.util.TreeMap;
/**
* @author Glavo
@ -47,9 +43,4 @@ public final class MojangJavaDistribution implements JavaDistribution<JavaRemote
public Set<JavaPackageType> getSupportedPackageTypes() {
return Collections.singleton(JavaPackageType.JRE);
}
@Override
public Task<TreeMap<Integer, JavaRemoteVersion>> getFetchJavaVersionsTask(DownloadProvider provider, Platform platform, JavaPackageType packageType) {
return null;
}
}