diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java index 39060b6e3..b23df4939 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsController.java @@ -1,13 +1,16 @@ package org.cryptomator.ui.vaultstatistics; -import javafx.beans.property.LongProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ReadOnlyObjectProperty; -import javafx.beans.property.SimpleLongProperty; +import javafx.animation.Animation; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; -import javafx.scene.chart.XYChart; +import javafx.scene.chart.XYChart.Data; +import javafx.scene.chart.XYChart.Series; import javafx.stage.Stage; +import javafx.util.Duration; import org.cryptomator.common.vaults.Vault; import org.cryptomator.ui.common.FxController; @@ -16,54 +19,85 @@ import javax.inject.Inject; @VaultStatisticsScoped public class VaultStatisticsController implements FxController { + private static final int IO_SAMPLING_STEPS = 100; + private static final double IO_SAMPLING_INTERVAL = 0.5; + private final Stage window; - private final ReadOnlyObjectProperty vault; + private final Vault vault; @FXML - private LineChart lineGraph; - private final LongProperty currentReadData; - private final LongProperty currentWriteData; - private final XYChart.Series readData; - private final XYChart.Series writeData; - private long timeAtStartOfTracking; + private LineChart lineGraph; + private final Series readData; + private final Series writeData; + private Timeline ioAnimation; + @Inject - public VaultStatisticsController(@VaultStatisticsWindow Stage window, ObjectProperty vault) { + public VaultStatisticsController(@VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault) { this.window = window; this.vault = vault; - readData = new XYChart.Series<>(); + readData = new Series<>(); readData.setName("Read Data"); // For Legend //TODO Add Name to strings.properties - writeData = new XYChart.Series<>(); + writeData = new Series<>(); writeData.setName("Write Data"); //TODO Add Name to strings.properties - - currentReadData = new SimpleLongProperty(); - currentReadData.bind(getVault().getStats().bytesPerSecondReadProperty()); - currentReadData.addListener((observable, oldValue, newValue) -> updateReadWriteData()); - - currentWriteData = new SimpleLongProperty(); - currentWriteData.bind(getVault().getStats().bytesPerSecondWrittenProperty()); - currentWriteData.addListener((observable, oldValue, newValue) -> updateReadWriteData()); + ioAnimation = new Timeline(); //TODO Research better timer + ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(readData, writeData))); + ioAnimation.setCycleCount(Animation.INDEFINITE); + ioAnimation.play(); } @FXML public void initialize() { - window.setTitle(window.getTitle() + " - " + vault.get().getDisplayableName()); - lineGraph.getData().addAll(writeData, readData); + lineGraph.getData().addAll(readData, writeData); + } + + private class IoSamplingAnimationHandler implements EventHandler { + + private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0; + private final Series decryptedBytesRead; + private final Series encryptedBytesWrite; + + public IoSamplingAnimationHandler(Series readData, Series writeData) { + this.decryptedBytesRead = readData; + this.encryptedBytesWrite = writeData; + + // initialize data once and change value of datapoints later: + for (int i = 0; i < IO_SAMPLING_STEPS; i++) { + decryptedBytesRead.getData().add(new Data<>(i, 0)); + encryptedBytesWrite.getData().add(new Data<>(i, 0)); + } + } + + @Override + public void handle(ActionEvent event) { + // move all values one step: + for (int i = 0; i < IO_SAMPLING_STEPS - 1; i++) { + int j = i + 1; + Number tmp = decryptedBytesRead.getData().get(j).getYValue(); + decryptedBytesRead.getData().get(i).setYValue(tmp); + + tmp = encryptedBytesWrite.getData().get(j).getYValue(); + encryptedBytesWrite.getData().get(i).setYValue(tmp); + } + + // add latest value: + final long decBytes = vault.getStats().bytesPerSecondReadProperty().get(); + final double decMb = decBytes * BYTES_TO_MEGABYTES_FACTOR; + final long encBytes = vault.getStats().bytesPerSecondWrittenProperty().get(); + final double encMb = encBytes * BYTES_TO_MEGABYTES_FACTOR; + decryptedBytesRead.getData().get(IO_SAMPLING_STEPS - 1).setYValue(decMb); + encryptedBytesWrite.getData().get(IO_SAMPLING_STEPS - 1).setYValue(encMb); + } } public Vault getVault() { - return vault.get(); - } - - private void updateReadWriteData() { - //So the graphs start at x = 0 - if (timeAtStartOfTracking == 0) { - timeAtStartOfTracking = System.currentTimeMillis(); - } - readData.getData().add(new XYChart.Data((System.currentTimeMillis() - timeAtStartOfTracking) / 1000.0, ((getVault().getStats().bytesPerSecondReadProperty().get()) / 1024.0))); - writeData.getData().add(new XYChart.Data((System.currentTimeMillis() - timeAtStartOfTracking) / 1000.0, ((getVault().getStats().bytesPerSecondWrittenProperty().get()) / 1024.0))); + return vault; } + /* + public ReadOnlyObjectProperty vaultProperty() { + return vault; + }*/ } diff --git a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java index 81a9724f6..0485c17d9 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java +++ b/main/ui/src/main/java/org/cryptomator/ui/vaultstatistics/VaultStatisticsModule.java @@ -4,9 +4,12 @@ import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoMap; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.scene.Scene; -import javafx.stage.Modality; import javafx.stage.Stage; +import org.cryptomator.common.vaults.Vault; +import org.cryptomator.common.vaults.VaultState; import org.cryptomator.ui.common.DefaultSceneFactory; import org.cryptomator.ui.common.FXMLLoaderFactory; import org.cryptomator.ui.common.FxController; @@ -14,7 +17,6 @@ import org.cryptomator.ui.common.FxControllerKey; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.StageFactory; -import org.cryptomator.ui.mainwindow.MainWindow; import javax.inject.Provider; import java.util.Map; @@ -33,12 +35,19 @@ abstract class VaultStatisticsModule { @Provides @VaultStatisticsWindow @VaultStatisticsScoped - static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) { + static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, @VaultStatisticsWindow Vault vault) { Stage stage = factory.create(); - stage.setTitle(resourceBundle.getString("vaultstatistics.title")); + stage.setTitle(String.format(resourceBundle.getString("vaultstatistics.title"), vault.getDisplayableName())); stage.setResizable(false); - stage.initModality(Modality.NONE); - stage.initOwner(owner); + vault.stateProperty().addListener(new ChangeListener<>() { + @Override + public void changed(ObservableValue observable, VaultState oldValue, VaultState newValue) { + if (newValue != VaultState.UNLOCKED) { + stage.hide(); + observable.removeListener(this); + } + } + }); return stage; } diff --git a/main/ui/src/main/resources/css/dark_theme.css b/main/ui/src/main/resources/css/dark_theme.css index 6f8022beb..52cf13aaa 100644 --- a/main/ui/src/main/resources/css/dark_theme.css +++ b/main/ui/src/main/resources/css/dark_theme.css @@ -869,3 +869,56 @@ -fx-background-color: PROGRESS_BAR_BG; -fx-background-radius: 4px; } +/******************************************************************************* + * * + * I/O Statistics * + * * + ******************************************************************************/ +.chart { + -fx-padding: 10px; +} + +.chart-plot-background { + -fx-background-color: MAIN_BG; + -fx-padding: 20px; +} +/* +.default-color0.chart-line-symbol { -fx-background-color: #00FFFF; } +.default-color1.chart-line-symbol { -fx-background-color: #00FF00; } +*/ +/* content */ + +.chart-content { + -fx-padding: 10px; +} +.chart-horizontal-grid-lines { + -fx-stroke: #FFFFFF; +} +.chart-vertical-zero-line, +.chart-horizontal-zero-line { + -fx-stroke: #FFFFFF; +} +.chart-series-line { + -fx-stroke-width: 2px; +} +.chart-alternative-row-fill { + -fx-fill: #E1E1E1; + -fx-stroke: transparent; + -fx-stroke-width: 0; +} + +.default-color0.chart-series-line { -fx-stroke: #FF0000; } +.default-color1.chart-series-line { -fx-stroke: #00FF00 ; } + +.chart-legend { + -fx-background-color: transparent; + -fx-padding: 20px; +} + +.chart-legend-item-symbol{ + -fx-background-radius: 10; +} + +.chart-legend-item{ + -fx-text-fill: #FFFFFF; +} diff --git a/main/ui/src/main/resources/css/light_theme.css b/main/ui/src/main/resources/css/light_theme.css index f87d2338b..3ba377a95 100644 --- a/main/ui/src/main/resources/css/light_theme.css +++ b/main/ui/src/main/resources/css/light_theme.css @@ -870,25 +870,44 @@ } /******************************************************************************* * * - * Vault Statistics * + * I/O Statistics * * * ******************************************************************************/ -.chart-plot-background { - -fx-background-color: #F7F7F7; +.chart { + -fx-padding: 10px; } -.chart-vertical-grid-lines { - -fx-stroke: #49B04A; + +.chart-plot-background { + -fx-background-color: MAIN_BG; + -fx-padding: 20px; +} +/* +.default-color0.chart-line-symbol { -fx-background-color: #00FFFF; } +.default-color1.chart-line-symbol { -fx-background-color: #00FF00; } +*/ +/* content */ + +.chart-content { + -fx-padding: 10px; } .chart-horizontal-grid-lines { - -fx-stroke: #49B04A; + -fx-stroke: #000000; +} +.chart-vertical-zero-line, +.chart-horizontal-zero-line { + -fx-stroke: #000000; +} +.chart-series-line { + -fx-stroke-width: 2px; } .chart-alternative-row-fill { -fx-fill: #E1E1E1; -fx-stroke: transparent; -fx-stroke-width: 0; } -.default-color0.chart-series-line { -fx-stroke: #2D4D2E; } -.default-color1.chart-series-line { -fx-stroke: #66CC68 ; } + +.default-color0.chart-series-line { -fx-stroke: #FF0000; } +.default-color1.chart-series-line { -fx-stroke: #00FF00 ; } .chart-legend { -fx-background-color: transparent; @@ -896,9 +915,9 @@ } .chart-legend-item-symbol{ - -fx-background-radius: 0; + -fx-background-radius: 10; } .chart-legend-item{ - -fx-text-fill: #191970; + -fx-text-fill: #000000; } diff --git a/main/ui/src/main/resources/fxml/vault_statistics.fxml b/main/ui/src/main/resources/fxml/vault_statistics.fxml index b4891eec8..36f0098ce 100644 --- a/main/ui/src/main/resources/fxml/vault_statistics.fxml +++ b/main/ui/src/main/resources/fxml/vault_statistics.fxml @@ -25,14 +25,13 @@ + styleClass="chart-plot-background, chart-alternative-row-fill, default-color2.chart-series-line" + fx:id="lineGraph" createSymbols="true" legendVisible="true" prefHeight="372.0" prefWidth="423.0" visible="true" animated="false" title="%vaultstatistics.throughputTitle" verticalZeroLineVisible="true" verticalGridLinesVisible="false" horizontalGridLinesVisible="true"> - + - + diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties index 133b99c23..9c26942c6 100644 --- a/main/ui/src/main/resources/i18n/strings.properties +++ b/main/ui/src/main/resources/i18n/strings.properties @@ -161,9 +161,9 @@ preferences.donationKey.getDonationKey=Get a donation key preferences.about=About # Vault Statistics -vaultstatistics.title=Vault Statistics +vaultstatistics.title=Statistics for %s vaultstatistics.xAxisTimeLabel=Seconds -vaultstatistics.yAxisTimeLabel=Throughput in KiB +vaultstatistics.yAxisThroughputLabel=Throughput in KiB/s vaultstatistics.throughputTitle=Read and Writes # Main Window