Search in sources :

Example 1 with BusyAnimation

use of bisq.desktop.components.BusyAnimation in project bisq-desktop by bisq-network.

the class MainView method createSplashScreen.

private VBox createSplashScreen() {
    VBox vBox = new VBox();
    vBox.setAlignment(Pos.CENTER);
    vBox.setSpacing(0);
    vBox.setId("splash");
    ImageView logo = new ImageView();
    logo.setId("image-splash-logo");
    // createBitcoinInfoBox
    btcSplashInfo = new AutoTooltipLabel();
    btcSplashInfo.textProperty().bind(model.btcInfo);
    walletServiceErrorMsgListener = (ov, oldValue, newValue) -> {
        btcSplashInfo.setId("splash-error-state-msg");
        btcSplashInfo.getStyleClass().add("error-text");
    };
    model.walletServiceErrorMsg.addListener(walletServiceErrorMsgListener);
    btcSyncIndicator = new ProgressBar();
    btcSyncIndicator.setPrefWidth(120);
    btcSyncIndicator.progressProperty().bind(model.btcSyncProgress);
    ImageView btcSyncIcon = new ImageView();
    btcSyncIcon.setVisible(false);
    btcSyncIcon.setManaged(false);
    btcSyncIconIdListener = (ov, oldValue, newValue) -> {
        btcSyncIcon.setId(newValue);
        btcSyncIcon.setVisible(true);
        btcSyncIcon.setManaged(true);
        btcSyncIndicator.setVisible(false);
        btcSyncIndicator.setManaged(false);
    };
    model.btcSplashSyncIconId.addListener(btcSyncIconIdListener);
    HBox blockchainSyncBox = new HBox();
    blockchainSyncBox.setSpacing(10);
    blockchainSyncBox.setAlignment(Pos.CENTER);
    blockchainSyncBox.setPadding(new Insets(40, 0, 0, 0));
    blockchainSyncBox.setPrefHeight(50);
    blockchainSyncBox.getChildren().addAll(btcSplashInfo, btcSyncIndicator, btcSyncIcon);
    // create P2PNetworkBox
    splashP2PNetworkLabel = new AutoTooltipLabel();
    splashP2PNetworkLabel.setWrapText(true);
    splashP2PNetworkLabel.setMaxWidth(500);
    splashP2PNetworkLabel.setTextAlignment(TextAlignment.CENTER);
    splashP2PNetworkLabel.textProperty().bind(model.p2PNetworkInfo);
    splashP2PNetworkBusyAnimation = new BusyAnimation();
    splashP2PNetworkErrorMsgListener = (ov, oldValue, newValue) -> {
        if (newValue != null) {
            splashP2PNetworkLabel.setId("splash-error-state-msg");
            splashP2PNetworkLabel.getStyleClass().add("error-text");
            splashP2PNetworkBusyAnimation.stop();
        } else if (model.splashP2PNetworkAnimationVisible.get()) {
            splashP2PNetworkBusyAnimation.play();
        }
    };
    model.p2pNetworkWarnMsg.addListener(splashP2PNetworkErrorMsgListener);
    Button showTorNetworkSettingsButton = new AutoTooltipButton(Res.get("settings.net.openTorSettingsButton"));
    showTorNetworkSettingsButton.setVisible(false);
    showTorNetworkSettingsButton.setManaged(false);
    showTorNetworkSettingsButton.setOnAction(e -> {
        model.torNetworkSettingsWindow.show();
    });
    ImageView splashP2PNetworkIcon = new ImageView();
    splashP2PNetworkIcon.setId("image-connection-tor");
    splashP2PNetworkIcon.setVisible(false);
    splashP2PNetworkIcon.setManaged(false);
    HBox.setMargin(splashP2PNetworkIcon, new Insets(0, 0, 5, 0));
    Timer showTorNetworkSettingsTimer = UserThread.runAfter(() -> {
        showTorNetworkSettingsButton.setVisible(true);
        showTorNetworkSettingsButton.setManaged(true);
    }, SHOW_TOR_SETTINGS_DELAY_SEC);
    splashP2PNetworkIconIdListener = (ov, oldValue, newValue) -> {
        splashP2PNetworkIcon.setId(newValue);
        splashP2PNetworkIcon.setVisible(true);
        splashP2PNetworkIcon.setManaged(true);
        // if we can connect in 10 sec. we know that tor is working
        showTorNetworkSettingsTimer.stop();
    };
    model.p2PNetworkIconId.addListener(splashP2PNetworkIconIdListener);
    splashP2PNetworkVisibleListener = (ov, oldValue, newValue) -> splashP2PNetworkBusyAnimation.setIsRunning(newValue);
    model.splashP2PNetworkAnimationVisible.addListener(splashP2PNetworkVisibleListener);
    HBox splashP2PNetworkBox = new HBox();
    splashP2PNetworkBox.setSpacing(10);
    splashP2PNetworkBox.setAlignment(Pos.CENTER);
    splashP2PNetworkBox.setPrefHeight(50);
    splashP2PNetworkBox.getChildren().addAll(splashP2PNetworkLabel, splashP2PNetworkBusyAnimation, splashP2PNetworkIcon, showTorNetworkSettingsButton);
    vBox.getChildren().addAll(logo, blockchainSyncBox, splashP2PNetworkBox);
    return vBox;
}
Also used : HBox(javafx.scene.layout.HBox) Insets(javafx.geometry.Insets) BusyAnimation(bisq.desktop.components.BusyAnimation) Timer(bisq.common.Timer) Button(javafx.scene.control.Button) ToggleButton(javafx.scene.control.ToggleButton) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton) AutoTooltipToggleButton(bisq.desktop.components.AutoTooltipToggleButton) ImageView(javafx.scene.image.ImageView) AutoTooltipLabel(bisq.desktop.components.AutoTooltipLabel) VBox(javafx.scene.layout.VBox) ProgressBar(javafx.scene.control.ProgressBar) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton)

