UI code cleanup

This commit is contained in:
Sebastian Stenzel
2016-03-03 14:05:50 +01:00
parent e3fd25aa41
commit f05440fe7a
17 changed files with 205 additions and 184 deletions

View File

@@ -19,8 +19,6 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import javafx.application.Application;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.util.SingleInstanceManager;
import org.cryptomator.ui.util.SingleInstanceManager.RemoteInstance;
@@ -28,11 +26,12 @@ import org.eclipse.jetty.util.ConcurrentHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Application;
public class Cryptomator {
public static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
public static final CompletableFuture<Consumer<File>> OPEN_FILE_HANDLER = new CompletableFuture<>();
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private static final Set<Runnable> SHUTDOWN_TASKS = new ConcurrentHashSet<>();
private static final CleanShutdownPerformer CLEAN_SHUTDOWN_PERFORMER = new CleanShutdownPerformer();

View File

@@ -13,7 +13,9 @@ import java.util.concurrent.ExecutorService;
import javax.inject.Singleton;
import org.cryptomator.ui.controllers.MainController;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.TrayIconUtil;
import dagger.Component;
@@ -25,4 +27,8 @@ interface CryptomatorComponent {
DeferredCloser deferredCloser();
MainController mainController();
Localization localization();
TrayIconUtil trayIconUtil();
}

View File

@@ -24,7 +24,6 @@ import org.cryptomator.ui.model.VaultObjectMapperProvider;
import org.cryptomator.ui.settings.Settings;
import org.cryptomator.ui.settings.SettingsProvider;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.DeferredCloser.Closer;
import org.cryptomator.ui.util.SemVerComparator;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -32,16 +31,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import dagger.Module;
import dagger.Provides;
import javafx.application.Application;
import javafx.stage.Stage;
@Module(includes = CryptoEngineModule.class)
class CryptomatorModule {
private final Application application;
private final DeferredCloser deferredCloser;
private final Stage mainWindow;
public CryptomatorModule(Application application) {
public CryptomatorModule(Application application, Stage mainWindow) {
this.application = application;
this.deferredCloser = new DeferredCloser();
this.mainWindow = mainWindow;
}
@Provides
@@ -50,10 +50,19 @@ class CryptomatorModule {
return application;
}
@Provides
@Singleton
@Named("mainWindow")
Stage provideMainWindow() {
return mainWindow;
}
@Provides
@Singleton
DeferredCloser provideDeferredCloser() {
return deferredCloser;
DeferredCloser closer = new DeferredCloser();
Cryptomator.addShutdownTask(closer::close);
return closer;
}
@Provides
@@ -78,8 +87,8 @@ class CryptomatorModule {
@Provides
@Singleton
ExecutorService provideExecutorService() {
return closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown);
ExecutorService provideExecutorService(DeferredCloser closer) {
return closer.closeLater(Executors.newCachedThreadPool(), ExecutorService::shutdown).get().orElseThrow(IllegalStateException::new);
}
@Provides
@@ -90,14 +99,10 @@ class CryptomatorModule {
@Provides
@Singleton
FrontendFactory provideFrontendFactory(WebDavServer webDavServer, Settings settings) {
FrontendFactory provideFrontendFactory(DeferredCloser closer, WebDavServer webDavServer, Settings settings) {
webDavServer.setPort(settings.getPort());
webDavServer.start();
return closeLater(webDavServer, WebDavServer::stop);
}
private <T> T closeLater(T object, Closer<T> closer) {
return deferredCloser.closeLater(object, closer).get().get();
return closer.closeLater(webDavServer, WebDavServer::stop).get().orElseThrow(IllegalStateException::new);
}
}

View File

@@ -12,8 +12,6 @@ import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.controllers.MainController;
@@ -21,7 +19,6 @@ import org.cryptomator.ui.util.ActiveWindowStyleSupport;
import org.cryptomator.ui.util.DeferredCloser;
import org.cryptomator.ui.util.SingleInstanceManager;
import org.cryptomator.ui.util.SingleInstanceManager.LocalInstance;
import org.cryptomator.ui.util.TrayIconUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,24 +32,16 @@ import javafx.stage.Stage;
public class MainApplication extends Application {
public static final String APPLICATION_KEY = "CryptomatorGUI";
private static final Logger LOG = LoggerFactory.getLogger(MainApplication.class);
private final ExecutorService executorService;
private final DeferredCloser closer;
private final MainController mainCtrl;
public MainApplication() {
final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this)).build();
this.executorService = comp.executorService();
this.closer = comp.deferredCloser();
this.mainCtrl = comp.mainController();
Cryptomator.addShutdownTask(closer::close);
}
private DeferredCloser closer;
@Override
public void start(final Stage primaryStage) throws IOException {
Font.loadFont(getClass().getResourceAsStream("/css/ionicons.ttf"), 12.0);
public void start(Stage primaryStage) throws IOException {
final CryptomatorComponent comp = DaggerCryptomatorComponent.builder().cryptomatorModule(new CryptomatorModule(this, primaryStage)).build();
final MainController mainCtrl = comp.mainController();
closer = comp.deferredCloser();
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
FXMLLoader.setDefaultClassLoader(contextClassLoader);
Platform.runLater(() -> {
@@ -65,36 +54,35 @@ public class MainApplication extends Application {
}
});
// Set stylesheets and initialize stage:
Font.loadFont(getClass().getResourceAsStream("/css/ionicons.ttf"), 12.0);
chooseNativeStylesheet();
mainCtrl.initStage(primaryStage);
final ResourceBundle rb = ResourceBundle.getBundle("localization");
primaryStage.titleProperty().bind(mainCtrl.windowTitle());
primaryStage.setResizable(false);
if (SystemUtils.IS_OS_WINDOWS) {
primaryStage.getIcons().add(new Image(MainApplication.class.getResourceAsStream("/window_icon.png")));
}
// show window and start observing its focus:
primaryStage.show();
ActiveWindowStyleSupport.startObservingFocus(primaryStage);
TrayIconUtil.init(primaryStage, rb, () -> {
quit();
});
comp.trayIconUtil().initTrayIcon(this::quit);
// open files, if requested during startup:
for (String arg : getParameters().getUnnamed()) {
handleCommandLineArg(arg);
handleCommandLineArg(arg, primaryStage, mainCtrl);
}
if (SystemUtils.IS_OS_MAC_OSX) {
Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(file.getAbsolutePath()));
Cryptomator.OPEN_FILE_HANDLER.complete(file -> handleCommandLineArg(file.getAbsolutePath(), primaryStage, mainCtrl));
}
LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, executorService), LocalInstance::close).get().get();
cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(arg));
// register this application instance as primary application, that other instances can send open file requests to:
LocalInstance cryptomatorGuiInstance = closer.closeLater(SingleInstanceManager.startLocalInstance(APPLICATION_KEY, comp.executorService()), LocalInstance::close).get().get();
cryptomatorGuiInstance.registerListener(arg -> handleCommandLineArg(arg, primaryStage, mainCtrl));
}
private void handleCommandLineArg(String arg) {
private void handleCommandLineArg(String arg, Stage primaryStage, MainController mainCtrl) {
// find correct location:
final Path path = FileSystems.getDefault().getPath(arg);
final Path vaultPath;
@@ -110,7 +98,10 @@ public class MainApplication extends Application {
// add vault to ctrl:
Platform.runLater(() -> {
mainCtrl.addVault(vaultPath, true);
mainCtrl.toFront();
primaryStage.setIconified(false);
primaryStage.show();
primaryStage.toFront();
primaryStage.requestFocus();
});
}

View File

@@ -28,16 +28,6 @@ abstract class AbstractFXMLViewController implements Initializable {
private final AtomicReference<Parent> fxmlRoot = new AtomicReference<>();
/**
* URL from #initialize(URL, ResourceBundle)
*/
protected URL rootUrl;
/**
* ResourceBundle from #initialize(URL, ResourceBundle)
*/
protected ResourceBundle resourceBundle;
/**
* Gets the URL to the FXML file describing the view presented by this controller.<br/>
*
@@ -57,8 +47,6 @@ abstract class AbstractFXMLViewController implements Initializable {
@Override
public final void initialize(URL location, ResourceBundle resources) {
this.rootUrl = location;
this.resourceBundle = resources;
this.initialize();
}

View File

@@ -21,6 +21,7 @@ import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.crypto.engine.UnsupportedVaultFormatException;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,12 +42,14 @@ public class ChangePasswordController extends AbstractFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class);
private final Application app;
private final Localization localization;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<ChangePasswordListener> listener = Optional.empty();
@Inject
public ChangePasswordController(Application app) {
public ChangePasswordController(Application app, Localization localization) {
this.app = app;
this.localization = localization;
}
@FXML
@@ -82,7 +85,7 @@ public class ChangePasswordController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
// ****************************************
@@ -103,21 +106,21 @@ public class ChangePasswordController extends AbstractFXMLViewController {
downloadsPageLink.setVisible(false);
try {
vault.get().changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters());
messageText.setText(resourceBundle.getString("changePassword.infoMessage.success"));
messageText.setText(localization.getString("changePassword.infoMessage.success"));
listener.ifPresent(this::invokeListenerLater);
} catch (InvalidPassphraseException e) {
messageText.setText(resourceBundle.getString("changePassword.errorMessage.wrongPassword"));
messageText.setText(localization.getString("changePassword.errorMessage.wrongPassword"));
Platform.runLater(oldPasswordField::requestFocus);
} catch (UncheckedIOException | IOException ex) {
messageText.setText(resourceBundle.getString("changePassword.errorMessage.decryptionFailed"));
messageText.setText(localization.getString("changePassword.errorMessage.decryptionFailed"));
LOG.error("Decryption failed for technical reasons.", ex);
} catch (UnsupportedVaultFormatException e) {
downloadsPageLink.setVisible(true);
LOG.warn("Unable to unlock vault: " + e.getMessage());
if (e.isVaultOlderThanSoftware()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
} else if (e.isSoftwareOlderThanVault()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
}
} finally {
oldPasswordField.swipe();

View File

@@ -20,6 +20,7 @@ import javax.inject.Singleton;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,11 +38,13 @@ public class InitializeController extends AbstractFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class);
private final Localization localization;
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
private Optional<InitializationListener> listener = Optional.empty();
@Inject
public InitializeController() {
public InitializeController(Localization localization) {
this.localization = localization;
}
@FXML
@@ -70,7 +73,7 @@ public class InitializeController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
// ****************************************
@@ -84,10 +87,10 @@ public class InitializeController extends AbstractFXMLViewController {
vault.get().create(passphrase);
listener.ifPresent(this::invokeListenerLater);
} catch (FileAlreadyExistsException ex) {
messageLabel.setText(resourceBundle.getString("initialize.messageLabel.alreadyInitialized"));
messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized"));
} catch (UncheckedIOException | IOException ex) {
LOG.error("I/O Exception", ex);
messageLabel.setText(resourceBundle.getString("initialize.messageLabel.initializationFailed"));
messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed"));
} finally {
passwordField.swipe();
retypePasswordField.swipe();

View File

@@ -15,6 +15,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import javafx.application.Application;
import javafx.beans.Observable;
@@ -41,6 +42,7 @@ import javafx.util.StringConverter;
public class MacWarningsController extends AbstractFXMLViewController {
private final Application application;
private final Localization localization;
private final ObservableList<Warning> warnings = FXCollections.observableArrayList();
private final ListChangeListener<String> unauthenticatedResourcesChangeListener = this::unauthenticatedResourcesDidChange;
private final ChangeListener<Boolean> stageVisibilityChangeListener = this::windowVisibilityDidChange;
@@ -48,8 +50,9 @@ public class MacWarningsController extends AbstractFXMLViewController {
private Stage stage;
@Inject
public MacWarningsController(Application application) {
public MacWarningsController(Application application, Localization localization) {
this.application = application;
this.localization = localization;
}
@FXML
@@ -84,7 +87,7 @@ public class MacWarningsController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
@Override
@@ -127,7 +130,7 @@ public class MacWarningsController extends AbstractFXMLViewController {
private void windowVisibilityDidChange(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (Boolean.TRUE.equals(newValue)) {
stage.setTitle(String.format(resourceBundle.getString("macWarnings.windowTitle"), vault.get().getName()));
stage.setTitle(String.format(localization.getString("macWarnings.windowTitle"), vault.get().getName()));
warnings.addAll(vault.get().getNamesOfResourcesWithInvalidMac().stream().map(Warning::new).collect(Collectors.toList()));
vault.get().getNamesOfResourcesWithInvalidMac().addListener(this.unauthenticatedResourcesChangeListener);
} else {

View File

@@ -19,12 +19,14 @@ import java.util.Map;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.cryptomator.ui.controls.DirectoryListCell;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.model.VaultFactory;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.monadic.MonadicBinding;
@@ -58,6 +60,8 @@ public class MainController extends AbstractFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
private final Stage mainWindow;
private final Localization localization;
private final VaultFactory vaultFactoy;
private final Lazy<WelcomeController> welcomeController;
private final Lazy<InitializeController> initializeController;
@@ -74,8 +78,11 @@ public class MainController extends AbstractFXMLViewController {
private final Map<Vault, UnlockedController> unlockedVaults = new HashMap<>();
@Inject
public MainController(Settings settings, VaultFactory vaultFactoy, Lazy<WelcomeController> welcomeController, Lazy<InitializeController> initializeController, Lazy<UnlockController> unlockController,
Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController, Lazy<SettingsController> settingsController) {
public MainController(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, VaultFactory vaultFactoy, Lazy<WelcomeController> welcomeController,
Lazy<InitializeController> initializeController, Lazy<UnlockController> unlockController, Provider<UnlockedController> unlockedControllerProvider, Lazy<ChangePasswordController> changePasswordController,
Lazy<SettingsController> settingsController) {
this.mainWindow = mainWindow;
this.localization = localization;
this.vaultFactoy = vaultFactoy;
this.welcomeController = welcomeController;
this.initializeController = initializeController;
@@ -89,8 +96,6 @@ public class MainController extends AbstractFXMLViewController {
this.isShowingSettings = activeController.isEqualTo(settingsController.get());
}
private Stage stage;
@FXML
private ContextMenu vaultListCellContextMenu;
@@ -137,7 +142,7 @@ public class MainController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
private ListCell<Vault> createDirecoryListCell(ListView<Vault> param) {
@@ -146,12 +151,6 @@ public class MainController extends AbstractFXMLViewController {
return cell;
}
@Override
public void initStage(Stage stage) {
super.initStage(stage);
this.stage = stage;
}
// ****************************************
// UI Events
// ****************************************
@@ -169,7 +168,7 @@ public class MainController extends AbstractFXMLViewController {
private void didClickCreateNewVault(ActionEvent event) {
final FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION));
final File file = fileChooser.showSaveDialog(stage);
final File file = fileChooser.showSaveDialog(mainWindow);
if (file == null) {
return;
}
@@ -194,7 +193,7 @@ public class MainController extends AbstractFXMLViewController {
private void didClickAddExistingVaults(ActionEvent event) {
final FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator vault", "*" + Vault.VAULT_FILE_EXTENSION));
final List<File> files = fileChooser.showOpenMultipleDialog(stage);
final List<File> files = fileChooser.showOpenMultipleDialog(mainWindow);
if (files != null) {
for (final File file : files) {
addVault(file.toPath(), false);
@@ -288,7 +287,7 @@ public class MainController extends AbstractFXMLViewController {
// ****************************************
public Binding<String> windowTitle() {
return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(resourceBundle.getString("app.name"));
return EasyBind.monadic(selectedVault).map(Vault::getName).orElse(localization.getString("app.name"));
}
// ****************************************
@@ -340,13 +339,4 @@ public class MainController extends AbstractFXMLViewController {
showUnlockView();
}
/**
* Attempts to make the application window visible.
*/
public void toFront() {
stage.setIconified(false);
stage.show();
stage.toFront();
}
}

View File

@@ -15,6 +15,7 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.CharUtils;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.fxmisc.easybind.EasyBind;
@@ -26,10 +27,12 @@ import javafx.scene.input.KeyEvent;
@Singleton
public class SettingsController extends AbstractFXMLViewController {
private final Localization localization;
private final Settings settings;
@Inject
public SettingsController(Settings settings) {
public SettingsController(Localization localization, Settings settings) {
this.localization = localization;
this.settings = settings;
}
@@ -57,7 +60,7 @@ public class SettingsController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
private void portDidChange(String newValue) {

View File

@@ -25,6 +25,7 @@ import org.cryptomator.frontend.FrontendFactory;
import org.cryptomator.frontend.webdav.mount.WindowsDriveLetters;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.fxmisc.easybind.EasyBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +55,7 @@ public class UnlockController extends AbstractFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class);
private final Application app;
private final Localization localization;
private final ExecutorService exec;
private final Lazy<FrontendFactory> frontendFactory;
private final WindowsDriveLetters driveLetters;
@@ -61,8 +63,9 @@ public class UnlockController extends AbstractFXMLViewController {
final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
@Inject
public UnlockController(Application app, ExecutorService exec, Lazy<FrontendFactory> frontendFactory, WindowsDriveLetters driveLetters) {
public UnlockController(Application app, Localization localization, ExecutorService exec, Lazy<FrontendFactory> frontendFactory, WindowsDriveLetters driveLetters) {
this.app = app;
this.localization = localization;
this.exec = exec;
this.frontendFactory = frontendFactory;
this.driveLetters = driveLetters;
@@ -123,7 +126,7 @@ public class UnlockController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
private void vaultChanged(Vault newVault) {
@@ -132,7 +135,7 @@ public class UnlockController extends AbstractFXMLViewController {
}
passwordField.clear();
advancedOptions.setVisible(false);
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show"));
advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show"));
progressIndicator.setVisible(false);
if (SystemUtils.IS_OS_WINDOWS) {
winDriveLetter.valueProperty().removeListener(driveLetterChangeListener);
@@ -167,9 +170,9 @@ public class UnlockController extends AbstractFXMLViewController {
private void didClickAdvancedOptionsButton(ActionEvent event) {
advancedOptions.setVisible(!advancedOptions.isVisible());
if (advancedOptions.isVisible()) {
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.hide"));
advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.hide"));
} else {
advancedOptionsButton.setText(resourceBundle.getString("unlock.button.advancedOptions.show"));
advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show"));
}
}
@@ -203,7 +206,7 @@ public class UnlockController extends AbstractFXMLViewController {
@Override
public String toString(Character letter) {
if (letter == null) {
return resourceBundle.getString("unlock.choicebox.winDriveLetter.auto");
return localization.getString("unlock.choicebox.winDriveLetter.auto");
} else {
return Character.toString(letter) + ":";
}
@@ -211,7 +214,7 @@ public class UnlockController extends AbstractFXMLViewController {
@Override
public Character fromString(String string) {
if (resourceBundle.getString("unlock.choicebox.winDriveLetter.auto").equals(string)) {
if (localization.getString("unlock.choicebox.winDriveLetter.auto").equals(string)) {
return null;
} else {
return CharUtils.toCharacterObject(string);
@@ -280,7 +283,7 @@ public class UnlockController extends AbstractFXMLViewController {
vault.get().reveal();
} catch (InvalidPassphraseException e) {
Platform.runLater(() -> {
messageText.setText(resourceBundle.getString("unlock.errorMessage.wrongPassword"));
messageText.setText(localization.getString("unlock.errorMessage.wrongPassword"));
passwordField.requestFocus();
});
} catch (UnsupportedVaultFormatException e) {
@@ -288,15 +291,15 @@ public class UnlockController extends AbstractFXMLViewController {
Platform.runLater(() -> {
downloadsPageLink.setVisible(true);
if (e.isVaultOlderThanSoftware()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
} else if (e.isSoftwareOlderThanVault()) {
messageText.setText(resourceBundle.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
}
});
} catch (FrontendCreationFailedException | CommandFailedException e) {
LOG.error("Decryption failed for technical reasons.", e);
Platform.runLater(() -> {
messageText.setText(resourceBundle.getString("unlock.errorMessage.mountingFailed"));
messageText.setText(localization.getString("unlock.errorMessage.mountingFailed"));
});
} finally {
Platform.runLater(() -> {

View File

@@ -18,6 +18,7 @@ import javax.inject.Provider;
import org.cryptomator.frontend.CommandFailedException;
import org.cryptomator.ui.model.Vault;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.util.ActiveWindowStyleSupport;
import org.fxmisc.easybind.EasyBind;
@@ -50,6 +51,7 @@ public class UnlockedController extends AbstractFXMLViewController {
private static final int IO_SAMPLING_STEPS = 100;
private static final double IO_SAMPLING_INTERVAL = 0.25;
private final Localization localization;
private final Stage macWarningsWindow = new Stage();
private final MacWarningsController macWarningsController;
private final ExecutorService exec;
@@ -58,7 +60,8 @@ public class UnlockedController extends AbstractFXMLViewController {
private Timeline ioAnimation;
@Inject
public UnlockedController(Provider<MacWarningsController> macWarningsControllerProvider, ExecutorService exec) {
public UnlockedController(Localization localization, Provider<MacWarningsController> macWarningsControllerProvider, ExecutorService exec) {
this.localization = localization;
this.macWarningsController = macWarningsControllerProvider.get();
this.exec = exec;
@@ -96,7 +99,7 @@ public class UnlockedController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
private void vaultChanged(Vault newVault) {
@@ -125,7 +128,7 @@ public class UnlockedController extends AbstractFXMLViewController {
vault.get().unmount();
} catch (CommandFailedException e) {
Platform.runLater(() -> {
messageLabel.setText(resourceBundle.getString("unlocked.label.unmountFailed"));
messageLabel.setText(localization.getString("unlocked.label.unmountFailed"));
});
return;
}
@@ -151,7 +154,7 @@ public class UnlockedController extends AbstractFXMLViewController {
vault.get().reveal();
} catch (CommandFailedException e) {
Platform.runLater(() -> {
messageLabel.setText(resourceBundle.getString("unlocked.label.revealFailed"));
messageLabel.setText(localization.getString("unlocked.label.revealFailed"));
});
}
});

View File

@@ -30,6 +30,7 @@ import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.settings.Localization;
import org.cryptomator.ui.settings.Settings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,13 +55,15 @@ public class WelcomeController extends AbstractFXMLViewController {
private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class);
private final Application app;
private final Localization localization;
private final Settings settings;
private final Comparator<String> semVerComparator;
private final ExecutorService executor;
@Inject
public WelcomeController(Application app, Settings settings, @Named("SemVer") Comparator<String> semVerComparator, ExecutorService executor) {
public WelcomeController(Application app, Localization localization, Settings settings, @Named("SemVer") Comparator<String> semVerComparator, ExecutorService executor) {
this.app = app;
this.localization = localization;
this.settings = settings;
this.semVerComparator = semVerComparator;
this.executor = executor;
@@ -99,7 +102,7 @@ public class WelcomeController extends AbstractFXMLViewController {
@Override
protected ResourceBundle getFxmlResourceBundle() {
return ResourceBundle.getBundle("localization");
return localization;
}
// ****************************************
@@ -115,7 +118,7 @@ public class WelcomeController extends AbstractFXMLViewController {
return;
}
Platform.runLater(() -> {
checkForUpdatesStatus.setText(resourceBundle.getString("welcome.checkForUpdates.label.currentlyChecking"));
checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking"));
checkForUpdatesIndicator.setVisible(true);
});
final HttpClient client = new HttpClient();
@@ -162,7 +165,7 @@ public class WelcomeController extends AbstractFXMLViewController {
final String currentVersion = applicationVersion().orElse(null);
LOG.debug("Current version: {}, lastest version: {}", currentVersion, latestVersion);
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
final String msg = String.format(resourceBundle.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
Platform.runLater(() -> {
this.updateLink.setText(msg);
this.updateLink.setVisible(true);

View File

@@ -0,0 +1,27 @@
package org.cryptomator.ui.settings;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class Localization extends ResourceBundle {
@Inject
public Localization() {
this.parent = ResourceBundle.getBundle("localization");
}
@Override
protected Object handleGetObject(String key) {
return parent.getObject(key);
}
@Override
public Enumeration<String> getKeys() {
return parent.getKeys();
}
}

View File

@@ -17,13 +17,14 @@ import org.cryptomator.ui.model.Vault;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
@JsonPropertyOrder(value = {"directories", "checkForUpdatesEnabled", "port"})
@JsonPropertyOrder(value = {"directories", "checkForUpdatesEnabled", "port", "numTrayNotifications"})
public class Settings implements Serializable {
private static final long serialVersionUID = 7609959894417878744L;
public static final int MIN_PORT = 1024;
public static final int MAX_PORT = 65535;
public static final int DEFAULT_PORT = 0;
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
@JsonProperty("directories")
private List<Vault> directories;
@@ -34,6 +35,9 @@ public class Settings implements Serializable {
@JsonProperty("port")
private Integer port;
@JsonProperty("numTrayNotifications")
private Integer numTrayNotifications;
/**
* Package-private constructor; use {@link SettingsProvider}.
*/
@@ -82,4 +86,12 @@ public class Settings implements Serializable {
return port == DEFAULT_PORT || port >= MIN_PORT && port <= MAX_PORT;
}
public Integer getNumTrayNotifications() {
return numTrayNotifications == null ? DEFAULT_NUM_TRAY_NOTIFICATIONS : numTrayNotifications;
}
public void setNumTrayNotifications(Integer numTrayNotifications) {
this.numTrayNotifications = numTrayNotifications;
}
}

View File

@@ -16,7 +16,7 @@ import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.cryptomator.ui.controllers.MainController;
import org.cryptomator.common.ConsumerThrowingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,23 +41,8 @@ import com.google.common.annotations.VisibleForTesting;
* @author Tillmann Gaida
*/
public class DeferredCloser implements AutoCloseable {
public static interface Closer<T> {
void close(T object) throws Exception;
}
static class EmptyResource<T> implements DeferredClosable<T> {
@Override
public Optional<T> get() {
return Optional.empty();
}
@Override
public void close() {
}
}
private static final Logger LOG = LoggerFactory.getLogger(MainController.class);
private static final Logger LOG = LoggerFactory.getLogger(DeferredCloser.class);
@VisibleForTesting
final Map<Long, ManagedResource<?>> cleanups = new ConcurrentSkipListMap<>();
@@ -65,13 +50,13 @@ public class DeferredCloser implements AutoCloseable {
@VisibleForTesting
final AtomicLong counter = new AtomicLong();
public class ManagedResource<T> implements DeferredClosable<T> {
private class ManagedResource<T> implements DeferredClosable<T> {
private final long number = counter.incrementAndGet();
private final AtomicReference<T> object = new AtomicReference<>();
private final Closer<T> closer;
private final ConsumerThrowingException<T, Exception> closer;
ManagedResource(T object, Closer<T> closer) {
public ManagedResource(T object, ConsumerThrowingException<T, Exception> closer) {
super();
this.object.set(object);
this.closer = closer;
@@ -82,11 +67,10 @@ public class DeferredCloser implements AutoCloseable {
final T oldObject = object.getAndSet(null);
if (oldObject != null) {
cleanups.remove(number);
try {
closer.close(oldObject);
closer.accept(oldObject);
} catch (Exception e) {
LOG.error("exception closing resource", e);
LOG.error("Closing resource failed.", e);
}
}
}
@@ -109,7 +93,7 @@ public class DeferredCloser implements AutoCloseable {
}
}
public <T> DeferredClosable<T> closeLater(T object, Closer<T> closer) {
public <T> DeferredClosable<T> closeLater(T object, ConsumerThrowingException<T, Exception> closer) {
Objects.requireNonNull(object);
Objects.requireNonNull(closer);
final ManagedResource<T> resource = new ManagedResource<T>(object, closer);
@@ -130,4 +114,16 @@ public class DeferredCloser implements AutoCloseable {
public static <T> DeferredClosable<T> empty() {
return (DeferredClosable<T>) EMPTY_RESOURCE;
}
static class EmptyResource<T> implements DeferredClosable<T> {
@Override
public Optional<T> get() {
return Optional.empty();
}
@Override
public void close() {
}
}
}

View File

@@ -18,77 +18,67 @@ import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.util.ResourceBundle;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.settings.Localization;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.stage.Stage;
@Singleton
public final class TrayIconUtil {
private static TrayIconUtil INSTANCE;
private static final Logger LOG = LoggerFactory.getLogger(TrayIconUtil.class);
private final Stage mainApplicationWindow;
private final ResourceBundle rb;
private final Runnable exitCommand;
private final Stage mainWindow;
private final Localization localization;
/**
* This will add an icon to the system tray and modify the application shutdown procedure. Depending on
* {@link Platform#isImplicitExit()} the application may still be running, allowing shutdown using the tray menu.
*/
public synchronized static void init(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) {
if (INSTANCE == null && SystemTray.isSupported()) {
INSTANCE = new TrayIconUtil(mainApplicationWindow, rb, exitCommand);
}
@Inject
public TrayIconUtil(@Named("mainWindow") Stage mainWindow, Localization localization) {
this.mainWindow = mainWindow;
this.localization = localization;
}
private TrayIconUtil(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) {
this.mainApplicationWindow = mainApplicationWindow;
this.rb = rb;
this.exitCommand = exitCommand;
initTrayIcon();
}
private void initTrayIcon() {
final TrayIcon trayIcon = createTrayIcon();
public void initTrayIcon(Runnable exitCommand) {
final TrayIcon trayIcon = createTrayIcon(exitCommand);
try {
SystemTray.getSystemTray().add(trayIcon);
mainApplicationWindow.setOnCloseRequest((e) -> {
mainWindow.setOnCloseRequest((e) -> {
if (Platform.isImplicitExit()) {
exitCommand.run();
} else {
mainApplicationWindow.close();
mainWindow.close();
this.showTrayNotification(trayIcon);
}
});
} catch (SecurityException | AWTException ex) {
// not working? then just go ahead and close the app
mainApplicationWindow.setOnCloseRequest((ev) -> {
mainWindow.setOnCloseRequest((ev) -> {
exitCommand.run();
});
}
}
private TrayIcon createTrayIcon() {
private TrayIcon createTrayIcon(Runnable exitCommand) {
final PopupMenu popup = new PopupMenu();
final MenuItem showItem = new MenuItem(rb.getString("tray.menu.open"));
final MenuItem showItem = new MenuItem(localization.getString("tray.menu.open"));
showItem.addActionListener(this::restoreFromTray);
popup.add(showItem);
final MenuItem exitItem = new MenuItem(rb.getString("tray.menu.quit"));
exitItem.addActionListener(this::quitFromTray);
final MenuItem exitItem = new MenuItem(localization.getString("tray.menu.quit"));
exitItem.addActionListener(e -> exitCommand.run());
popup.add(exitItem);
final Image image;
@@ -98,7 +88,7 @@ public final class TrayIconUtil {
image = Toolkit.getDefaultToolkit().getImage(TrayIconUtil.class.getResource("/tray_icon.png"));
}
return new TrayIcon(image, rb.getString("app.name"), popup);
return new TrayIcon(image, localization.getString("app.name"), popup);
}
/**
@@ -120,8 +110,8 @@ public final class TrayIconUtil {
private void showTrayNotification(TrayIcon trayIcon) {
final Runnable notificationCmd;
if (SystemUtils.IS_OS_MAC_OSX) {
final String title = rb.getString("tray.infoMsg.title");
final String msg = rb.getString("tray.infoMsg.msg.osx");
final String title = localization.getString("tray.infoMsg.title");
final String msg = localization.getString("tray.infoMsg.msg.osx");
final String notificationCenterAppleScript = String.format("display notification \"%s\" with title \"%s\"", msg, title);
notificationCmd = () -> {
try {
@@ -137,8 +127,8 @@ public final class TrayIconUtil {
}
};
} else {
final String title = rb.getString("tray.infoMsg.title");
final String msg = rb.getString("tray.infoMsg.msg");
final String title = localization.getString("tray.infoMsg.title");
final String msg = localization.getString("tray.infoMsg.msg");
notificationCmd = () -> {
trayIcon.displayMessage(title, msg, MessageType.INFO);
};
@@ -150,13 +140,9 @@ public final class TrayIconUtil {
private void restoreFromTray(ActionEvent event) {
Platform.runLater(() -> {
mainApplicationWindow.show();
mainApplicationWindow.requestFocus();
mainWindow.show();
mainWindow.requestFocus();
});
}
private void quitFromTray(ActionEvent event) {
exitCommand.run();
}
}