feat: Mod dependencies & Mod downloads should be classified.

This commit is contained in:
huanghongxun
2021-09-20 17:35:49 +08:00
parent f2741f9725
commit 0b2e9c3f62
12 changed files with 272 additions and 134 deletions

View File

@@ -109,6 +109,7 @@ public class ComponentList extends Control {
public void onExpand() {
if (!expanded && lazyInitializer != null) {
lazyInitializer.accept(this);
setNeedsLayout(true);
}
expanded = true;

View File

@@ -22,6 +22,7 @@ import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
@@ -137,25 +138,31 @@ class ComponentListCell extends StackPane {
setExpanded(!isExpanded());
double newAnimatedHeight = content.prefHeight(-1) * (isExpanded() ? 1 : -1);
double newHeight = isExpanded() ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = isExpanded() ? newAnimatedHeight : 0;
if (isExpanded()) {
updateClip(newHeight);
list.onExpand();
list.layout();
}
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
new KeyValue(container.minHeightProperty(), contentHeight, FXUtils.SINE),
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtils.SINE)
));
Platform.runLater(() -> {
double newAnimatedHeight = content.prefHeight(-1) * (isExpanded() ? 1 : -1);
double newHeight = isExpanded() ? getHeight() + newAnimatedHeight : prefHeight(-1);
double contentHeight = isExpanded() ? newAnimatedHeight : 0;
if (!isExpanded()) {
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
}
if (isExpanded()) {
updateClip(newHeight);
}
expandAnimation.play();
expandAnimation = new Timeline(new KeyFrame(new Duration(320.0),
new KeyValue(container.minHeightProperty(), contentHeight, FXUtils.SINE),
new KeyValue(container.maxHeightProperty(), contentHeight, FXUtils.SINE)
));
if (!isExpanded()) {
expandAnimation.setOnFinished(e2 -> updateClip(newHeight));
}
expandAnimation.play();
});
});
expandedProperty().addListener((a, b, newValue) ->

View File

@@ -18,23 +18,24 @@
package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXScrollPane;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.GameVersion;
import org.jackhuang.hmcl.mod.DownloadManager;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.setting.Profile;
@@ -45,29 +46,27 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.construct.FloatListCell;
import org.jackhuang.hmcl.ui.construct.JFXHyperlink;
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.SimpleMultimap;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.NetworkUtils;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Comparator;
import java.util.Locale;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
public class DownloadPage extends Control implements DecoratorPage {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
private final ListProperty<DownloadManager.Version> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList());
private final BooleanProperty loaded = new SimpleBooleanProperty(false);
private final BooleanProperty loading = new SimpleBooleanProperty(false);
private final BooleanProperty failed = new SimpleBooleanProperty(false);
private final DownloadManager.Mod addon;
@@ -76,6 +75,9 @@ public class DownloadPage extends Control implements DecoratorPage {
private final DownloadCallback callback;
private final DownloadListPage page;
private List<DownloadManager.Mod> dependencies;
private SimpleMultimap<String, DownloadManager.Version> versions;
public DownloadPage(DownloadListPage page, DownloadManager.Mod addon, Profile.ProfileVersion version, @Nullable DownloadCallback callback) {
this.page = page;
this.addon = addon;
@@ -89,30 +91,57 @@ public class DownloadPage extends Control implements DecoratorPage {
setLoading(true);
setFailed(false);
Task.supplyAsync(() -> {
if (StringUtils.isNotBlank(version.getVersion())) {
Optional<String> gameVersion = GameVersion.minecraftVersion(versionJar);
if (gameVersion.isPresent()) {
return addon.getData().loadVersions()
.filter(file -> file.getGameVersions().contains(gameVersion.get()));
}
}
return addon.getData().loadVersions();
}).whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
items.setAll(result
.sorted(Comparator.comparing(DownloadManager.Version::getDatePublished).reversed())
.collect(Collectors.toList()));
setFailed(false);
} else {
setFailed(true);
}
setLoading(false);
}).start();
Task.allOf(
Task.supplyAsync(() -> addon.getData().loadDependencies()),
Task.supplyAsync(() -> {
Stream<DownloadManager.Version> versions = addon.getData().loadVersions();
// if (StringUtils.isNotBlank(version.getVersion())) {
// Optional<String> gameVersion = GameVersion.minecraftVersion(versionJar);
// if (gameVersion.isPresent()) {
// return sortVersions(
// .filter(file -> file.getGameVersions().contains(gameVersion.get())));
// }
// }
return sortVersions(versions);
}))
.whenComplete(Schedulers.javafx(), (result, exception) -> {
if (exception == null) {
@SuppressWarnings("unchecked")
List<DownloadManager.Mod> dependencies = (List<DownloadManager.Mod>) result.get(0);
@SuppressWarnings("unchecked")
SimpleMultimap<String, DownloadManager.Version> versions = (SimpleMultimap<String, DownloadManager.Version>) result.get(1);
this.dependencies = dependencies;
this.versions = versions;
loaded.set(true);
setFailed(false);
} else {
setFailed(true);
}
setLoading(false);
}).start();
this.state.set(State.fromTitle(addon.getTitle()));
}
private SimpleMultimap<String, DownloadManager.Version> sortVersions(Stream<DownloadManager.Version> versions) {
SimpleMultimap<String, DownloadManager.Version> classifiedVersions
= new SimpleMultimap<String, DownloadManager.Version>(HashMap::new, ArrayList::new);
versions.forEach(version -> {
for (String gameVersion : version.getGameVersions()) {
classifiedVersions.put(gameVersion, version);
}
});
for (String gameVersion : classifiedVersions.keys()) {
List<DownloadManager.Version> versionList = (List<DownloadManager.Version>) classifiedVersions.get(gameVersion);
versionList.sort(Comparator.comparing(DownloadManager.Version::getDatePublished).reversed());
}
return classifiedVersions;
}
public DownloadManager.Mod getAddon() {
return addon;
}
@@ -186,49 +215,79 @@ public class DownloadPage extends Control implements DecoratorPage {
protected ModDownloadPageSkin(DownloadPage control) {
super(control);
BorderPane pane = new BorderPane();
VBox pane = new VBox(8);
pane.getStyleClass().add("gray-background");
pane.setPadding(new Insets(10));
ScrollPane scrollPane = new ScrollPane(pane);
JFXScrollPane.smoothScrolling(scrollPane);
scrollPane.setFitToWidth(true);
scrollPane.setFitToHeight(true);
HBox descriptionPane = new HBox(8);
descriptionPane.setAlignment(Pos.CENTER);
pane.setTop(descriptionPane);
descriptionPane.getStyleClass().add("card");
pane.getChildren().add(descriptionPane);
descriptionPane.getStyleClass().add("card-non-transparent");
BorderPane.setMargin(descriptionPane, new Insets(11, 11, 0, 11));
ImageView imageView = new ImageView();
if (StringUtils.isNotBlank(getSkinnable().addon.getIconUrl())) {
imageView.setImage(new Image(getSkinnable().addon.getIconUrl(), 40, 40, true, true, true));
}
descriptionPane.getChildren().add(FXUtils.limitingSize(imageView, 40, 40));
TwoLineListItem content = new TwoLineListItem();
HBox.setHgrow(content, Priority.ALWAYS);
ModTranslations.Mod mod = ModTranslations.getModByCurseForgeId(getSkinnable().addon.getSlug());
content.setTitle(mod != null ? mod.getDisplayName() : getSkinnable().addon.getTitle());
content.setSubtitle(getSkinnable().addon.getDescription());
content.getTags().setAll(getSkinnable().addon.getCategories().stream()
.map(category -> getSkinnable().page.getLocalizedCategory(category))
.collect(Collectors.toList()));
descriptionPane.getChildren().add(content);
if (getSkinnable().mod != null) {
JFXHyperlink openMcmodButton = new JFXHyperlink(i18n("mods.mcmod"));
openMcmodButton.setOnAction(e -> FXUtils.openLink(ModManager.getMcmodUrl(getSkinnable().mod.getMcmod())));
descriptionPane.getChildren().add(openMcmodButton);
if (StringUtils.isNotBlank(getSkinnable().mod.getMcbbs())) {
JFXHyperlink openMcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
openMcbbsButton.setOnAction(e -> FXUtils.openLink(ModManager.getMcbbsUrl(getSkinnable().mod.getMcbbs())));
descriptionPane.getChildren().add(openMcbbsButton);
{
ImageView imageView = new ImageView();
if (StringUtils.isNotBlank(getSkinnable().addon.getIconUrl())) {
imageView.setImage(new Image(getSkinnable().addon.getIconUrl(), 40, 40, true, true, true));
}
descriptionPane.getChildren().add(FXUtils.limitingSize(imageView, 40, 40));
TwoLineListItem content = new TwoLineListItem();
HBox.setHgrow(content, Priority.ALWAYS);
ModTranslations.Mod mod = ModTranslations.getModByCurseForgeId(getSkinnable().addon.getSlug());
content.setTitle(mod != null ? mod.getDisplayName() : getSkinnable().addon.getTitle());
content.setSubtitle(getSkinnable().addon.getDescription());
content.getTags().setAll(getSkinnable().addon.getCategories().stream()
.map(category -> getSkinnable().page.getLocalizedCategory(category))
.collect(Collectors.toList()));
descriptionPane.getChildren().add(content);
if (getSkinnable().mod != null) {
JFXHyperlink openMcmodButton = new JFXHyperlink(i18n("mods.mcmod"));
openMcmodButton.setOnAction(e -> FXUtils.openLink(ModManager.getMcmodUrl(getSkinnable().mod.getMcmod())));
descriptionPane.getChildren().add(openMcmodButton);
if (StringUtils.isNotBlank(getSkinnable().mod.getMcbbs())) {
JFXHyperlink openMcbbsButton = new JFXHyperlink(i18n("mods.mcbbs"));
openMcbbsButton.setOnAction(e -> FXUtils.openLink(ModManager.getMcbbsUrl(getSkinnable().mod.getMcbbs())));
descriptionPane.getChildren().add(openMcbbsButton);
}
}
JFXHyperlink openUrlButton = new JFXHyperlink(control.page.getLocalizedOfficialPage());
openUrlButton.setOnAction(e -> FXUtils.openLink(getSkinnable().addon.getPageUrl()));
descriptionPane.getChildren().add(openUrlButton);
}
JFXHyperlink openUrlButton = new JFXHyperlink(control.page.getLocalizedOfficialPage());
openUrlButton.setOnAction(e -> FXUtils.openLink(getSkinnable().addon.getPageUrl()));
descriptionPane.getChildren().add(openUrlButton);
{
ComponentList dependencyPane = new ComponentList();
dependencyPane.getStyleClass().add("no-padding");
FXUtils.onChangeAndOperate(control.loaded, loaded -> {
if (loaded) {
dependencyPane.getContent().setAll(control.dependencies.stream()
.map(dependency -> new DependencyModItem(getSkinnable().page, dependency, control.version, control.callback))
.collect(Collectors.toList()));
}
});
Node title = ComponentList.createComponentListTitle(i18n("mods.dependencies"));
BooleanBinding show = Bindings.createBooleanBinding(() -> !control.dependencies.isEmpty(), control.loaded);
title.managedProperty().bind(show);
title.visibleProperty().bind(show);
dependencyPane.managedProperty().bind(show);
dependencyPane.visibleProperty().bind(show);
pane.getChildren().addAll(title, dependencyPane);
}
SpinnerPane spinnerPane = new SpinnerPane();
pane.setCenter(spinnerPane);
VBox.setVgrow(spinnerPane, Priority.ALWAYS);
pane.getChildren().add(spinnerPane);
{
spinnerPane.loadingProperty().bind(getSkinnable().loadingProperty());
spinnerPane.failedReasonProperty().bind(Bindings.createStringBinding(() -> {
@@ -239,60 +298,101 @@ public class DownloadPage extends Control implements DecoratorPage {
}
}, getSkinnable().failedProperty()));
JFXListView<DownloadManager.Version> listView = new JFXListView<>();
spinnerPane.setContent(listView);
Bindings.bindContent(listView.getItems(), getSkinnable().items);
listView.setCellFactory(x -> new FloatListCell<DownloadManager.Version>(listView) {
TwoLineListItem content = new TwoLineListItem();
StackPane graphicPane = new StackPane();
JFXButton saveAsButton = new JFXButton();
ComponentList list = new ComponentList();
StackPane.setAlignment(list, Pos.TOP_CENTER);
spinnerPane.setContent(list);
{
HBox container = new HBox(8);
container.setAlignment(Pos.CENTER_LEFT);
pane.getChildren().add(container);
FXUtils.onChangeAndOperate(control.loaded, loaded -> {
if (control.versions == null) return;
saveAsButton.getStyleClass().add("toggle-icon4");
saveAsButton.setGraphic(SVG.contentSaveMoveOutline(Theme.blackFillBinding(), -1, -1));
for (String gameVersion : control.versions.keys().stream()
.sorted(VersionNumber.VERSION_COMPARATOR.reversed())
.collect(Collectors.toList())) {
ComponentList sublist = new ComponentList();
sublist.setLazyInitializer(self -> {
self.getContent().setAll(control.versions.get(gameVersion).stream()
.map(version -> new ModItem(version, control))
.collect(Collectors.toList()));
});
sublist.getStyleClass().add("no-padding");
sublist.setTitle(gameVersion);
HBox.setHgrow(content, Priority.ALWAYS);
container.getChildren().setAll(graphicPane, content, saveAsButton);
list.getContent().add(sublist);
}
@Override
protected void updateControl(DownloadManager.Version dataItem, boolean empty) {
if (empty) return;
content.setTitle(dataItem.getName());
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished()));
content.getTags().setAll(dataItem.getGameVersions());
saveAsButton.setOnMouseClicked(e -> getSkinnable().saveAs(dataItem));
switch (dataItem.getVersionType()) {
case Release:
graphicPane.getChildren().setAll(SVG.releaseCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.release"));
break;
case Beta:
graphicPane.getChildren().setAll(SVG.betaCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.snapshot"));
break;
case Alpha:
graphicPane.getChildren().setAll(SVG.alphaCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.snapshot"));
break;
}
}
});
listView.setOnMouseClicked(e -> {
if (listView.getSelectionModel().getSelectedIndex() < 0)
return;
DownloadManager.Version selectedItem = listView.getSelectionModel().getSelectedItem();
getSkinnable().download(selectedItem);
});
}
getChildren().setAll(pane);
getChildren().setAll(scrollPane);
}
}
private static final class DependencyModItem extends StackPane {
DependencyModItem(DownloadListPage page, DownloadManager.Mod addon, Profile.ProfileVersion version, DownloadCallback callback) {
HBox pane = new HBox(8);
pane.setPadding(new Insets(8));
pane.setAlignment(Pos.CENTER_LEFT);
TwoLineListItem content = new TwoLineListItem();
HBox.setHgrow(content, Priority.ALWAYS);
ImageView imageView = new ImageView();
pane.getChildren().setAll(FXUtils.limitingSize(imageView, 40, 40), content);
RipplerContainer container = new RipplerContainer(pane);
container.setOnMouseClicked(e -> Controllers.navigate(new DownloadPage(page, addon, version, callback)));
getChildren().setAll(container);
ModTranslations.Mod mod = ModTranslations.getModByCurseForgeId(addon.getSlug());
content.setTitle(mod != null ? mod.getDisplayName() : addon.getTitle());
content.setSubtitle(addon.getDescription());
content.getTags().setAll(addon.getCategories().stream()
.map(page::getLocalizedCategory)
.collect(Collectors.toList()));
if (StringUtils.isNotBlank(addon.getIconUrl())) {
imageView.setImage(new Image(addon.getIconUrl(), 40, 40, true, true, true));
}
}
}
private static final class ModItem extends StackPane {
ModItem(DownloadManager.Version dataItem, DownloadPage selfPage) {
HBox pane = new HBox(8);
pane.setPadding(new Insets(8));
pane.setAlignment(Pos.CENTER_LEFT);
TwoLineListItem content = new TwoLineListItem();
StackPane graphicPane = new StackPane();
JFXButton saveAsButton = new JFXButton();
RipplerContainer container = new RipplerContainer(pane);
container.setOnMouseClicked(e -> {
selfPage.download(dataItem);
});
getChildren().setAll(container);
saveAsButton.getStyleClass().add("toggle-icon4");
saveAsButton.setGraphic(SVG.contentSaveMoveOutline(Theme.blackFillBinding(), -1, -1));
HBox.setHgrow(content, Priority.ALWAYS);
pane.getChildren().setAll(graphicPane, content, saveAsButton);
content.setTitle(dataItem.getName());
content.setSubtitle(FORMATTER.format(dataItem.getDatePublished()));
saveAsButton.setOnMouseClicked(e -> selfPage.saveAs(dataItem));
switch (dataItem.getVersionType()) {
case Release:
graphicPane.getChildren().setAll(SVG.releaseCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.release"));
break;
case Beta:
graphicPane.getChildren().setAll(SVG.betaCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.snapshot"));
break;
case Alpha:
graphicPane.getChildren().setAll(SVG.alphaCircleOutline(Theme.blackFillBinding(), 24, 24));
content.getTags().add(i18n("version.game.snapshot"));
break;
}
}
}

View File

@@ -180,7 +180,7 @@ public class GameListPage extends ListPageBase<GameListItem> implements Decorato
Profiles.registerVersionsListener(this::loadVersions);
setOnFailedAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
setOnFailedAction(e -> Controllers.navigate(Controllers.getDownloadPage()));
}
private void loadVersions(Profile profile) {

View File

@@ -533,6 +533,7 @@ mods.add.success=Successfully installed mods %s.
mods.category=Category
mods.choose_mod=Choose your mods
mods.curseforge=CurseForge
mods.dependencies=Dependencies
mods.disable=Disable
mods.download=Mod Downloads
mods.download.title=Mod Downloads - %1s

View File

@@ -533,6 +533,7 @@ mods.add.success=成功新增模組 %s。
mods.category=類別
mods.choose_mod=選擇模組
mods.curseforge=CurseForge
mods.dependencies=前置 Mod
mods.disable=停用
mods.download=模組下載
mods.download.title=模組下載 - %1s

View File

@@ -533,6 +533,7 @@ mods.add.success=成功添加模组 %s。
mods.category=类别
mods.choose_mod=选择模组
mods.curseforge=CurseForge
mods.dependencies=前置 Mod
mods.disable=禁用
mods.download=模组下载
mods.download.title=模组下载 - %1s

View File

@@ -28,6 +28,7 @@ public final class DownloadManager {
}
public interface IMod {
List<Mod> loadDependencies() throws IOException;
Stream<Version> loadVersions() throws IOException;
}

View File

@@ -22,8 +22,10 @@ import org.jackhuang.hmcl.util.Immutable;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -159,6 +161,19 @@ public class CurseAddon implements DownloadManager.IMod {
return isExperimental;
}
@Override
public List<DownloadManager.Mod> loadDependencies() throws IOException {
Set<Integer> dependencies = latestFiles.stream()
.flatMap(latestFile -> latestFile.getDependencies().stream())
.map(Dependency::getAddonId)
.collect(Collectors.toSet());
List<DownloadManager.Mod> mods = new ArrayList<>();
for (int dependencyId : dependencies) {
mods.add(CurseModManager.getAddon(dependencyId).toMod());
}
return mods;
}
@Override
public Stream<DownloadManager.Version> loadVersions() throws IOException {
return CurseModManager.getFiles(this).stream()
@@ -485,7 +500,7 @@ public class CurseAddon implements DownloadManager.IMod {
versionType,
new DownloadManager.File(Collections.emptyMap(), getDownloadUrl(), getFileName()),
Collections.emptyList(),
gameVersion,
gameVersion.stream().filter(ver -> ver.startsWith("1.") || ver.contains("w")).collect(Collectors.toList()),
Collections.emptyList()
);
}

View File

@@ -47,6 +47,11 @@ public final class CurseModManager {
}.getType());
}
public static CurseAddon getAddon(int id) throws IOException {
String response = NetworkUtils.doGet(NetworkUtils.toURL("https://addons-ecs.forgesvc.net/api/v2/addon/" + id));
return JsonUtils.fromNonNullJson(response, CurseAddon.class);
}
public static List<CurseAddon.LatestFile> getFiles(CurseAddon addon) throws IOException {
String response = NetworkUtils.doGet(NetworkUtils.toURL("https://addons-ecs.forgesvc.net/api/v2/addon/" + addon.getId() + "/files"));
return JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon.LatestFile>>() {

View File

@@ -418,6 +418,11 @@ public final class Modrinth {
return latestVersion;
}
@Override
public List<DownloadManager.Mod> loadDependencies() throws IOException {
return Collections.emptyList();
}
@Override
public Stream<DownloadManager.Version> loadVersions() throws IOException {
return Modrinth.getFiles(this).stream()

View File

@@ -865,7 +865,7 @@ public abstract class Task<T> {
* @param tasks the Tasks
* @return a new Task that is completed when all of the given Tasks complete
*/
public static Task<Void> allOf(Task<?>... tasks) {
public static Task<List<Object>> allOf(Task<?>... tasks) {
return allOf(Arrays.asList(tasks));
}
@@ -880,14 +880,15 @@ public abstract class Task<T> {
* @param tasks the Tasks
* @return a new Task that is completed when all of the given Tasks complete
*/
public static Task<Void> allOf(Collection<Task<?>> tasks) {
return new Task<Void>() {
public static Task<List<Object>> allOf(Collection<Task<?>> tasks) {
return new Task<List<Object>>() {
{
setSignificance(TaskSignificance.MINOR);
}
@Override
public void execute() {
setResult(tasks.stream().map(Task::getResult).collect(Collectors.toList()));
}
@Override