Example 2 with BusyAnimation

use of bisq.desktop.components.BusyAnimation in project bisq-desktop by bisq-network.

the class TakeOfferView method addFundingGroup.

private void addFundingGroup() {
    // don't increase gridRow as we removed button when this gets visible
    payFundsPane = FormBuilder.addTitledGroupBg(gridPane, gridRow, 3, Res.get("takeOffer.fundsBox.title"), Layout.GROUP_DISTANCE);
    GridPane.setColumnSpan(payFundsPane, 3);
    payFundsPane.setVisible(false);
    Tuple2<Label, FundsTextField> fundsTuple = addLabelFundsTextfield(gridPane, gridRow, Res.get("shared.totalsNeeded"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
    totalToPayLabel = fundsTuple.first;
    totalToPayLabel.setVisible(false);
    totalToPayTextField = fundsTuple.second;
    totalToPayTextField.setVisible(false);
    qrCodeImageView = new ImageView();
    qrCodeImageView.setVisible(false);
    qrCodeImageView.getStyleClass().add("qr-code");
    Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
    qrCodeImageView.setOnMouseClicked(e -> GUIUtil.showFeeInfoBeforeExecute(() -> UserThread.runAfter(() -> new QRCodeWindow(getBitcoinURI()).show(), 200, TimeUnit.MILLISECONDS)));
    GridPane.setRowIndex(qrCodeImageView, gridRow);
    GridPane.setColumnIndex(qrCodeImageView, 2);
    GridPane.setRowSpan(qrCodeImageView, 3);
    GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_AND_GROUP_DISTANCE - 9, 0, 0, 5));
    gridPane.getChildren().add(qrCodeImageView);
    Tuple2<Label, AddressTextField> addressTuple = FormBuilder.addLabelAddressTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletAddress"));
    addressLabel = addressTuple.first;
    addressLabel.setVisible(false);
    addressTextField = addressTuple.second;
    addressTextField.setVisible(false);
    Tuple2<Label, BalanceTextField> balanceTuple = FormBuilder.addLabelBalanceTextField(gridPane, ++gridRow, Res.get("shared.tradeWalletBalance"));
    balanceLabel = balanceTuple.first;
    balanceLabel.setVisible(false);
    balanceTextField = balanceTuple.second;
    balanceTextField.setVisible(false);
    fundingHBox = new HBox();
    fundingHBox.setVisible(false);
    fundingHBox.setManaged(false);
    fundingHBox.setSpacing(10);
    Button fundFromSavingsWalletButton = new AutoTooltipButton(Res.get("shared.fundFromSavingsWalletButton"));
    fundFromSavingsWalletButton.setDefaultButton(true);
    fundFromSavingsWalletButton.setDefaultButton(false);
    fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
    Label label = new AutoTooltipLabel(Res.get("shared.OR"));
    label.setPadding(new Insets(5, 0, 0, 0));
    Button fundFromExternalWalletButton = new AutoTooltipButton(Res.get("shared.fundFromExternalWalletButton"));
    fundFromExternalWalletButton.setDefaultButton(false);
    fundFromExternalWalletButton.setOnAction(e -> GUIUtil.showFeeInfoBeforeExecute(this::openWallet));
    waitingForFundsBusyAnimation = new BusyAnimation(false);
    waitingForFundsLabel = new AutoTooltipLabel();
    waitingForFundsLabel.setPadding(new Insets(5, 0, 0, 0));
    fundingHBox.getChildren().addAll(fundFromSavingsWalletButton, label, fundFromExternalWalletButton, waitingForFundsBusyAnimation, waitingForFundsLabel);
    GridPane.setRowIndex(fundingHBox, ++gridRow);
    GridPane.setColumnIndex(fundingHBox, 1);
    GridPane.setMargin(fundingHBox, new Insets(15, 10, 0, 0));
    gridPane.getChildren().add(fundingHBox);
    takeOfferButton = FormBuilder.addButtonAfterGroup(gridPane, gridRow, "");
    takeOfferButton.setVisible(false);
    takeOfferButton.setManaged(false);
    takeOfferButton.setMinHeight(40);
    takeOfferButton.setPadding(new Insets(0, 20, 0, 20));
    takeOfferButton.setOnAction(e -> onTakeOffer());
    cancelButton2 = FormBuilder.addButton(gridPane, ++gridRow, Res.get("shared.cancel"));
    cancelButton2.setOnAction(e -> {
        if (model.dataModel.getIsBtcWalletFunded().get()) {
            new Popup<>().warning(Res.get("takeOffer.alreadyFunded.askCancel")).closeButtonText(Res.get("shared.no")).actionButtonText(Res.get("shared.yesCancel")).onAction(() -> {
                model.dataModel.swapTradeToSavings();
                close();
            }).show();
        } else {
            close();
            model.dataModel.swapTradeToSavings();
        }
    });
    cancelButton2.setDefaultButton(false);
    cancelButton2.setVisible(false);
}
Also used : HBox(javafx.scene.layout.HBox) Insets(javafx.geometry.Insets) BusyAnimation(bisq.desktop.components.BusyAnimation) AddressTextField(bisq.desktop.components.AddressTextField) Tooltip(javafx.scene.control.Tooltip) AutoTooltipLabel(bisq.desktop.components.AutoTooltipLabel) Label(javafx.scene.control.Label) QRCodeWindow(bisq.desktop.main.overlays.windows.QRCodeWindow) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton) Button(javafx.scene.control.Button) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton) BalanceTextField(bisq.desktop.components.BalanceTextField) FundsTextField(bisq.desktop.components.FundsTextField) ImageView(javafx.scene.image.ImageView) AutoTooltipLabel(bisq.desktop.components.AutoTooltipLabel)

