diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 6c30aa606..9ced33c15 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -43,6 +43,7 @@ body: - WinFsp (Local Drive) - FUSE-T - macFUSE + - FUSE - WebDAV (Windows Explorer) - WebDAV (AppleScript) - WebDAV (gio) @@ -95,4 +96,4 @@ body: id: further-info attributes: label: Anything else? - description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? \ No newline at end of file + description: Links? References? Screenshots? Configurations? Any data that might be necessary to reproduce the issue? diff --git a/.github/workflows/debian.yml b/.github/workflows/debian.yml index 14cdca80a..2ae6c3262 100644 --- a/.github/workflows/debian.yml +++ b/.github/workflows/debian.yml @@ -97,7 +97,8 @@ jobs: run: | cp -r dist/linux/debian/ pkgdir export RFC2822_TIMESTAMP=`date --rfc-2822` - envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM}' < dist/linux/debian/rules > pkgdir/debian/rules + export DISABLE_UPDATE_CHECK=${{ inputs.dput }} + envsubst '${SEMVER_STR} ${VERSION_NUM} ${REVISION_NUM} ${DISABLE_UPDATE_CHECK}' < dist/linux/debian/rules > pkgdir/debian/rules envsubst '${PPA_VERSION} ${RFC2822_TIMESTAMP}' < dist/linux/debian/changelog > pkgdir/debian/changelog find . -name "*.jar" >> pkgdir/debian/source/include-binaries mv pkgdir cryptomator_${{ inputs.ppaver }} diff --git a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml index 596ebea4a..6e4873712 100644 --- a/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml +++ b/dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml @@ -66,6 +66,7 @@ + diff --git a/dist/linux/debian/rules b/dist/linux/debian/rules index 231f2a1d5..c12879025 100755 --- a/dist/linux/debian/rules +++ b/dist/linux/debian/rules @@ -59,6 +59,7 @@ override_dh_auto_build: --java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"/usr/share/icons/hicolor/symbolic/apps\"" \ --java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \ --java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \ + --java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \ --app-version "${VERSION_NUM}.${REVISION_NUM}" \ --resource-dir resources \ --verbose diff --git a/pom.xml b/pom.xml index f1012222d..08cb4bb73 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.cryptomator cryptomator - 1.10.0 + 1.10.1 Cryptomator Desktop App @@ -35,12 +35,12 @@ 2.6.7 1.3.0 - 1.2.2 - 1.2.1 - 1.3.0-beta6 + 1.2.3 + 1.2.2 + 1.3.0 3.0.0 2.0.0 - 2.0.3 + 2.0.4 3.13.0 diff --git a/src/main/java/org/cryptomator/common/Environment.java b/src/main/java/org/cryptomator/common/Environment.java index c47870dd8..981b3729b 100644 --- a/src/main/java/org/cryptomator/common/Environment.java +++ b/src/main/java/org/cryptomator/common/Environment.java @@ -2,6 +2,7 @@ package org.cryptomator.common; import com.google.common.base.Splitter; import com.google.common.base.Strings; +import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +32,7 @@ public class Environment { private static final String BUILD_NUMBER_PROP_NAME = "cryptomator.buildNumber"; private static final String PLUGIN_DIR_PROP_NAME = "cryptomator.pluginDir"; private static final String TRAY_ICON_PROP_NAME = "cryptomator.showTrayIcon"; + private static final String DISABLE_UPDATE_CHECK_PROP_NAME = "cryptomator.disableUpdateCheck"; private Environment() {} @@ -43,15 +45,16 @@ public class Environment { logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME); logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME); logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME); + logCryptomatorSystemProperty(P12_PATH_PROP_NAME); logCryptomatorSystemProperty(LOG_DIR_PROP_NAME); logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME); - logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME); logCryptomatorSystemProperty(MOUNTPOINT_DIR_PROP_NAME); logCryptomatorSystemProperty(MIN_PW_LENGTH_PROP_NAME); logCryptomatorSystemProperty(APP_VERSION_PROP_NAME); logCryptomatorSystemProperty(BUILD_NUMBER_PROP_NAME); + logCryptomatorSystemProperty(PLUGIN_DIR_PROP_NAME); logCryptomatorSystemProperty(TRAY_ICON_PROP_NAME); - logCryptomatorSystemProperty(P12_PATH_PROP_NAME); + logCryptomatorSystemProperty(DISABLE_UPDATE_CHECK_PROP_NAME); } public static Environment getInstance() { @@ -74,10 +77,6 @@ public class Environment { return getPaths(SETTINGS_PATH_PROP_NAME); } - public Stream getP12Path() { - return getPaths(P12_PATH_PROP_NAME); - } - public Stream getIpcSocketPath() { return getPaths(IPC_SOCKET_PATH_PROP_NAME); } @@ -86,6 +85,10 @@ public class Environment { return getPaths(KEYCHAIN_PATHS_PROP_NAME); } + public Stream getP12Path() { + return getPaths(P12_PATH_PROP_NAME); + } + public Optional getLogDir() { return getPath(LOG_DIR_PROP_NAME); } @@ -94,14 +97,14 @@ public class Environment { return Optional.ofNullable(System.getProperty(LOOPBACK_ALIAS_PROP_NAME)); } - public Optional getPluginDir() { - return getPath(PLUGIN_DIR_PROP_NAME); - } - public Optional getMountPointsDir() { return getPath(MOUNTPOINT_DIR_PROP_NAME); } + public int getMinPwLength() { + return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH); + } + /** * Returns the app version defined in the {@value APP_VERSION_PROP_NAME} property or returns "SNAPSHOT". * @@ -115,20 +118,24 @@ public class Environment { return Optional.ofNullable(System.getProperty(BUILD_NUMBER_PROP_NAME)); } - public int getMinPwLength() { - return Integer.getInteger(MIN_PW_LENGTH_PROP_NAME, DEFAULT_MIN_PW_LENGTH); + public Optional getPluginDir() { + return getPath(PLUGIN_DIR_PROP_NAME); } public boolean showTrayIcon() { return Boolean.getBoolean(TRAY_ICON_PROP_NAME); } + public boolean disableUpdateCheck() { + return Boolean.getBoolean(DISABLE_UPDATE_CHECK_PROP_NAME); + } + private Optional getPath(String propertyName) { String value = System.getProperty(propertyName); return Optional.ofNullable(value).map(Paths::get); } - // visible for testing + @VisibleForTesting Stream getPaths(String propertyName) { Stream rawSettingsPaths = getRawList(propertyName, System.getProperty("path.separator").charAt(0)); return rawSettingsPaths.filter(Predicate.not(Strings::isNullOrEmpty)).map(Path::of); diff --git a/src/main/java/org/cryptomator/common/ErrorCode.java b/src/main/java/org/cryptomator/common/ErrorCode.java index 7363e2278..d75ab97d0 100644 --- a/src/main/java/org/cryptomator/common/ErrorCode.java +++ b/src/main/java/org/cryptomator/common/ErrorCode.java @@ -3,6 +3,7 @@ package org.cryptomator.common; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.base.Throwables; +import org.jetbrains.annotations.VisibleForTesting; import java.util.Locale; import java.util.Objects; @@ -114,7 +115,7 @@ public class ErrorCode { * @param bottomFrames Other stack frames, potentially forming the bottom of the stack of allFrames * @return The number of additional frames in allFrames. In most cases this should be equal to the difference in size. */ - // visible for testing + @VisibleForTesting static int countTopmostFrames(StackTraceElement[] allFrames, StackTraceElement[] bottomFrames) { if (allFrames.length < bottomFrames.length) { // if frames had been stacked on top of bottomFrames, allFrames would be larger @@ -124,7 +125,7 @@ public class ErrorCode { } } - // visible for testing + @VisibleForTesting static int commonSuffixLength(T[] set, T[] subset) { Preconditions.checkArgument(set.length >= subset.length); // iterate items backwards as long as they are identical diff --git a/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java b/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java index b632923a8..b436bc19a 100644 --- a/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java +++ b/src/main/java/org/cryptomator/common/mount/MountWithinParentUtil.java @@ -1,6 +1,7 @@ package org.cryptomator.common.mount; import org.apache.commons.lang3.SystemUtils; +import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,7 +67,7 @@ public final class MountWithinParentUtil { } } - //visible for testing + @VisibleForTesting static MountPointState getMountPointState(Path path) throws IOException, IllegalMountPointException { if (Files.notExists(path, LinkOption.NOFOLLOW_LINKS)) { return MountPointState.NOT_EXISTING; @@ -82,7 +83,7 @@ public final class MountWithinParentUtil { return MountPointState.BROKEN_JUNCTION; } - //visible for testing + @VisibleForTesting enum MountPointState { NOT_EXISTING, @@ -93,7 +94,7 @@ public final class MountWithinParentUtil { } - //visible for testing + @VisibleForTesting static void removeResidualHideaway(Path mountPoint, Path hideaway) throws IOException { checkIsHideawayDirectory(mountPoint, hideaway); Files.delete(hideaway); //Fails if not empty @@ -155,7 +156,7 @@ public final class MountWithinParentUtil { } } - //visible for testing + @VisibleForTesting static Path getHideaway(Path mountPoint) { return mountPoint.resolveSibling(HIDEAWAY_PREFIX + mountPoint.getFileName().toString() + HIDEAWAY_SUFFIX); } diff --git a/src/main/java/org/cryptomator/common/settings/VaultSettings.java b/src/main/java/org/cryptomator/common/settings/VaultSettings.java index 7b7e319c9..6662f61ff 100644 --- a/src/main/java/org/cryptomator/common/settings/VaultSettings.java +++ b/src/main/java/org/cryptomator/common/settings/VaultSettings.java @@ -9,6 +9,7 @@ import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import org.apache.commons.lang3.SystemUtils; +import org.jetbrains.annotations.VisibleForTesting; import javafx.beans.Observable; import javafx.beans.binding.Bindings; @@ -126,7 +127,7 @@ public class VaultSettings { return json; } - //visible for testing + @VisibleForTesting static String normalizeDisplayName(String original) { if (original.isBlank() || ".".equals(original) || "..".equals(original)) { return "_"; diff --git a/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java index eb2418c69..dbdb37823 100644 --- a/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java +++ b/src/main/java/org/cryptomator/launcher/FileOpenRequestHandler.java @@ -6,6 +6,7 @@ *******************************************************************************/ package org.cryptomator.launcher; +import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,7 +49,7 @@ class FileOpenRequestHandler { handleLaunchArgs(FileSystems.getDefault(), args); } - // visible for testing + @VisibleForTesting void handleLaunchArgs(FileSystem fs, List args) { Collection pathsToOpen = args.stream().map(str -> { try { diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java index 5cf14444a..2ffda4d73 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/ReadmeGenerator.java @@ -1,5 +1,7 @@ package org.cryptomator.ui.addvaultwizard; +import org.jetbrains.annotations.VisibleForTesting; + import javax.inject.Inject; import java.util.List; import java.util.ResourceBundle; @@ -51,7 +53,7 @@ public class ReadmeGenerator { resourceBundle.getString("addvault.new.readme.accessLocation.4"))); } - // visible for testing + @VisibleForTesting String createDocument(Iterable paragraphs) { StringBuilder sb = new StringBuilder(RTF_HEADER); for (String p : paragraphs) { @@ -63,7 +65,7 @@ public class ReadmeGenerator { return sb.toString(); } - // visible for testing + @VisibleForTesting String escapeNonAsciiChars(CharSequence input) { StringBuilder sb = new StringBuilder(); appendEscaped(sb, input); diff --git a/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java b/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java index 51ff65ec1..fd6d49b89 100644 --- a/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java +++ b/src/main/java/org/cryptomator/ui/convertvault/HubToPasswordConvertController.java @@ -16,6 +16,7 @@ import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.fxapp.FxApplicationWindows; import org.cryptomator.ui.recoverykey.RecoveryKeyFactory; +import org.jetbrains.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -116,7 +117,7 @@ public class HubToPasswordConvertController implements FxController { }, Platform::runLater); // } - //visible for testing + @VisibleForTesting void convertInternal() throws CompletionException, IllegalArgumentException { var passphrase = newPasswordController.getNewPassword(); var vaultPath = vault.getPath(); @@ -141,7 +142,7 @@ public class HubToPasswordConvertController implements FxController { } } - //visible for testing + @VisibleForTesting void backupHubConfig(Path hubConfigPath) throws IOException { byte[] hubConfigBytes = Files.readAllBytes(hubConfigPath); Path backupPath = hubConfigPath.resolveSibling(VAULTCONFIG_FILENAME + BackupHelper.generateFileIdSuffix(hubConfigBytes) + MASTERKEY_BACKUP_SUFFIX); @@ -149,7 +150,7 @@ public class HubToPasswordConvertController implements FxController { LOG.debug("Successfully created hub config backup {}", backupPath.getFileName()); } - //visible for testing + @VisibleForTesting Path createPasswordConfig(Path passwordConfigPath, Path masterkeyFile, Passphrase passphrase) throws IOException, MasterkeyLoadingFailedException { var unverifiedVaultConfig = vault.getVaultConfigCache().get(); try (var masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) { diff --git a/src/main/java/org/cryptomator/ui/error/ErrorController.java b/src/main/java/org/cryptomator/ui/error/ErrorController.java index 3feb3ff44..61f2b0e10 100644 --- a/src/main/java/org/cryptomator/ui/error/ErrorController.java +++ b/src/main/java/org/cryptomator/ui/error/ErrorController.java @@ -42,7 +42,8 @@ public class ErrorController implements FxController { private static final ObjectMapper JSON = new ObjectMapper(); private static final Logger LOG = LoggerFactory.getLogger(ErrorController.class); - private static final String ERROR_CODES_URL = "https://api.cryptomator.org/desktop/error-codes.json"; + private static final String USER_AGENT_FORMAT = "Cryptomator/%s (Build %s) (%s %s %s)"; + private static final String ERROR_CODES_URL_FORMAT = "https://api.cryptomator.org/desktop/error-codes.json?error-code=%s"; private static final String SEARCH_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/categories/errors?discussions_q=category:Errors+%s"; private static final String REPORT_URL_FORMAT = "https://github.com/cryptomator/cryptomator/discussions/new?category=Errors&title=Error+%s&body=%s"; private static final String SEARCH_ERRORCODE_DELIM = " OR "; @@ -142,11 +143,18 @@ public class ErrorController implements FxController { @FXML public void lookUpSolution() { + String userAgent = USER_AGENT_FORMAT.formatted( // + environment.getAppVersion(), // + environment.getBuildNumber().orElse("undefined"), // + System.getProperty("os.name"), // + System.getProperty("os.version"), // + System.getProperty("os.arch")); isLoadingHttpResponse.set(true); askedForLookupDatabasePermission.set(true); HttpClient httpClient = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); HttpRequest httpRequest = HttpRequest.newBuilder()// - .uri(URI.create(ERROR_CODES_URL))// + .header("User-Agent", userAgent) + .uri(URI.create(ERROR_CODES_URL_FORMAT.formatted(URLEncoder.encode(errorCode.toString(),StandardCharsets.UTF_8))))// .build(); httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream())// .thenAcceptAsync(this::loadHttpResponse, executorService)// diff --git a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java index d845655cf..711a0fa44 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java +++ b/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java @@ -1,6 +1,7 @@ package org.cryptomator.ui.fxapp; import dagger.Lazy; +import org.cryptomator.common.Environment; import org.cryptomator.common.settings.Settings; import org.cryptomator.ui.traymenu.TrayMenuComponent; import org.slf4j.Logger; @@ -17,6 +18,7 @@ public class FxApplication { private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class); private final long startupTime; + private final Environment environment; private final Settings settings; private final AppLaunchEventHandler launchEventHandler; private final Lazy trayMenu; @@ -26,8 +28,9 @@ public class FxApplication { private final AutoUnlocker autoUnlocker; @Inject - FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) { + FxApplication(@Named("startupTime") long startupTime, Environment environment, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) { this.startupTime = startupTime; + this.environment = environment; this.settings = settings; this.launchEventHandler = launchEventHandler; this.trayMenu = trayMenu; @@ -68,7 +71,9 @@ public class FxApplication { return null; }); - appWindows.checkAndShowUpdateReminderWindow(); + if (!environment.disableUpdateCheck()) { + appWindows.checkAndShowUpdateReminderWindow(); + } launchEventHandler.startHandlingLaunchEvents(); autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES); diff --git a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java index 4418f79b5..709eb2fe7 100644 --- a/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java +++ b/src/main/java/org/cryptomator/ui/fxapp/UpdateChecker.java @@ -22,23 +22,23 @@ public class UpdateChecker { private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class); private static final Duration AUTOCHECK_DELAY = Duration.seconds(5); + private final Environment env; private final Settings settings; - private final String currentVersion; private final StringProperty latestVersionProperty; private final Comparator semVerComparator; private final ScheduledService updateCheckerService; @Inject UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator semVerComparator, ScheduledService updateCheckerService) { + this.env = env; this.settings = settings; this.latestVersionProperty = latestVersionProperty; this.semVerComparator = semVerComparator; this.updateCheckerService = updateCheckerService; - this.currentVersion = env.getAppVersion(); } public void automaticallyCheckForUpdatesIfEnabled() { - if (settings.checkForUpdates.get()) { + if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) { startCheckingForUpdates(AUTOCHECK_DELAY); } } @@ -63,9 +63,9 @@ public class UpdateChecker { private void checkSucceeded(WorkerStateEvent event) { String latestVersion = updateCheckerService.getValue(); - LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion); + LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion); - if (semVerComparator.compare(currentVersion, latestVersion) < 0) { + if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) { // update is available latestVersionProperty.set(latestVersion); } else { @@ -88,7 +88,7 @@ public class UpdateChecker { } public String getCurrentVersion() { - return currentVersion; + return env.getAppVersion(); } } diff --git a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java index 357222b33..8f0a91d58 100644 --- a/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java +++ b/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java @@ -20,9 +20,10 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ObservableValue; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; -import javafx.event.Event; import javafx.fxml.FXML; +import javafx.geometry.Side; import javafx.scene.control.Button; +import javafx.scene.control.ContextMenu; import javafx.scene.control.ListView; import javafx.scene.input.ContextMenuEvent; import javafx.scene.input.DragEvent; @@ -67,6 +68,8 @@ public class VaultListController implements FxController { public ListView vaultList; public StackPane root; public Button addVaultBtn; + @FXML + private ContextMenu addVaultContextMenu; @Inject VaultListController(@MainWindow Stage mainWindow, // @@ -140,15 +143,15 @@ public class VaultListController implements FxController { root.setOnDragOver(this::handleDragEvent); root.setOnDragDropped(this::handleDragEvent); root.setOnDragExited(this::handleDragEvent); - - addVaultBtn.addEventFilter(ContextMenuEvent.CONTEXT_MENU_REQUESTED, Event::consume); } @FXML - private void showMenu() { - double screenX = addVaultBtn.localToScreen(addVaultBtn.getBoundsInLocal()).getMinX(); - double screenY = addVaultBtn.localToScreen(addVaultBtn.getBoundsInLocal()).getMaxY(); - addVaultBtn.getContextMenu().show(addVaultBtn, screenX, screenY); + private void toggleMenu() { + if (addVaultContextMenu.isShowing()) { + addVaultContextMenu.hide(); + } else { + addVaultContextMenu.show(addVaultBtn, Side.BOTTOM, 0.0, 0.0); + } } private void deselect(MouseEvent released) { diff --git a/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java b/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java index caaaf7d80..0937fccd9 100644 --- a/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java +++ b/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java @@ -1,5 +1,6 @@ package org.cryptomator.ui.preferences; +import org.cryptomator.common.Environment; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.fxapp.UpdateChecker; import org.slf4j.Logger; @@ -19,6 +20,7 @@ public class PreferencesController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(PreferencesController.class); + private final Environment env; private final Stage window; private final ObjectProperty selectedTabProperty; private final BooleanBinding updateAvailable; @@ -31,7 +33,8 @@ public class PreferencesController implements FxController { public Tab aboutTab; @Inject - public PreferencesController(@PreferencesWindow Stage window, ObjectProperty selectedTabProperty, UpdateChecker updateChecker) { + public PreferencesController(Environment env, @PreferencesWindow Stage window, ObjectProperty selectedTabProperty, UpdateChecker updateChecker) { + this.env = env; this.window = window; this.selectedTabProperty = selectedTabProperty; this.updateAvailable = updateChecker.latestVersionProperty().isNotNull(); @@ -42,6 +45,9 @@ public class PreferencesController implements FxController { window.setOnShowing(this::windowWillAppear); selectedTabProperty.addListener(observable -> this.selectChosenTab()); tabPane.getSelectionModel().selectedItemProperty().addListener(observable -> this.selectedTabChanged()); + if (env.disableUpdateCheck()) { + tabPane.getTabs().remove(updatesTab); + } } private void selectChosenTab() { diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java index 73279396d..8f5bb0500 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyFactory.java @@ -8,6 +8,7 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.common.MasterkeyFileAccess; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.VisibleForTesting; import javax.inject.Inject; import javax.inject.Singleton; @@ -58,7 +59,7 @@ public class RecoveryKeyFactory { } } - // visible for testing + @VisibleForTesting String createRecoveryKey(byte[] rawKey) { Preconditions.checkArgument(rawKey.length == 64, "key should be 64 bytes"); byte[] paddedKey = Arrays.copyOf(rawKey, 66); diff --git a/src/main/resources/fxml/vault_list.fxml b/src/main/resources/fxml/vault_list.fxml index 146fa877c..f9cb29258 100644 --- a/src/main/resources/fxml/vault_list.fxml +++ b/src/main/resources/fxml/vault_list.fxml @@ -28,27 +28,27 @@ - + + + + + + + + + + + + + + + + diff --git a/src/main/resources/i18n/strings_bg.properties b/src/main/resources/i18n/strings_bg.properties index ace7569d6..172b27b5c 100644 --- a/src/main/resources/i18n/strings_bg.properties +++ b/src/main/resources/i18n/strings_bg.properties @@ -153,6 +153,7 @@ hub.invalidLicense.message=Лиценза за Hub е недействителе # Lock ## Force +lock.forced.retryBtn=Повторен опит ## Failure # Migration diff --git a/src/main/resources/i18n/strings_zh_TW.properties b/src/main/resources/i18n/strings_zh_TW.properties index 0aede0ed4..3f2ee62fd 100644 --- a/src/main/resources/i18n/strings_zh_TW.properties +++ b/src/main/resources/i18n/strings_zh_TW.properties @@ -22,6 +22,9 @@ error.hyperlink.report=回報錯誤 error.technicalDetails=詳情: error.existingSolutionDescription=Cryptomator 沒有預料到會發生這種情況。但我們找到了一個現有的解決方案來解決這個錯誤。請查看以下連結。 error.hyperlink.solution=查詢解決方案 +error.lookupPermissionMessage=Cryptomator 可以在線查找此問題的解決方案。 這將從您的 IP 地址向我們的問題數據庫發送請求。 +error.dismiss=忽略 +error.lookUpSolution=查詢解決方案 # Defaults defaults.vault.vaultName=加密檔案庫 @@ -134,6 +137,7 @@ unlock.error.customPath.message=無法將檔案庫掛載至自訂路徑 unlock.error.customPath.description.notSupported=如果要繼續使用自訂的掛載路徑,必須變更成支援的磁區空間類型,不然就必須使用不同的掛載路徑 unlock.error.customPath.description.notExists=自訂的掛載路徑並不存在‧ 請在本機創立該路徑,或者在加密庫選項中更改 unlock.error.customPath.description.inUse=磁碟代號或自訂掛載路徑「%s」已被使用。 +unlock.error.customPath.description.hideawayNotDir=無法移除用於解鎖的臨時隱藏檔案「%3$s」。請檢查該檔案,然後手動刪除。 unlock.error.customPath.description.couldNotBeCleaned=無法將您的保險庫掛載至路徑「%s」。請再試一次或選擇不同的路徑。 unlock.error.customPath.description.notEmptyDir=自訂掛載路徑「%s」不是一個空資料夾。請選擇一個空資料夾並重試。 unlock.error.customPath.description.generic=您為此保險庫選擇了自訂掛載路徑,但使用時出現了錯誤訊息:%2$s