mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-22 04:31:27 +00:00
added update notification
This commit is contained in:
@@ -18,8 +18,8 @@
|
||||
<name>Cryptomator WebDAV and I/O module</name>
|
||||
|
||||
<properties>
|
||||
<jetty.version>9.2.5.v20141112</jetty.version>
|
||||
<jackrabbit.version>2.9.0</jackrabbit.version>
|
||||
<jetty.version>9.2.10.v20150310</jetty.version>
|
||||
<jackrabbit.version>2.9.1</jackrabbit.version>
|
||||
<commons.transaction.version>1.2</commons.transaction.version>
|
||||
<jta.version>1.1</jta.version>
|
||||
</properties>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<commons-collections.version>4.0</commons-collections.version>
|
||||
<commons-lang3.version>3.3.2</commons-lang3.version>
|
||||
<commons-codec.version>1.10</commons-codec.version>
|
||||
<commons-httpclient.version>3.1</commons-httpclient.version>
|
||||
<jackson-databind.version>2.4.4</jackson-databind.version>
|
||||
<mockito.version>1.10.19</mockito.version>
|
||||
</properties>
|
||||
@@ -110,6 +111,12 @@
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- org.apache.httpcomponents:httpclient is newer, but jackrabbit uses this version. We don't have a reason to upgrade -->
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
<version>${commons-httpclient.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Guava -->
|
||||
<dependency>
|
||||
|
||||
@@ -48,7 +48,11 @@
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-httpclient</groupId>
|
||||
<artifactId>commons-httpclient</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DI -->
|
||||
<dependency>
|
||||
<groupId>com.google.inject</groupId>
|
||||
@@ -78,6 +82,7 @@
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Main-Class>${exec.mainClass}</Main-Class>
|
||||
<Implementation-Version>${project.version}</Implementation-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
|
||||
@@ -57,14 +57,15 @@ public class MainApplication extends Application {
|
||||
}
|
||||
|
||||
public MainApplication(Injector injector) {
|
||||
this(injector.getInstance(ExecutorService.class), injector.getInstance(ControllerFactory.class), injector.getInstance(DeferredCloser.class));
|
||||
this(injector.getInstance(ExecutorService.class), injector.getInstance(ControllerFactory.class), injector.getInstance(DeferredCloser.class), injector.getInstance(MainApplicationReference.class));
|
||||
}
|
||||
|
||||
public MainApplication(ExecutorService executorService, ControllerFactory controllerFactory, DeferredCloser closer) {
|
||||
public MainApplication(ExecutorService executorService, ControllerFactory controllerFactory, DeferredCloser closer, MainApplicationReference appRef) {
|
||||
super();
|
||||
this.executorService = executorService;
|
||||
this.controllerFactory = controllerFactory;
|
||||
this.closer = closer;
|
||||
appRef.set(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -175,4 +176,25 @@ public class MainApplication extends Application {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed to inject MainApplication. Problem: Application needs to be set asap after injector creation.
|
||||
*/
|
||||
static class MainApplicationReference {
|
||||
|
||||
private Application application;
|
||||
|
||||
private void set(Application application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
public Application get() {
|
||||
if (application == null) {
|
||||
throw new IllegalStateException("not yet ready.");
|
||||
} else {
|
||||
return application;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,22 +8,27 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.crypto.SamplingDecorator;
|
||||
import org.cryptomator.crypto.aes256.Aes256Cryptor;
|
||||
import org.cryptomator.ui.MainApplication.MainApplicationReference;
|
||||
import org.cryptomator.ui.model.VaultFactory;
|
||||
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 org.cryptomator.ui.util.mount.WebDavMounter;
|
||||
import org.cryptomator.ui.util.mount.WebDavMounterProvider;
|
||||
import org.cryptomator.webdav.WebDavServer;
|
||||
@@ -57,6 +62,24 @@ public class MainModule extends AbstractModule {
|
||||
return cls -> injector.getInstance(cls);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MainApplicationReference getApplicationBinding() {
|
||||
return new MainApplicationReference();
|
||||
}
|
||||
|
||||
@Provides
|
||||
Application getApplication(MainApplicationReference ref) {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("SemVer")
|
||||
@Singleton
|
||||
Comparator<String> getSemVerComparator() {
|
||||
return new SemVerComparator();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ExecutorService getExec() {
|
||||
|
||||
@@ -8,22 +8,105 @@
|
||||
******************************************************************************/
|
||||
package org.cryptomator.ui.controllers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpMethod;
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.httpclient.methods.GetMethod;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class WelcomeController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private ImageView botImageView;
|
||||
|
||||
@FXML
|
||||
private Hyperlink updateLink;
|
||||
|
||||
private final Application app;
|
||||
private final Comparator<String> semVerComparator;
|
||||
private final ExecutorService executor;
|
||||
private ResourceBundle rb;
|
||||
|
||||
@Inject
|
||||
public WelcomeController(Application app, @Named("SemVer") Comparator<String> semVerComparator, ExecutorService executor) {
|
||||
this.app = app;
|
||||
this.semVerComparator = semVerComparator;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
this.rb = rb;
|
||||
this.botImageView.setImage(new Image(WelcomeController.class.getResource("/bot_welcome.png").toString()));
|
||||
executor.execute(this::checkForUpdates);
|
||||
}
|
||||
|
||||
private void checkForUpdates() {
|
||||
final HttpClient client = new HttpClient();
|
||||
final HttpMethod method = new GetMethod("https://cryptomator.org/downloads/latestVersion.json");
|
||||
client.getParams().setConnectionManagerTimeout(5000);
|
||||
try {
|
||||
client.executeMethod(method);
|
||||
if (method.getStatusCode() == HttpStatus.SC_OK) {
|
||||
final byte[] responseData = method.getResponseBody();
|
||||
final ObjectMapper mapper = new ObjectMapper();
|
||||
final Map<String, String> map = mapper.readValue(responseData, new TypeReference<HashMap<String, String>>() {
|
||||
});
|
||||
this.compareVersions(map);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// no error handling required. Maybe next time the version check is successful.
|
||||
}
|
||||
}
|
||||
|
||||
private void compareVersions(final Map<String, String> latestVersions) {
|
||||
final String latestVersion;
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
latestVersion = latestVersions.get("mac");
|
||||
} else if (SystemUtils.IS_OS_WINDOWS) {
|
||||
latestVersion = latestVersions.get("win");
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
latestVersion = latestVersions.get("linux");
|
||||
} else {
|
||||
// no version check possible on unsupported OS
|
||||
return;
|
||||
}
|
||||
final String currentVersion = WelcomeController.class.getPackage().getImplementationVersion();
|
||||
if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) {
|
||||
final String msg = String.format(rb.getString("welcome.newVersionMessage"), latestVersion, currentVersion);
|
||||
Platform.runLater(() -> {
|
||||
this.updateLink.setText(msg);
|
||||
this.updateLink.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickUpdateLink(ActionEvent event) {
|
||||
app.getHostServices().showDocument("https://cryptomator.org/#download");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class SemVerComparator implements Comparator<String> {
|
||||
|
||||
@Override
|
||||
public int compare(String version1, String version2) {
|
||||
final String[] vComps1 = StringUtils.split(version1, '.');
|
||||
final String[] vComps2 = StringUtils.split(version2, '.');
|
||||
final int commonCompCount = Math.min(vComps1.length, vComps2.length);
|
||||
|
||||
for (int i = 0; i < commonCompCount; i++) {
|
||||
int subversionComparisionResult = 0;
|
||||
try {
|
||||
final int v1 = Integer.parseInt(vComps1[i]);
|
||||
final int v2 = Integer.parseInt(vComps2[i]);
|
||||
subversionComparisionResult = v1 - v2;
|
||||
} catch (NumberFormatException ex) {
|
||||
// ok, lets compare this fragment lexicographically
|
||||
subversionComparisionResult = vComps1[i].compareTo(vComps2[i]);
|
||||
}
|
||||
if (subversionComparisionResult != 0) {
|
||||
return subversionComparisionResult;
|
||||
}
|
||||
}
|
||||
|
||||
// all in common so far? longest version string wins:
|
||||
return vComps1.length - vComps2.length;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -293,6 +293,20 @@
|
||||
-fx-text-fill: -fx-text-background-color;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Hyperlink *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.hyperlink {
|
||||
-fx-cursor: hand;
|
||||
-fx-text-fill: #0069D9;
|
||||
}
|
||||
.hyperlink:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Button & ToggleButton *
|
||||
|
||||
@@ -295,6 +295,20 @@
|
||||
-fx-text-fill: -fx-text-background-color;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Hyperlink *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.hyperlink {
|
||||
-fx-cursor: hand;
|
||||
-fx-text-fill: #3399FF;
|
||||
}
|
||||
.hyperlink:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Button & ToggleButton *
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
<?import javafx.scene.layout.AnchorPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.control.Hyperlink?>
|
||||
|
||||
<AnchorPane xmlns:fx="http://javafx.com/fxml" fx:controller="org.cryptomator.ui.controllers.WelcomeController">
|
||||
|
||||
<children>
|
||||
<Label AnchorPane.leftAnchor="100.0" AnchorPane.topAnchor="20.0" style="-fx-font-size: 1.5em;" text="%welcome.welcomeLabel"/>
|
||||
<Label AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="20.0" prefWidth="400.0" alignment="CENTER" style="-fx-font-size: 1.5em;" text="%welcome.welcomeLabel"/>
|
||||
<Hyperlink AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="50.0" prefWidth="400.0" alignment="CENTER" fx:id="updateLink" onAction="#didClickUpdateLink" visible="false"/>
|
||||
|
||||
<ImageView fx:id="botImageView" AnchorPane.leftAnchor="100.0" AnchorPane.topAnchor="200.0" fitHeight="200.0" preserveRatio="true" smooth="false"/>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ main.addDirectory.contextMenu.open=Add existing vault
|
||||
# welcome.fxml
|
||||
welcome.welcomeLabel=Welcome to Cryptomator
|
||||
welcome.addButtonInstructionLabel=Start by adding a new vault
|
||||
welcome.newVersionMessage=Version %s can be downloaded. This is %s.
|
||||
|
||||
# initialize.fxml
|
||||
initialize.label.password=Password
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.cryptomator.ui;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MainApplicationTest {
|
||||
|
||||
@Test
|
||||
public void testInjection() throws Exception {
|
||||
new MainApplication();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.cryptomator.ui.test;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.junit.runners.BlockJUnit4ClassRunner;
|
||||
import org.junit.runners.model.InitializationError;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Module;
|
||||
|
||||
/**
|
||||
* Taken from http://fabiostrozzi.eu/2011/03/27/junit-tests-easy-guice/
|
||||
*/
|
||||
public class GuiceJUnitRunner extends BlockJUnit4ClassRunner {
|
||||
private final Injector injector;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface GuiceModules {
|
||||
Class<?>[] value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createTest() throws Exception {
|
||||
Object obj = super.createTest();
|
||||
injector.injectMembers(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
public GuiceJUnitRunner(Class<?> klass) throws InitializationError {
|
||||
super(klass);
|
||||
Class<?>[] classes = getModulesFor(klass);
|
||||
injector = createInjectorFor(classes);
|
||||
}
|
||||
|
||||
private Injector createInjectorFor(Class<?>[] classes) throws InitializationError {
|
||||
Module[] modules = new Module[classes.length];
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
try {
|
||||
modules[i] = (Module) (classes[i]).newInstance();
|
||||
} catch (InstantiationException e) {
|
||||
throw new InitializationError(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InitializationError(e);
|
||||
}
|
||||
}
|
||||
return Guice.createInjector(modules);
|
||||
}
|
||||
|
||||
private Class<?>[] getModulesFor(Class<?> klass) throws InitializationError {
|
||||
GuiceModules annotation = klass.getAnnotation(GuiceModules.class);
|
||||
if (annotation == null)
|
||||
throw new InitializationError("Missing @GuiceModules annotation for unit test '" + klass.getName() + "'");
|
||||
return annotation.value();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.cryptomator.ui.MainModule;
|
||||
import org.cryptomator.ui.test.GuiceJUnitRunner;
|
||||
import org.cryptomator.ui.test.GuiceJUnitRunner.GuiceModules;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(GuiceJUnitRunner.class)
|
||||
@GuiceModules(MainModule.class)
|
||||
public class SemVerComparatorTest {
|
||||
|
||||
@Inject
|
||||
@Named("SemVer")
|
||||
private Comparator<String> semVerComparator;
|
||||
|
||||
// equal versions
|
||||
|
||||
@Test
|
||||
public void compareEqualVersions() {
|
||||
final int comparisonResult = semVerComparator.compare("1.23.4", "1.23.4");
|
||||
Assert.assertEquals(0, Integer.signum(comparisonResult));
|
||||
}
|
||||
|
||||
// newer versions in first argument
|
||||
|
||||
@Test
|
||||
public void compareHigherToLowerVersions() {
|
||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.5", "1.23.4")));
|
||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.24.4", "1.23.4")));
|
||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4", "1.23")));
|
||||
Assert.assertEquals(1, Integer.signum(semVerComparator.compare("1.23.4a", "1.23.4")));
|
||||
}
|
||||
|
||||
// newer versions in second argument
|
||||
|
||||
@Test
|
||||
public void compareLowerToHigherVersions() {
|
||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.5")));
|
||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.24.4")));
|
||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23", "1.23.4")));
|
||||
Assert.assertEquals(-1, Integer.signum(semVerComparator.compare("1.23.4", "1.23.4a")));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user