Example 3 with BusyAnimation

use of bisq.desktop.components.BusyAnimation in project bisq-desktop by bisq-network.

the class Overlay method addBusyAnimation.

protected void addBusyAnimation() {
    BusyAnimation busyAnimation = new BusyAnimation();
    GridPane.setHalignment(busyAnimation, HPos.CENTER);
    GridPane.setRowIndex(busyAnimation, ++rowIndex);
    GridPane.setColumnSpan(busyAnimation, 2);
    gridPane.getChildren().add(busyAnimation);
}
Also used : BusyAnimation(bisq.desktop.components.BusyAnimation)

Example 4 with BusyAnimation

use of bisq.desktop.components.BusyAnimation in project bisq-desktop by bisq-network.

the class TorNetworkSettingsWindow method addContent.

private void addContent() {
    addTitledGroupBg(gridPane, ++rowIndex, 1, Res.get("torNetworkSettingWindow.deleteFiles.header"));
    Label deleteFilesLabel = addLabel(gridPane, rowIndex, Res.get("torNetworkSettingWindow.deleteFiles.info"), Layout.FIRST_ROW_DISTANCE);
    deleteFilesLabel.setWrapText(true);
    GridPane.setColumnIndex(deleteFilesLabel, 0);
    GridPane.setColumnSpan(deleteFilesLabel, 2);
    GridPane.setHalignment(deleteFilesLabel, HPos.LEFT);
    GridPane.setValignment(deleteFilesLabel, VPos.TOP);
    Tuple3<Button, BusyAnimation, Label> tuple = addButtonBusyAnimationLabelAfterGroup(gridPane, ++rowIndex, Res.get("torNetworkSettingWindow.deleteFiles.button"));
    Button deleteFilesButton = tuple.first;
    deleteFilesButton.setOnAction(e -> {
        tuple.second.play();
        tuple.third.setText(Res.get("torNetworkSettingWindow.deleteFiles.progress"));
        gridPane.setMouseTransparent(true);
        deleteFilesButton.setDisable(true);
        cleanTorDir(() -> {
            tuple.second.stop();
            tuple.third.setText("");
            new Popup<>().feedback(Res.get("torNetworkSettingWindow.deleteFiles.success")).useShutDownButton().hideCloseButton().show();
        });
    });
    addTitledGroupBg(gridPane, ++rowIndex, 7, Res.get("torNetworkSettingWindow.bridges.header"), Layout.GROUP_DISTANCE);
    Label bridgesLabel = addLabel(gridPane, rowIndex, Res.get("torNetworkSettingWindow.bridges.info"), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
    bridgesLabel.setWrapText(true);
    GridPane.setColumnIndex(bridgesLabel, 0);
    GridPane.setColumnSpan(bridgesLabel, 2);
    GridPane.setHalignment(bridgesLabel, HPos.LEFT);
    GridPane.setValignment(bridgesLabel, VPos.TOP);
    // addLabelTextArea(gridPane, rowIndex, Res.get("torNetworkSettingWindow.info"), "", Layout.FIRST_ROW_AND_GROUP_DISTANCE);
    ToggleGroup toggleGroup = new ToggleGroup();
    // noBridges
    noBridgesRadioButton = addRadioButton(gridPane, ++rowIndex, toggleGroup, Res.get("torNetworkSettingWindow.noBridges"));
    noBridgesRadioButton.setUserData(BridgeOption.NONE);
    GridPane.setMargin(noBridgesRadioButton, new Insets(20, 0, 0, 0));
    // providedBridges
    providedBridgesRadioButton = addRadioButton(gridPane, ++rowIndex, toggleGroup, Res.get("torNetworkSettingWindow.providedBridges"));
    providedBridgesRadioButton.setUserData(BridgeOption.PROVIDED);
    final Tuple2<Label, ComboBox> labelComboBoxTuple2 = addLabelComboBox(gridPane, ++rowIndex, Res.get("torNetworkSettingWindow.transportType"));
    transportTypeLabel = labelComboBoxTuple2.first;
    transportTypeComboBox = labelComboBoxTuple2.second;
    transportTypeComboBox.setItems(FXCollections.observableArrayList(Arrays.asList(Transport.OBFS_4, Transport.OBFS_3, Transport.MEEK_AMAZON, Transport.MEEK_AZURE)));
    transportTypeComboBox.setConverter(new StringConverter<Transport>() {

        @Override
        public String toString(Transport transport) {
            switch(transport) {
                case OBFS_3:
                    return Res.get("torNetworkSettingWindow.obfs3");
                case MEEK_AMAZON:
                    return Res.get("torNetworkSettingWindow.meekAmazon");
                case MEEK_AZURE:
                    return Res.get("torNetworkSettingWindow.meekAzure");
                default:
                case OBFS_4:
                    return Res.get("torNetworkSettingWindow.obfs4");
            }
        }

        @Override
        public Transport fromString(String string) {
            return null;
        }
    });
    // customBridges
    customBridgesRadioButton = addRadioButton(gridPane, ++rowIndex, toggleGroup, Res.get("torNetworkSettingWindow.customBridges"));
    customBridgesRadioButton.setUserData(BridgeOption.CUSTOM);
    final Tuple2<Label, TextArea> labelTextAreaTuple2 = addLabelTextArea(gridPane, ++rowIndex, Res.get("torNetworkSettingWindow.enterBridge"), Res.get("torNetworkSettingWindow.enterBridgePrompt"));
    enterBridgeLabel = labelTextAreaTuple2.first;
    bridgeEntriesTextArea = labelTextAreaTuple2.second;
    Label label2 = addLabel(gridPane, ++rowIndex, Res.get("torNetworkSettingWindow.restartInfo"));
    label2.setWrapText(true);
    GridPane.setColumnIndex(label2, 1);
    GridPane.setColumnSpan(label2, 2);
    GridPane.setHalignment(label2, HPos.LEFT);
    GridPane.setValignment(label2, VPos.TOP);
    GridPane.setMargin(label2, new Insets(10, 10, 20, 0));
    // init persisted values
    selectedBridgeOption = BridgeOption.values()[preferences.getBridgeOptionOrdinal()];
    switch(selectedBridgeOption) {
        case PROVIDED:
            toggleGroup.selectToggle(providedBridgesRadioButton);
            break;
        case CUSTOM:
            toggleGroup.selectToggle(customBridgesRadioButton);
            break;
        default:
        case NONE:
            toggleGroup.selectToggle(noBridgesRadioButton);
            break;
    }
    applyToggleSelection();
    selectedTorTransportOrdinal = Transport.values()[preferences.getTorTransportOrdinal()];
    transportTypeComboBox.getSelectionModel().select(selectedTorTransportOrdinal);
    customBridges = preferences.getCustomBridges();
    bridgeEntriesTextArea.setText(customBridges);
    toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
        selectedBridgeOption = (BridgeOption) newValue.getUserData();
        preferences.setBridgeOptionOrdinal(selectedBridgeOption.ordinal());
        applyToggleSelection();
    });
    transportTypeComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
        selectedTorTransportOrdinal = newValue;
        preferences.setTorTransportOrdinal(selectedTorTransportOrdinal.ordinal());
        setBridgeAddressesByTransport();
    });
    bridgeEntriesTextArea.textProperty().addListener((observable, oldValue, newValue) -> {
        customBridges = newValue;
        preferences.setCustomBridges(customBridges);
        setBridgeAddressesByCustomBridges();
    });
}
Also used : BusyAnimation(bisq.desktop.components.BusyAnimation) Insets(javafx.geometry.Insets) TextArea(javafx.scene.control.TextArea) ComboBox(javafx.scene.control.ComboBox) Label(javafx.scene.control.Label) Button(javafx.scene.control.Button) RadioButton(javafx.scene.control.RadioButton) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton) Popup(bisq.desktop.main.overlays.popups.Popup) ToggleGroup(javafx.scene.control.ToggleGroup)

