diff --git a/src/main/java/org/cryptomator/common/settings/Settings.java b/src/main/java/org/cryptomator/common/settings/Settings.java index cfe30641d..fd9f01904 100644 --- a/src/main/java/org/cryptomator/common/settings/Settings.java +++ b/src/main/java/org/cryptomator/common/settings/Settings.java @@ -44,6 +44,7 @@ public class Settings { static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess"; static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name(); static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false; + static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01"; public final ObservableList directories; public final BooleanProperty askedForUpdateCheck; @@ -67,6 +68,7 @@ public class Settings { public final StringProperty displayConfiguration; public final StringProperty language; public final StringProperty mountService; + public final StringProperty lastUpdateCheck; private Consumer saveCmd; @@ -104,6 +106,7 @@ public class Settings { this.displayConfiguration = new SimpleStringProperty(this, "displayConfiguration", json.displayConfiguration); this.language = new SimpleStringProperty(this, "language", json.language); this.mountService = new SimpleStringProperty(this, "mountService", json.mountService); + this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck); this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList()); @@ -131,6 +134,7 @@ public class Settings { displayConfiguration.addListener(this::somethingChanged); language.addListener(this::somethingChanged); mountService.addListener(this::somethingChanged); + lastUpdateCheck.addListener(this::somethingChanged); } @SuppressWarnings("deprecation") @@ -185,6 +189,7 @@ public class Settings { json.displayConfiguration = displayConfiguration.get(); json.language = language.get(); json.mountService = mountService.get(); + json.lastUpdateCheck = lastUpdateCheck.get(); return json; } diff --git a/src/main/java/org/cryptomator/common/settings/SettingsJson.java b/src/main/java/org/cryptomator/common/settings/SettingsJson.java index 4bd23bec7..977e1d3cd 100644 --- a/src/main/java/org/cryptomator/common/settings/SettingsJson.java +++ b/src/main/java/org/cryptomator/common/settings/SettingsJson.java @@ -83,4 +83,7 @@ class SettingsJson { @JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233 String preferredVolumeImpl; + @JsonProperty("lastUpdateCheck") + String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK; + } diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index d316ba328..cefd424ff 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -42,6 +42,7 @@ public enum FxmlFile { RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), // RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), // REMOVE_VAULT("/fxml/remove_vault.fxml"), // + UPDATE_REMINDER("/fxml/update_reminder.fxml"), // UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"), UNLOCK_INVALID_MOUNT_POINT("/fxml/unlock_invalid_mount_point.fxml"), // UNLOCK_SELECT_MASTERKEYFILE("/fxml/unlock_select_masterkeyfile.fxml"), // diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index b71da265c..d845655cf 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -68,6 +68,8 @@ public class FxApplication { return null; }); + appWindows.checkAndShowUpdateReminderWindow(); + launchEventHandler.startHandlingLaunchEvents(); autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); } diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java index d9f7da434..86ca62c3b 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationModule.java @@ -15,13 +15,14 @@ import org.cryptomator.ui.preferences.PreferencesComponent; import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.cryptomator.ui.unlock.UnlockComponent; +import org.cryptomator.ui.updatereminder.UpdateReminderComponent; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; import javafx.scene.image.Image; import java.io.IOException; import java.io.InputStream; -@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class}) +@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, VaultOptionsComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class, HealthCheckComponent.class, UpdateReminderComponent.class}) abstract class FxApplicationModule { private static Image createImageFromResource(String resourceName) throws IOException { @@ -53,4 +54,5 @@ abstract class FxApplicationModule { static QuitComponent provideQuitComponent(QuitComponent.Builder builder) { return builder.build(); } + } \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java index 5b81fa15a..866b1d9df 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplicationWindows.java @@ -13,6 +13,7 @@ import org.cryptomator.ui.preferences.SelectedPreferencesTab; import org.cryptomator.ui.quit.QuitComponent; import org.cryptomator.ui.unlock.UnlockComponent; import org.cryptomator.ui.unlock.UnlockWorkflow; +import org.cryptomator.ui.updatereminder.UpdateReminderComponent; import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab; import org.cryptomator.ui.vaultoptions.VaultOptionsComponent; import org.jetbrains.annotations.Nullable; @@ -45,6 +46,7 @@ public class FxApplicationWindows { private final Lazy preferencesWindow; private final QuitComponent.Builder quitWindowBuilder; private final UnlockComponent.Factory unlockWorkflowFactory; + private final UpdateReminderComponent.Factory updateReminderWindowBuilder; private final LockComponent.Factory lockWorkflowFactory; private final ErrorComponent.Factory errorWindowFactory; private final ExecutorService executor; @@ -52,13 +54,24 @@ public class FxApplicationWindows { private final FilteredList visibleWindows; @Inject - public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional trayIntegration, Lazy mainWindow, Lazy preferencesWindow, QuitComponent.Builder quitWindowBuilder, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor, VaultOptionsComponent.Factory vaultOptionsWindow) { + public FxApplicationWindows(@PrimaryStage Stage primaryStage, + Optional trayIntegration, // + Lazy mainWindow, // + Lazy preferencesWindow, // + QuitComponent.Builder quitWindowBuilder, // + UnlockComponent.Factory unlockWorkflowFactory, // + UpdateReminderComponent.Factory updateReminderWindowBuilder, // + LockComponent.Factory lockWorkflowFactory, // + ErrorComponent.Factory errorWindowFactory, // + VaultOptionsComponent.Factory vaultOptionsWindow, // + ExecutorService executor) { this.primaryStage = primaryStage; this.trayIntegration = trayIntegration; this.mainWindow = mainWindow; this.preferencesWindow = preferencesWindow; this.quitWindowBuilder = quitWindowBuilder; this.unlockWorkflowFactory = unlockWorkflowFactory; + this.updateReminderWindowBuilder = updateReminderWindowBuilder; this.lockWorkflowFactory = lockWorkflowFactory; this.errorWindowFactory = errorWindowFactory; this.executor = executor; @@ -117,6 +130,10 @@ public class FxApplicationWindows { CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater); } + public void checkAndShowUpdateReminderWindow() { + CompletableFuture.runAsync(() -> updateReminderWindowBuilder.create().checkAndShowUpdateReminderWindow(), Platform::runLater); + } + public CompletionStage startUnlockWorkflow(Vault vault, @Nullable Stage owner) { return CompletableFuture.supplyAsync(() -> { Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked."); diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java new file mode 100644 index 000000000..aa13f30da --- /dev/null +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderComponent.java @@ -0,0 +1,38 @@ +package org.cryptomator.ui.updatereminder; + +import dagger.Lazy; +import dagger.Subcomponent; +import org.cryptomator.common.settings.Settings; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlScene; + +import javafx.scene.Scene; +import javafx.stage.Stage; +import java.time.LocalDate; + +@UpdateReminderScoped +@Subcomponent(modules = {UpdateReminderModule.class}) +public interface UpdateReminderComponent { + + @UpdateReminderWindow + Stage window(); + + @FxmlScene(FxmlFile.UPDATE_REMINDER) + Lazy updateReminderScene(); + + Settings settings(); + + default void checkAndShowUpdateReminderWindow() { + if (LocalDate.parse(settings().lastUpdateCheck.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) { + Stage stage = window(); + stage.setScene(updateReminderScene().get()); + stage.sizeToScene(); + stage.show(); + } + } + + @Subcomponent.Factory + interface Factory { + UpdateReminderComponent create(); + } +} \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java new file mode 100644 index 000000000..28ae0b5c6 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderController.java @@ -0,0 +1,49 @@ +package org.cryptomator.ui.updatereminder; + +import org.cryptomator.common.settings.Settings; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.fxapp.UpdateChecker; + +import javax.inject.Inject; +import javafx.fxml.FXML; +import javafx.stage.Stage; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@UpdateReminderScoped +public class UpdateReminderController implements FxController { + + private final Stage window; + private final Settings settings; + private final UpdateChecker updateChecker; + + + @Inject + UpdateReminderController(@UpdateReminderWindow Stage window, Settings settings, UpdateChecker updateChecker) { + this.window = window; + this.settings = settings; + this.updateChecker = updateChecker; + } + + @FXML + public void cancel() { + settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); + window.close(); + } + + @FXML + public void once() { + settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); + updateChecker.checkForUpdatesNow(); + window.close(); + } + + @FXML + public void automatically() { + settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE)); + updateChecker.checkForUpdatesNow(); + settings.checkForUpdates.set(true); + window.close(); + } + +} \ No newline at end of file diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderModule.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderModule.java new file mode 100644 index 000000000..b450c6a14 --- /dev/null +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderModule.java @@ -0,0 +1,59 @@ +package org.cryptomator.ui.updatereminder; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoMap; +import org.cryptomator.ui.common.DefaultSceneFactory; +import org.cryptomator.ui.common.FxController; +import org.cryptomator.ui.common.FxControllerKey; +import org.cryptomator.ui.common.FxmlFile; +import org.cryptomator.ui.common.FxmlLoaderFactory; +import org.cryptomator.ui.common.FxmlScene; +import org.cryptomator.ui.common.StageFactory; + +import javax.inject.Provider; +import javafx.scene.Scene; +import javafx.stage.Modality; +import javafx.stage.Stage; +import java.util.Map; +import java.util.ResourceBundle; + +@Module +abstract class UpdateReminderModule { + + @Provides + @UpdateReminderWindow + @UpdateReminderScoped + static FxmlLoaderFactory provideFxmlLoaderFactory(Map, Provider> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) { + return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle); + } + + @Provides + @UpdateReminderWindow + @UpdateReminderScoped + static Stage provideStage(StageFactory factory, ResourceBundle resourceBundle) { + Stage stage = factory.create(); + stage.setTitle(resourceBundle.getString("updateReminder.title")); + stage.setMinWidth(500); + stage.setMinHeight(100); + stage.initModality(Modality.APPLICATION_MODAL); + return stage; + } + + @Provides + @FxmlScene(FxmlFile.UPDATE_REMINDER) + @UpdateReminderScoped + static Scene provideUpdateReminderScene(@UpdateReminderWindow FxmlLoaderFactory fxmlLoaders) { + return fxmlLoaders.createScene(FxmlFile.UPDATE_REMINDER); + } + + + // ------------------ + + @Binds + @IntoMap + @FxControllerKey(UpdateReminderController.class) + abstract FxController bindUpdateReminderController(UpdateReminderController controller); + +} diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderScoped.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderScoped.java new file mode 100644 index 000000000..13133af2a --- /dev/null +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderScoped.java @@ -0,0 +1,13 @@ +package org.cryptomator.ui.updatereminder; + +import javax.inject.Scope; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Scope +@Documented +@Retention(RetentionPolicy.RUNTIME) +@interface UpdateReminderScoped { + +} diff --git a/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderWindow.java b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderWindow.java new file mode 100644 index 000000000..f7c9b9d2c --- /dev/null +++ b/src/main/java/org/cryptomator/ui/updatereminder/UpdateReminderWindow.java @@ -0,0 +1,14 @@ +package org.cryptomator.ui.updatereminder; + +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 UpdateReminderWindow { + +} diff --git a/src/main/resources/fxml/update_reminder.fxml b/src/main/resources/fxml/update_reminder.fxml new file mode 100644 index 000000000..50bb80b5f --- /dev/null +++ b/src/main/resources/fxml/update_reminder.fxml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +