Merge branch 'develop' into feature/#1013-#1061-cleanupAndInformation

This commit is contained in:
Armin Schrenk
2020-11-10 12:08:23 +01:00
25 changed files with 932 additions and 71 deletions

View File

@@ -32,7 +32,9 @@ import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import java.io.IOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -68,6 +70,7 @@ public class Vault {
private final BooleanBinding unknownError;
private final StringBinding accessPoint;
private final BooleanBinding accessPointPresent;
private final BooleanProperty showingStats;
private volatile Volume volume;
@@ -90,6 +93,7 @@ public class Vault {
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
this.accessPoint = Bindings.createStringBinding(this::getAccessPoint, state);
this.accessPointPresent = this.accessPoint.isNotEmpty();
this.showingStats = new SimpleBooleanProperty(false);
}
// ******************************************************************************
@@ -268,6 +272,15 @@ public class Vault {
}
}
public BooleanProperty showingStatsProperty() {
return showingStats;
}
public boolean isShowingStats() {
return accessPointPresent.get();
}
// ******************************************************************************
// Getter/Setter
// *******************************************************************************/

View File

@@ -8,8 +8,10 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
@@ -28,6 +30,15 @@ public class VaultStats {
private final ScheduledService<Optional<CryptoFileSystemStats>> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
private final LongProperty bytesPerSecondEncrypted = new SimpleLongProperty();
private final LongProperty bytesPerSecondDecrypted = new SimpleLongProperty();
private final DoubleProperty cacheHitRate = new SimpleDoubleProperty();
private final LongProperty toalBytesRead = new SimpleLongProperty();
private final LongProperty toalBytesWritten = new SimpleLongProperty();
private final LongProperty totalBytesEncrypted = new SimpleLongProperty();
private final LongProperty totalBytesDecrypted = new SimpleLongProperty();
private final LongProperty filesRead = new SimpleLongProperty();
private final LongProperty filesWritten = new SimpleLongProperty();
@Inject
VaultStats(AtomicReference<CryptoFileSystem> fs, ObjectProperty<VaultState> state, ExecutorService executor) {
@@ -53,8 +64,28 @@ public class VaultStats {
private void updateStats(Optional<CryptoFileSystemStats> stats) {
assert Platform.isFxApplicationThread();
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0l));
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0l));
bytesPerSecondRead.set(stats.map(CryptoFileSystemStats::pollBytesRead).orElse(0L));
bytesPerSecondWritten.set(stats.map(CryptoFileSystemStats::pollBytesWritten).orElse(0L));
cacheHitRate.set(stats.map(this::getCacheHitRate).orElse(0.0));
bytesPerSecondDecrypted.set(stats.map(CryptoFileSystemStats::pollBytesDecrypted).orElse(0L));
bytesPerSecondEncrypted.set(stats.map(CryptoFileSystemStats::pollBytesEncrypted).orElse(0L));
toalBytesRead.set(stats.map(CryptoFileSystemStats::pollTotalBytesRead).orElse(0L));
toalBytesWritten.set(stats.map(CryptoFileSystemStats::pollTotalBytesWritten).orElse(0L));
totalBytesEncrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesEncrypted).orElse(0L));
totalBytesDecrypted.set(stats.map(CryptoFileSystemStats::pollTotalBytesDecrypted).orElse(0L));
filesRead.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesRead).orElse(0L));
filesWritten.set(stats.map(CryptoFileSystemStats::pollAmountOfAccessesWritten).orElse(0L));
}
private double getCacheHitRate(CryptoFileSystemStats stats) {
long accesses = stats.pollChunkCacheAccesses();
long hits = stats.pollChunkCacheHits();
if (accesses == 0) {
return 0.0;
} else {
return hits / (double) accesses;
}
}
private class UpdateStatsService extends ScheduledService<Optional<CryptoFileSystemStats>> {
@@ -98,4 +129,50 @@ public class VaultStats {
public long getBytesPerSecondWritten() {
return bytesPerSecondWritten.get();
}
public LongProperty bytesPerSecondEncryptedProperty() {
return bytesPerSecondEncrypted;
}
public long getBytesPerSecondEnrypted() {
return bytesPerSecondEncrypted.get();
}
public LongProperty bytesPerSecondDecryptedProperty() {
return bytesPerSecondDecrypted;
}
public long getBytesPerSecondDecrypted() {
return bytesPerSecondDecrypted.get();
}
public DoubleProperty cacheHitRateProperty() { return cacheHitRate; }
public double getCacheHitRate() {
return cacheHitRate.get();
}
public LongProperty toalBytesReadProperty() {return toalBytesRead;}
public long getTotalBytesRead() { return toalBytesRead.get();}
public LongProperty toalBytesWrittenProperty() {return toalBytesWritten;}
public long getTotalBytesWritten() { return toalBytesWritten.get();}
public LongProperty totalBytesEncryptedProperty() {return totalBytesEncrypted;}
public long getTotalBytesEncrypted() { return totalBytesEncrypted.get();}
public LongProperty totalBytesDecryptedProperty() {return totalBytesDecrypted;}
public long getTotalBytesDecrypted() { return totalBytesDecrypted.get();}
public LongProperty filesRead() { return filesRead;}
public long getFilesRead() { return filesRead.get();}
public LongProperty filesWritten() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
}

View File

@@ -24,31 +24,29 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>1.9.12</cryptomator.cryptofs.version>
<cryptomator.cryptofs.version>1.9.13</cryptomator.cryptofs.version>
<cryptomator.integrations.version>0.1.6</cryptomator.integrations.version>
<cryptomator.integrations.win.version>0.1.0-beta1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>0.1.0-beta3</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>0.1.0-beta1</cryptomator.integrations.linux.version>
<cryptomator.integrations.linux.version>0.1.0-beta2</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.2.5</cryptomator.fuse.version>
<cryptomator.dokany.version>1.1.15</cryptomator.dokany.version>
<cryptomator.webdav.version>1.0.12</cryptomator.webdav.version>
<cryptomator.dokany.version>1.2.0</cryptomator.dokany.version>
<cryptomator.webdav.version>1.0.13</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>14</javafx.version>
<javafx.version>15</javafx.version>
<commons-lang3.version>3.11</commons-lang3.version>
<secret-service.version>1.1.0</secret-service.version>
<kdewallet.version>1.1.1</kdewallet.version>
<jwt.version>3.10.3</jwt.version>
<jwt.version>3.11.0</jwt.version>
<easybind.version>2.1.0</easybind.version>
<guava.version>30.0-jre</guava.version>
<dagger.version>2.22</dagger.version>
<dagger.version>2.29.1</dagger.version>
<gson.version>2.8.6</gson.version>
<slf4j.version>1.7.30</slf4j.version>
<logback.version>1.2.3</logback.version>
<!-- test dependencies -->
<junit.jupiter.version>5.6.2</junit.jupiter.version>
<mockito.version>3.3.3</mockito.version>
<junit.jupiter.version>5.7.0</junit.jupiter.version>
<mockito.version>3.6.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
</properties>
@@ -79,11 +77,6 @@
</dependency>
<!-- Cryptomator Libs -->
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>siv-mode</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
@@ -242,6 +235,7 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
@@ -382,7 +376,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<version>0.8.6</version>
<executions>
<execution>
<id>prepare-agent</id>

View File

@@ -4,6 +4,7 @@ import dagger.BindsInstance;
import dagger.Subcomponent;
import javax.annotation.Nullable;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -16,6 +17,14 @@ public interface ErrorComponent {
Scene scene();
default void showErrorScene() {
if (Platform.isFxApplicationThread()) {
show();
} else {
Platform.runLater(this::show);
}
}
private void show() {
Stage stage = window();
stage.setScene(scene());
stage.show();

View File

@@ -28,6 +28,7 @@ public enum FxmlFile {
UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), //
UNLOCK_SUCCESS("/fxml/unlock_success.fxml"), //
VAULT_OPTIONS("/fxml/vault_options.fxml"), //
VAULT_STATISTICS("/fxml/stats.fxml"), //
WRONGFILEALERT("/fxml/wrongfilealert.fxml");
private final String ressourcePathString;

View File

@@ -1,7 +1,11 @@
package org.cryptomator.ui.common;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.binding.LongBinding;
import javafx.beans.binding.StringBinding;
import javafx.beans.value.ObservableObjectValue;
import javafx.beans.value.ObservableValue;
/**
@@ -29,4 +33,61 @@ public final class WeakBindings {
};
}
/**
* Create a new LongBinding that listens to changes from the given observable without being strongly referenced by it.
*
* @param observable The observable
* @return a LongBinding weakly referenced from the given observable
*/
public static LongBinding bindLong(ObservableValue<Number> observable) {
return new LongBinding() {
{
bind(observable);
}
@Override
protected long computeValue() {
return observable.getValue().longValue();
}
};
}
/**
* Create a new DoubleBinding that listens to changes from the given observable without being strongly referenced by it.
*
* @param observable The observable
* @return a DoubleBinding weakly referenced from the given observable
*/
public static DoubleBinding bindDouble(ObservableValue<Number> observable) {
return new DoubleBinding() {
{
bind(observable);
}
@Override
protected double computeValue() {
return observable.getValue().doubleValue();
}
};
}
/**
* Create a new IntegerBinding that listens to changes from the given observable without being strongly referenced by it.
*
* @param observable The observable
* @return a IntegerBinding weakly referenced from the given observable
*/
public static IntegerBinding bindInterger(ObservableValue<Number> observable) {
return new IntegerBinding() {
{
bind(observable);
}
@Override
protected int computeValue() {
return observable.getValue().intValue();
}
};
}
}

View File

@@ -0,0 +1,88 @@
package org.cryptomator.ui.controls;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.control.Label;
public class DataLabel extends Label {
private static final long KIB_THRESHOLD = 1l << 7; // 0.128 kiB
private static final long MIB_THRESHOLD = 1l << 19; // 0.512 MiB
private static final long GIB_THRESHOLD = 1l << 29; // 0.512 GiB
private final StringProperty byteFormat = new SimpleStringProperty("-");
private final StringProperty kibFormat = new SimpleStringProperty("%.3f");
private final StringProperty mibFormat = new SimpleStringProperty("%.3f");
private final StringProperty gibFormat = new SimpleStringProperty("%.3f");
private final LongProperty dataInBytes = new SimpleLongProperty();
public DataLabel() {
textProperty().bind(createStringBinding());
}
protected StringBinding createStringBinding() {
return Bindings.createStringBinding(this::updateText, kibFormat, mibFormat, gibFormat, dataInBytes);
}
private String updateText() {
long data = dataInBytes.get();
if (data > GIB_THRESHOLD) {
double giB = ((double) data) / 1024.0 / 1024.0 / 1024.0;
return String.format(gibFormat.get(), giB);
} else if (data > MIB_THRESHOLD) {
double miB = ((double) data) / 1024.0 / 1024.0;
return String.format(mibFormat.get(), miB);
} else if (data > KIB_THRESHOLD) {
double kiB = ((double) data) / 1024.0;
return String.format(kibFormat.get(), kiB);
} else {
return String.format(byteFormat.get(), data);
}
}
public StringProperty byteFormatProperty() { return byteFormat; }
public String getByteFormat() { return byteFormat.get(); }
public void setByteFormat(String byteFormat) {
this.byteFormat.set(byteFormat);
}
public StringProperty kibFormatProperty() { return kibFormat; }
public String getKibFormat() { return kibFormat.get(); }
public void setKibFormat(String kibFormat) {
this.kibFormat.set(kibFormat);
}
public StringProperty mibFormatProperty() { return mibFormat; }
public String getMibFormat() { return mibFormat.get(); }
public void setMibFormat(String mibFormat) {
this.mibFormat.set(mibFormat);
}
public StringProperty gibFormatProperty() { return gibFormat; }
public String getGibFormat() { return gibFormat.get(); }
public void setGibFormat(String gibFormat) {
this.gibFormat.set(gibFormat);
}
public LongProperty dataInBytesProperty() { return dataInBytes; }
public long getDataInBytes() {
return dataInBytes.get();
}
public void setDataInBytes(long dataInBytes) { this.dataInBytes.set(dataInBytes); }
}

View File

@@ -10,8 +10,8 @@ import javafx.scene.control.Label;
public class ThrougputLabel extends Label {
private static final long kibsThreshold = 1l << 7; // 0.128 kiB/s
private static final long mibsThreshold = 1l << 19; // 0.512 MiB/s
private static final long KIBS_THRESHOLD = 1l << 7; // 0.128 kiB/s
private static final long MIBS_THRESHOLD = 1l << 19; // 0.512 MiB/s
private final StringProperty idleFormat = new SimpleStringProperty("-");
private final StringProperty kibsFormat = new SimpleStringProperty("%.3f");
@@ -22,18 +22,17 @@ public class ThrougputLabel extends Label {
textProperty().bind(createStringBinding());
}
protected StringBinding createStringBinding() {
return Bindings.createStringBinding(this::updateText, kibsFormat, mibsFormat, bytesPerSecond);
}
private String updateText() {
long bps = bytesPerSecond.get();
if (bps > mibsThreshold) {
double mibs = ((double) bytesPerSecond.get()) / 1024.0 / 1024.0;
if (bps > MIBS_THRESHOLD) {
double mibs = ((double) bps) / 1024.0 / 1024.0;
return String.format(mibsFormat.get(), mibs);
} else if (bps > kibsThreshold) {
double kibs = ((double) bytesPerSecond.get()) / 1024.0;
} else if (bps > KIBS_THRESHOLD) {
double kibs = ((double) bps) / 1024.0;
return String.format(kibsFormat.get(), kibs);
} else {
return String.format(idleFormat.get(), bps);

View File

@@ -15,6 +15,7 @@ import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
import javax.inject.Provider;
@@ -26,7 +27,7 @@ import javafx.stage.StageStyle;
import java.util.Map;
import java.util.ResourceBundle;
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, WrongFileAlertComponent.class})
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class})
abstract class MainWindowModule {
@Provides

View File

@@ -1,8 +1,12 @@
package org.cryptomator.ui.mainwindow;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.stats.VaultStatisticsComponent;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
@@ -14,11 +18,19 @@ public class VaultDetailUnlockedController implements FxController {
private final ReadOnlyObjectProperty<Vault> vault;
private final VaultService vaultService;
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
@Inject
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, VaultService vaultService) {
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder) {
this.vault = vault;
this.vaultService = vaultService;
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
this.vaultStatsBuilder = vaultStatsBuilder;
}
private VaultStatisticsComponent buildVaultStats(Vault vault) {
return vaultStatsBuilder.vault(vault).build();
}
@FXML
@@ -32,6 +44,11 @@ public class VaultDetailUnlockedController implements FxController {
// TODO count lock attempts, and allow forced lock
}
@FXML
public void showVaultStatistics() {
vaultStats.getUnchecked(vault.get()).showVaultStatisticsWindow();
}
/* Getter/Setter */
public ReadOnlyObjectProperty<Vault> vaultProperty() {

View File

@@ -6,6 +6,7 @@ import org.cryptomator.common.settings.KeychainBackend;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -24,6 +25,7 @@ import javafx.scene.control.ChoiceBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.util.Arrays;
import java.util.Optional;
@@ -37,6 +39,7 @@ public class GeneralPreferencesController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(GeneralPreferencesController.class);
private final Stage window;
private final Settings settings;
private final boolean trayMenuSupported;
private final Optional<AutoStartStrategy> autoStartStrategy;
@@ -47,6 +50,7 @@ public class GeneralPreferencesController implements FxController {
private final Application application;
private final Environment environment;
private final Set<KeychainAccessProvider> keychainAccessProviders;
private final ErrorComponent.Builder errorComponent;
public ChoiceBox<UiTheme> themeChoiceBox;
public ChoiceBox<KeychainBackend> keychainBackendChoiceBox;
public CheckBox startHiddenCheckbox;
@@ -57,7 +61,8 @@ public class GeneralPreferencesController implements FxController {
public RadioButton nodeOrientationRtl;
@Inject
GeneralPreferencesController(Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional<AutoStartStrategy> autoStartStrategy, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment) {
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, @Named("trayMenuSupported") boolean trayMenuSupported, Optional<AutoStartStrategy> autoStartStrategy, Set<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ExecutorService executor, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
this.window = window;
this.settings = settings;
this.trayMenuSupported = trayMenuSupported;
this.autoStartStrategy = autoStartStrategy;
@@ -68,6 +73,7 @@ public class GeneralPreferencesController implements FxController {
this.resourceBundle = resourceBundle;
this.application = application;
this.environment = environment;
this.errorComponent = errorComponent;
}
@FXML
@@ -129,6 +135,7 @@ public class GeneralPreferencesController implements FxController {
toggleTask.setOnFailed(event -> {
autoStartCheckbox.setSelected(!enableAutoStart); // restore previous state
LOG.error("Failed to toggle autostart.", event.getSource().getException());
errorComponent.cause(event.getSource().getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
});
executor.execute(toggleTask);
});

View File

@@ -4,6 +4,7 @@ import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
@@ -31,16 +32,18 @@ public class RecoveryKeyCreationController implements FxController {
private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty recoveryKeyProperty;
private final ErrorComponent.Builder errorComponent;
public NiceSecurePasswordField passwordField;
@Inject
public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey) {
public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, ErrorComponent.Builder errorComponent) {
this.window = window;
this.successScene = successScene;
this.vault = vault;
this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory;
this.recoveryKeyProperty = recoveryKey;
this.errorComponent = errorComponent;
}
@FXML
@@ -59,6 +62,7 @@ public class RecoveryKeyCreationController implements FxController {
Animations.createShakeWindowAnimation(window).play();
} else {
LOG.error("Creation of recovery key failed.", task.getException());
errorComponent.cause(task.getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
});
executor.submit(task);

View File

@@ -0,0 +1,49 @@
package org.cryptomator.ui.stats;
import dagger.BindsInstance;
import dagger.Lazy;
import dagger.Subcomponent;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* For each vault there can be up to one statistics component.
* <p>
* <b>Important:</b> Outside of {@link org.cryptomator.ui.stats}, this component should be weakly referenced,
* as it include memory-intensive UI nodes.
* <p>
* While the stats window is visible, this component is strongly referenced by the window's main controller.
* As soon as the window is closed, the full objectgraph becomes eligible for GC.
*/
@VaultStatisticsScoped
@Subcomponent(modules = {VaultStatisticsModule.class})
public interface VaultStatisticsComponent {
@VaultStatisticsWindow
Stage window();
@FxmlScene(FxmlFile.VAULT_STATISTICS)
Lazy<Scene> scene();
default void showVaultStatisticsWindow() {
Stage stage = window();
stage.setScene(scene().get());
stage.sizeToScene();
stage.show();
stage.requestFocus();
}
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder vault(@VaultStatisticsWindow Vault vault);
VaultStatisticsComponent build();
}
}

View File

@@ -0,0 +1,210 @@
package org.cryptomator.ui.stats;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultStats;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.WeakBindings;
import javax.inject.Inject;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.binding.LongBinding;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Arrays;
@VaultStatisticsScoped
public class VaultStatisticsController implements FxController {
private static final int IO_SAMPLING_STEPS = 30;
private static final double IO_SAMPLING_INTERVAL = 1;
private final VaultStatisticsComponent component; // keep a strong reference to the component (see component's javadoc)
private final VaultStats stats;
private final Series<Number, Number> readData;
private final Series<Number, Number> writeData;
private final Timeline ioAnimation;
private final LongBinding bpsRead;
private final LongBinding bpsWritten;
private final DoubleBinding cacheHitRate;
private final DoubleBinding cacheHitDegrees;
private final DoubleBinding cacheHitPercentage;
private final LongBinding totalBytesRead;
private final LongBinding totalBytesWritten;
private final LongBinding totalBytesEncrypted;
private final LongBinding totalBytesDecrypted;
private final LongBinding filesRead;
private final LongBinding filesWritten;
private final LongBinding bpsEncrypted;
private final LongBinding bpsDecrypted;
public AreaChart<Number, Number> readChart;
public AreaChart<Number, Number> writeChart;
public NumberAxis readChartXAxis;
public NumberAxis readChartYAxis;
public NumberAxis writeChartXAxis;
public NumberAxis writeChartYAxis;
@Inject
public VaultStatisticsController(VaultStatisticsComponent component, @VaultStatisticsWindow Stage window, @VaultStatisticsWindow Vault vault) {
this.component = component;
this.stats = vault.getStats();
this.readData = new Series<>();
this.writeData = new Series<>();
this.bpsRead = WeakBindings.bindLong(stats.bytesPerSecondReadProperty());
this.bpsWritten = WeakBindings.bindLong(stats.bytesPerSecondWrittenProperty());
this.cacheHitRate = WeakBindings.bindDouble(stats.cacheHitRateProperty());
this.cacheHitDegrees = cacheHitRate.multiply(-270);
this.cacheHitPercentage = cacheHitRate.multiply(100);
this.totalBytesRead = WeakBindings.bindLong(stats.toalBytesReadProperty());
this.totalBytesWritten = WeakBindings.bindLong(stats.toalBytesWrittenProperty());
this.totalBytesDecrypted = WeakBindings.bindLong(stats.totalBytesDecryptedProperty());
this.totalBytesEncrypted = WeakBindings.bindLong(stats.totalBytesEncryptedProperty());
this.filesRead = WeakBindings.bindLong(stats.filesRead());
this.filesWritten = WeakBindings.bindLong(stats.filesWritten());
this.bpsEncrypted = WeakBindings.bindLong(stats.bytesPerSecondEncryptedProperty());
this.bpsDecrypted = WeakBindings.bindLong(stats.bytesPerSecondDecryptedProperty());
this.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();
// make sure to stop animating while window is closed
// otherwise a global timer (GC root) will keep a strong reference to animation
window.setOnHiding(evt -> ioAnimation.stop());
window.setOnShowing(evt -> ioAnimation.play());
}
@FXML
public void initialize() {
readChart.getData().addAll(readData);
writeChart.getData().addAll(writeData);
}
private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> {
private long step = IO_SAMPLING_STEPS;
private final Series<Number, Number> decryptedBytesRead;
private final Series<Number, Number> encryptedBytesWrite;
private final long[] maxBuf = new long[IO_SAMPLING_STEPS];
public IoSamplingAnimationHandler(Series<Number, Number> readData, Series<Number, Number> 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) {
final long currentStep = step++;
final long decBytes = stats.bytesPerSecondReadProperty().get();
final long encBytes = stats.bytesPerSecondWrittenProperty().get();
maxBuf[(int) currentStep % IO_SAMPLING_STEPS] = Math.max(decBytes, encBytes);
long allTimeMax = Arrays.stream(maxBuf).max().orElse(0l);
// remove oldest value:
decryptedBytesRead.getData().remove(0);
encryptedBytesWrite.getData().remove(0);
// add latest value:
decryptedBytesRead.getData().add(new Data<>(currentStep, decBytes));
encryptedBytesWrite.getData().add(new Data<>(currentStep, encBytes));
// adjust ranges:
readChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
readChartXAxis.setUpperBound(currentStep);
readChartYAxis.setUpperBound(allTimeMax);
writeChartXAxis.setLowerBound(currentStep - IO_SAMPLING_STEPS);
writeChartXAxis.setUpperBound(currentStep);
writeChartYAxis.setUpperBound(allTimeMax);
}
}
/* Getter/Setter */
public LongBinding bpsReadProperty() {
return bpsRead;
}
public long getBpsRead() {
return bpsRead.get();
}
public LongBinding bpsWrittenProperty() {
return bpsWritten;
}
public long getBpsWritten() {
return bpsWritten.get();
}
public DoubleBinding cacheHitPercentageProperty() {
return cacheHitPercentage;
}
public double getCacheHitPercentage() { return cacheHitPercentage.get(); }
public DoubleBinding cacheHitDegreesProperty() {
return cacheHitDegrees;
}
public double getCacheHitDegrees() {
return cacheHitDegrees.get();
}
public LongBinding totalBytesReadProperty() { return totalBytesRead;}
public long getTotalBytesRead() { return totalBytesRead.get();}
public LongBinding totalBytesWrittenProperty() { return totalBytesWritten;}
public long getTotalBytesWritten() { return totalBytesWritten.get();}
public LongBinding totalBytesEncryptedProperty() {return totalBytesEncrypted;}
public long getTotalBytesEncrypted() { return totalBytesEncrypted.get();}
public LongBinding totalBytesDecryptedProperty() {return totalBytesDecrypted;}
public long getTotalBytesDecrypted() { return totalBytesDecrypted.get();}
public LongBinding bpsEncryptedProperty() {
return bpsEncrypted;
}
public long getBpsEncrypted() {
return bpsEncrypted.get();
}
public LongBinding bpsDecryptedProperty() {
return bpsDecrypted;
}
public long getBpsDecrypted() {
return bpsDecrypted.get();
}
public LongBinding filesReadProperty() { return filesRead;}
public long getFilesRead() { return filesRead.get();}
public LongBinding filesWrittenProperty() {return filesWritten;}
public long getFilesWritten() {return filesWritten.get();}
}

View File

@@ -0,0 +1,73 @@
package org.cryptomator.ui.stats;
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.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;
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 javax.inject.Provider;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.ResourceBundle;
@Module
abstract class VaultStatisticsModule {
@Provides
@VaultStatisticsWindow
@VaultStatisticsScoped
static FXMLLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
return new FXMLLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@VaultStatisticsWindow
@VaultStatisticsScoped
static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle, @VaultStatisticsWindow Vault vault) {
Stage stage = factory.create();
stage.setTitle(String.format(resourceBundle.getString("stats.title"), vault.getDisplayName()));
stage.setResizable(false);
var weakStage = new WeakReference<>(stage);
vault.stateProperty().addListener(new ChangeListener<>() {
@Override
public void changed(ObservableValue<? extends VaultState> observable, VaultState oldValue, VaultState newValue) {
if (newValue != VaultState.UNLOCKED) {
Stage stage = weakStage.get();
if (stage != null) {
stage.hide();
}
observable.removeListener(this);
}
}
});
stage.setOnCloseRequest(windowEvent -> vault.showingStatsProperty().setValue(false));
return stage;
}
@Provides
@FxmlScene(FxmlFile.VAULT_STATISTICS)
@VaultStatisticsScoped
static Scene provideVaultStatisticsScene(@VaultStatisticsWindow FXMLLoaderFactory fxmlLoaders) {
return fxmlLoaders.createScene("/fxml/stats.fxml");
}
// ------------------
@Binds
@IntoMap
@FxControllerKey(VaultStatisticsController.class)
abstract FxController bindVaultStatisticsController(VaultStatisticsController controller);
}

View File

@@ -0,0 +1,13 @@
package org.cryptomator.ui.stats;
import javax.inject.Scope;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface VaultStatisticsScoped {
}

View File

@@ -0,0 +1,14 @@
package org.cryptomator.ui.stats;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
@interface VaultStatisticsWindow {
}

View File

@@ -209,9 +209,7 @@ public class UnlockWorkflow extends Task<Boolean> {
private void handleGenericError(Throwable e) {
LOG.error("Unlock failed for technical reasons.", e);
Platform.runLater(() -> {
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
});
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
}
private void wipePassword(char[] pw) {

View File

@@ -162,7 +162,9 @@ public class MountOptionsController implements FxController {
@Override
public String toString(String driveLetter) {
if (occupiedDriveLetters.contains(driveLetter)) {
if (Strings.isNullOrEmpty(driveLetter)) {
return "";
} else if (occupiedDriveLetters.contains(driveLetter)) {
return driveLetter + ": (" + resourceBundle.getString("vaultOptions.mount.winDriveLetterOccupied") + ")";
} else {
return driveLetter + ":";

View File

@@ -884,3 +884,57 @@
-fx-background-color: PROGRESS_BAR_BG;
-fx-background-radius: 4px;
}
/*******************************************************************************
* *
* I/O Statistics *
* *
******************************************************************************/
.cache-arc-background {
-fx-fill: transparent;
-fx-stroke: MUTED_BG;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.cache-arc-foreground {
-fx-fill: transparent;
-fx-stroke: PRIMARY;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.chart.io-stats {
-fx-padding: 10px;
-fx-horizontal-grid-lines-visible: false;
-fx-horizontal-zero-line-visible: false;
-fx-vertical-grid-lines-visible: false;
-fx-vertical-zero-line-visible: false;
}
.axis.io-stats {
-fx-tick-mark-visible: false;
-fx-minor-tick-visible: false;
-fx-tick-labels-visible: false;
}
.chart-plot-background {
-fx-background-color: transparent;
}
.chart-vertical-zero-line,
.chart-horizontal-zero-line,
.chart-alternative-row-fill {
-fx-stroke: transparent;
-fx-stroke-width: 0;
}
.default-color0.chart-series-area-line {
-fx-stroke: PRIMARY;
}
.default-color0.chart-series-area-fill {
-fx-fill: linear-gradient(to bottom, PRIMARY, transparent);
-fx-stroke: transparent;
}

View File

@@ -882,3 +882,68 @@
-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;
}
/* content */
.cache-arc-background {
-fx-fill: transparent;
-fx-stroke: MUTED_BG;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.cache-arc-foreground {
-fx-fill: transparent;
-fx-stroke: PRIMARY;
-fx-stroke-type: centered;
-fx-stroke-width: 12;
-fx-stroke-line-cap: butt;
}
.chart.io-stats {
-fx-padding: 10px;
-fx-horizontal-grid-lines-visible: false;
-fx-horizontal-zero-line-visible: false;
-fx-vertical-grid-lines-visible: false;
-fx-vertical-zero-line-visible: false;
}
.axis.io-stats {
-fx-tick-mark-visible: false;
-fx-minor-tick-visible: false;
-fx-tick-labels-visible: false;
}
.chart-plot-background {
-fx-background-color: transparent;
}
.chart-vertical-zero-line,
.chart-horizontal-zero-line,
.chart-alternative-row-fill {
-fx-stroke: transparent;
-fx-stroke-width: 0;
}
.default-color0.chart-series-area-line {
-fx-stroke: PRIMARY;
}
.default-color0.chart-series-area-fill {
-fx-fill: linear-gradient(to bottom, PRIMARY, transparent);
-fx-stroke: transparent;
}

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import org.cryptomator.ui.controls.DataLabel?>
<?import org.cryptomator.ui.controls.FormattedLabel?>
<?import org.cryptomator.ui.controls.ThrougputLabel?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.AreaChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Arc?>
<HBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="org.cryptomator.ui.stats.VaultStatisticsController"
prefWidth="800.0" spacing="12">
<padding>
<Insets topRightBottomLeft="12"/>
</padding>
<!-- Caching -->
<VBox prefWidth="200" prefHeight="200">
<StackPane>
<Group>
<Arc styleClass="cache-arc-background" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="-270"/>
<Arc styleClass="cache-arc-foreground" centerX="100" centerY="100" radiusX="100" radiusY="100" startAngle="225" length="${controller.cacheHitDegrees}"/>
</Group>
<VBox StackPane.alignment="CENTER" alignment="CENTER">
<FormattedLabel styleClass="label-large" format="\%1.0f %%" arg1="${controller.cacheHitPercentage}"/>
<Label text="%stats.cacheHitRate"/>
</VBox>
</StackPane>
</VBox>
<!-- Read -->
<VBox prefWidth="300" prefHeight="300" spacing="6" alignment="CENTER">
<ThrougputLabel styleClass="label-large" idleFormat="%stats.read.throughput.idle" kibsFormat="%stats.read.throughput.kibs" mibsFormat="%stats.read.throughput.mibs" bytesPerSecond="${controller.bpsRead}"/>
<AreaChart fx:id="readChart" styleClass="io-stats" createSymbols="false" animated="false">
<xAxis>
<NumberAxis fx:id="readChartXAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="false" side="BOTTOM"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="readChartYAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="true" side="LEFT" tickUnit="Infinity"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</AreaChart>
<DataLabel byteFormat="%stats.read.total.data.none" kibFormat="%stats.read.total.data.kib" mibFormat="%stats.read.total.data.mib" gibFormat="%stats.read.total.data.gib" dataInBytes="${controller.totalBytesRead}"/>
<DataLabel byteFormat="%stats.decr.total.data.none" kibFormat="%stats.decr.total.data.kib" mibFormat="%stats.decr.total.data.mib" gibFormat="%stats.decr.total.data.gib" dataInBytes="${controller.totalBytesDecrypted}"/>
<FormattedLabel format="%stats.read.accessCount" arg1="${controller.filesRead}"/>
</VBox>
<!-- Write -->
<VBox prefWidth="300" prefHeight="300" spacing="6" alignment="CENTER">
<ThrougputLabel styleClass="label-large" idleFormat="%stats.write.throughput.idle" kibsFormat="%stats.write.throughput.kibs" mibsFormat="%stats.write.throughput.mibs" bytesPerSecond="${controller.bpsWritten}"/>
<AreaChart fx:id="writeChart" styleClass="io-stats" createSymbols="false" animated="false">
<xAxis>
<NumberAxis fx:id="writeChartXAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="false" side="BOTTOM"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="writeChartYAxis" styleClass="io-stats" autoRanging="false" forceZeroInRange="true" side="LEFT" tickUnit="Infinity"/>
</yAxis>
<cursor>
<Cursor fx:constant="DEFAULT"/>
</cursor>
</AreaChart>
<DataLabel byteFormat="%stats.write.total.data.none" kibFormat="%stats.write.total.data.kib" mibFormat="%stats.write.total.data.mib" gibFormat="%stats.write.total.data.gib" dataInBytes="${controller.totalBytesWritten}"/>
<DataLabel byteFormat="%stats.encr.total.data.none" kibFormat="%stats.encr.total.data.kib" mibFormat="%stats.encr.total.data.mib" gibFormat="%stats.encr.total.data.gib" dataInBytes="${controller.totalBytesEncrypted}"/>
<FormattedLabel format="%stats.write.accessCount" arg1="${controller.filesWritten}"/>
</VBox>
</HBox>

View File

@@ -35,14 +35,20 @@
<Region VBox.vgrow="ALWAYS"/>
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondRead"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondRead}"/>
</HBox>
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondWritten"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps"
mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondWritten}"/>
<HBox alignment="BOTTOM_RIGHT">
<Button text="%main.vaultDetail.stats" minWidth="120" onAction="#showVaultStatistics" contentDisplay="BOTTOM">
<graphic>
<VBox spacing="6">
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondRead"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps" mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondRead}"/>
</HBox>
<HBox alignment="CENTER_RIGHT" spacing="6">
<Label styleClass="label-small,label-muted" text="%main.vaultDetail.bytesPerSecondWritten"/>
<ThrougputLabel styleClass="label-small,label-muted" alignment="CENTER_RIGHT" minWidth="60" idleFormat="%main.vaultDetail.throughput.idle" kibsFormat="%main.vaultDetail.throughput.kbps" mibsFormat="%main.vaultDetail.throughput.mbps" bytesPerSecond="${controller.vault.stats.bytesPerSecondWritten}"/>
</HBox>
</VBox>
</graphic>
</Button>
</HBox>
</VBox>

View File

@@ -172,6 +172,36 @@ preferences.donationKey.getDonationKey=Get a donation key
## About
preferences.about=About
# Vault Statistics
stats.title=Statistics for %s
stats.cacheHitRate=Cache Hit Rate
## Read
stats.read.throughput.idle=Read: idle
stats.read.throughput.kibs=Read: %.2f kiB/s
stats.read.throughput.mibs=Read: %.2f MiB/s
stats.read.total.data.none=Data read: -
stats.read.total.data.kib=Data read: %.1f kiB
stats.read.total.data.mib=Data read: %.1f MiB
stats.read.total.data.gib=Data read: %.1f GiB
stats.decr.total.data.none=Data decrypted: -
stats.decr.total.data.kib=Data decrypted: %.1f kiB
stats.decr.total.data.mib=Data decrypted: %.1f MiB
stats.decr.total.data.gib=Data decrypted: %.1f GiB
stats.read.accessCount=Total reads: %d
## Write
stats.write.throughput.idle=Write: idle
stats.write.throughput.kibs=Write: %.2f kiB/s
stats.write.throughput.mibs=Write: %.2f MiB/s
stats.write.total.data.none=Data read: -
stats.write.total.data.kib=Data written: %.1f kiB
stats.write.total.data.mib=Data written: %.1f MiB
stats.write.total.data.gib=Data written: %.1f GiB
stats.encr.total.data.none=Data encrypted: -
stats.encr.total.data.kib=Data encrypted: %.1f kiB
stats.encr.total.data.mib=Data encrypted: %.1f MiB
stats.encr.total.data.gib=Data encrypted: %.1f GiB
stats.write.accessCount=Total writes: %d
# Main Window
main.closeBtn.tooltip=Close
main.minimizeBtn.tooltip=Minimize
@@ -199,11 +229,12 @@ main.vaultDetail.unlockedStatus=UNLOCKED
main.vaultDetail.accessLocation=Your vault's contents are accessible here:
main.vaultDetail.revealBtn=Reveal Drive
main.vaultDetail.lockBtn=Lock
main.vaultDetail.bytesPerSecondRead=read:
main.vaultDetail.bytesPerSecondWritten=written:
main.vaultDetail.bytesPerSecondRead=Read:
main.vaultDetail.bytesPerSecondWritten=Write:
main.vaultDetail.throughput.idle=idle
main.vaultDetail.throughput.kbps=%.1f kiB/s
main.vaultDetail.throughput.mbps=%.1f MiB/s
main.vaultDetail.stats=Vault Statistics
### Missing
main.vaultDetail.missing.info=Cryptomator could not find a vault at this path.
main.vaultDetail.missing.recheck=Recheck

View File

@@ -19,7 +19,7 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - http://github.com/jnr/jnr-ffi)
- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - http://findbugs.sourceforge.net/)
- Gson (com.google.code.gson:gson:2.8.6 - https://github.com/google/gson/gson)
- Dagger (com.google.dagger:dagger:2.22 - https://github.com/google/dagger)
- Dagger (com.google.dagger:dagger:2.29.1 - https://github.com/google/dagger)
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotations)
- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - https://github.com/google/guava/failureaccess)
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - https://github.com/google/guava/guava)
@@ -28,19 +28,19 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- Apache Commons CLI (commons-cli:commons-cli:1.4 - http://commons.apache.org/proper/commons-cli/)
- Apache Commons IO (commons-io:commons-io:2.6 - http://commons.apache.org/proper/commons-io/)
- javax.inject (javax.inject:javax.inject:1 - http://code.google.com/p/atinject/)
- Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna)
- Java Native Access (net.java.dev.jna:jna:5.5.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - https://github.com/java-native-access/jna)
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - https://commons.apache.org/proper/commons-lang/)
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - http://hc.apache.org/httpcomponents-core-ga)
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - http://jackrabbit.apache.org/jackrabbit-webdav/)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-xml)
BSD:
- asm (org.ow2.asm:asm:7.1 - http://asm.ow2.org/)
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - http://asm.ow2.org/)
@@ -48,30 +48,30 @@ Cryptomator uses 46 third-party dependencies under the following licenses:
- asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/)
- asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/)
Eclipse Public License - Version 1.0:
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.31.v20200723 - http://www.eclipse.org/jetty)
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-http)
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-io)
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-security)
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-server)
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-servlet)
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-util)
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-webapp)
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.33.v20201020 - https://eclipse.org/jetty/jetty-xml)
Eclipse Public License - v 2.0:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
GPLv2:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
GPLv2+CE:
- Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - http://servlet-spec.java.net)
- javafx-base (org.openjfx:javafx-base:14 - https://openjdk.java.net/projects/openjfx/javafx-base/)
- javafx-controls (org.openjfx:javafx-controls:14 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
- javafx-fxml (org.openjfx:javafx-fxml:14 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
- javafx-graphics (org.openjfx:javafx-graphics:14 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
- javafx-base (org.openjfx:javafx-base:15 - https://openjdk.java.net/projects/openjfx/javafx-base/)
- javafx-controls (org.openjfx:javafx-controls:15 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
- javafx-fxml (org.openjfx:javafx-fxml:15 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
- javafx-graphics (org.openjfx:javafx-graphics:15 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
LGPL 2.1:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
- Java Native Access (net.java.dev.jna:jna:5.1.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.1.0 - https://github.com/java-native-access/jna)
- Java Native Access (net.java.dev.jna:jna:5.5.0 - https://github.com/java-native-access/jna)
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - https://github.com/java-native-access/jna)
MIT License:
- java jwt (com.auth0:java-jwt:3.10.3 - https://github.com/auth0/java-jwt)
- java jwt (com.auth0:java-jwt:3.11.0 - https://github.com/auth0/java-jwt)
- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - http://github.com/jnr/jnr-x86asm)
- jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined)
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - https://github.com/nulab/zxcvbn4j)