Example 5 with BusyAnimation

use of bisq.desktop.components.BusyAnimation in project bisq-desktop by bisq-network.

the class WalletPasswordWindow method addButtons.

private void addButtons() {
    BusyAnimation busyAnimation = new BusyAnimation(false);
    Label deriveStatusLabel = new AutoTooltipLabel();
    unlockButton = new AutoTooltipButton(Res.get("shared.unlock"));
    unlockButton.setDefaultButton(true);
    unlockButton.setDisable(true);
    unlockButton.setOnAction(e -> {
        String password = passwordTextField.getText();
        checkArgument(password.length() < 500, Res.get("password.tooLong"));
        KeyCrypterScrypt keyCrypterScrypt = walletsManager.getKeyCrypterScrypt();
        if (keyCrypterScrypt != null) {
            busyAnimation.play();
            deriveStatusLabel.setText(Res.get("password.deriveKey"));
            ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, password, aesKey -> {
                if (walletsManager.checkAESKey(aesKey)) {
                    if (aesKeyHandler != null)
                        aesKeyHandler.onAesKey(aesKey);
                    hide();
                } else {
                    busyAnimation.stop();
                    deriveStatusLabel.setText("");
                    UserThread.runAfter(() -> new Popup<>().warning(Res.get("password.wrongPw")).onClose(this::blurAgain).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
                }
            });
        } else {
            log.error("wallet.getKeyCrypter() is null, that must not happen.");
        }
    });
    forgotPasswordButton = new AutoTooltipButton(Res.get("password.forgotPassword"));
    forgotPasswordButton.setOnAction(e -> {
        forgotPasswordButton.setDisable(true);
        unlockButton.setDefaultButton(false);
        showRestoreScreen();
    });
    Button cancelButton = new AutoTooltipButton(Res.get("shared.cancel"));
    cancelButton.setOnAction(event -> {
        hide();
        closeHandlerOptional.ifPresent(Runnable::run);
    });
    HBox hBox = new HBox();
    hBox.setMinWidth(560);
    hBox.setSpacing(10);
    GridPane.setRowIndex(hBox, ++rowIndex);
    GridPane.setColumnIndex(hBox, 1);
    hBox.setAlignment(Pos.CENTER_LEFT);
    if (hideCloseButton)
        hBox.getChildren().addAll(unlockButton, forgotPasswordButton, busyAnimation, deriveStatusLabel);
    else
        hBox.getChildren().addAll(unlockButton, cancelButton);
    gridPane.getChildren().add(hBox);
    ColumnConstraints columnConstraints1 = new ColumnConstraints();
    columnConstraints1.setHalignment(HPos.RIGHT);
    columnConstraints1.setHgrow(Priority.SOMETIMES);
    ColumnConstraints columnConstraints2 = new ColumnConstraints();
    columnConstraints2.setHgrow(Priority.ALWAYS);
    gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2);
}
Also used : HBox(javafx.scene.layout.HBox) BusyAnimation(bisq.desktop.components.BusyAnimation) Button(javafx.scene.control.Button) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton) FormBuilder.addButton(bisq.desktop.util.FormBuilder.addButton) ColumnConstraints(javafx.scene.layout.ColumnConstraints) Popup(bisq.desktop.main.overlays.popups.Popup) AutoTooltipLabel(bisq.desktop.components.AutoTooltipLabel) Label(javafx.scene.control.Label) AutoTooltipLabel(bisq.desktop.components.AutoTooltipLabel) KeyCrypterScrypt(org.bitcoinj.crypto.KeyCrypterScrypt) AutoTooltipButton(bisq.desktop.components.AutoTooltipButton)

Aggregations

BusyAnimation (bisq.desktop.components.BusyAnimation)14 Button (javafx.scene.control.Button)12 Label (javafx.scene.control.Label)11 Insets (javafx.geometry.Insets)9 AutoTooltipButton (bisq.desktop.components.AutoTooltipButton)8 AutoTooltipLabel (bisq.desktop.components.AutoTooltipLabel)8 HBox (javafx.scene.layout.HBox)8 Popup (bisq.desktop.main.overlays.popups.Popup)6 ImageView (javafx.scene.image.ImageView)5 Tooltip (javafx.scene.control.Tooltip)4 Timer (bisq.common.Timer)2 Utilities (bisq.common.util.Utilities)2 Res (bisq.core.locale.Res)2 CryptoCurrencyAccountPayload (bisq.core.payment.payload.CryptoCurrencyAccountPayload)2 PaymentAccountPayload (bisq.core.payment.payload.PaymentAccountPayload)2 Contract (bisq.core.trade.Contract)2 AddressTextField (bisq.desktop.components.AddressTextField)2 File (java.io.File)2 IOException (java.io.IOException)2 ArrayList (java.util.ArrayList)2