mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-19 05:36:02 +00:00
Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts: # pom.xml # src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java # src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java # src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java # src/main/resources/license/THIRD-PARTY.txt
This commit is contained in:
@@ -4,6 +4,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -28,6 +32,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
|
||||
@@ -43,13 +43,15 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
- name: Upload code coverage report
|
||||
id: codacyCoverageReporter
|
||||
if: "github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'pr:safe')"
|
||||
run: bash <(curl -Ls https://coverage.codacy.com/get.sh)
|
||||
- name: Sign source tarball with key 615D449FE6E6A235
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
git archive --prefix="cryptomator-${{ github.ref_name }}/" -o "cryptomator-${{ github.ref_name }}.tar.gz" ${{ github.ref }}
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
echo "${GPG_PASSPHRASE}" | gpg --batch --quiet --passphrase-fd 0 --pinentry-mode loopback -u 615D449FE6E6A235 --detach-sign -a cryptomator-*.tar.gz
|
||||
env:
|
||||
CODACY_PROJECT_TOKEN: ${{ secrets.CODACY_PROJECT_TOKEN }}
|
||||
continue-on-error: true
|
||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||
- name: Draft a release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v1
|
||||
@@ -58,6 +60,9 @@ jobs:
|
||||
discussion_category_name: releases
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
cryptomator-*.tar.gz.asc
|
||||
fail_on_unmatched_files: true
|
||||
body: |-
|
||||
:construction: Work in Progress
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@ on:
|
||||
required: true
|
||||
default: false
|
||||
type: boolean
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -38,6 +41,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
|
||||
@@ -4,6 +4,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -28,6 +32,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
|
||||
@@ -23,4 +23,4 @@ jobs:
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
cache: 'maven'
|
||||
- name: Build and Test
|
||||
run: mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage,dependency-check
|
||||
@@ -4,6 +4,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
required: false
|
||||
|
||||
env:
|
||||
JAVA_VERSION: 17
|
||||
@@ -33,6 +37,9 @@ jobs:
|
||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR=${GITHUB_REF##*/}
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
elif [[ "${{ github.event.inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
||||
mvn versions:set -DnewVersion=${SEM_VER_STR}
|
||||
else
|
||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||
fi
|
||||
@@ -80,6 +87,7 @@ jobs:
|
||||
--app-version "${{ steps.versions.outputs.semVerNum }}.${{ steps.versions.outputs.revNum }}"
|
||||
--java-options "-Xss5m"
|
||||
--java-options "-Xmx256m"
|
||||
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
|
||||
--java-options "-Dfile.encoding=\"utf-8\""
|
||||
--java-options "-Dcryptomator.logDir=\"~/AppData/Roaming/Cryptomator\""
|
||||
--java-options "-Dcryptomator.pluginDir=\"~/AppData/Roaming/Cryptomator/Plugins\""
|
||||
@@ -217,11 +225,11 @@ jobs:
|
||||
run: >
|
||||
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
|
||||
-ext WixBalExtension
|
||||
-out installer/unsigned/Cryptomator.exe
|
||||
-out installer/unsigned/Cryptomator-Installer.exe
|
||||
- name: Detach burn engine in preparation to sign
|
||||
run: >
|
||||
"${WIX}/bin/insignia.exe"
|
||||
-ib installer/unsigned/Cryptomator.exe
|
||||
-ib installer/unsigned/Cryptomator-Installer.exe
|
||||
-o tmp/engine.exe
|
||||
- name: Codesign burn engine
|
||||
uses: skymatic/code-sign-action@v1
|
||||
@@ -235,8 +243,8 @@ jobs:
|
||||
- name: Reattach signed burn engine to installer
|
||||
run : >
|
||||
"${WIX}/bin/insignia.exe"
|
||||
-ab tmp/engine.exe installer/unsigned/Cryptomator.exe
|
||||
-o installer/Cryptomator.exe
|
||||
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
|
||||
-o installer/Cryptomator-Installer.exe
|
||||
- name: Codesign EXE
|
||||
uses: skymatic/code-sign-action@v1
|
||||
with:
|
||||
@@ -247,7 +255,7 @@ jobs:
|
||||
timestampUrl: 'http://timestamp.digicert.com'
|
||||
folder: installer
|
||||
- name: Add possible alpha/beta tags to installer name
|
||||
run: mv installer/Cryptomator.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
|
||||
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
|
||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||
run: |
|
||||
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
|
||||
@@ -271,4 +279,44 @@ jobs:
|
||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||
files: |
|
||||
Cryptomator-*.exe
|
||||
Cryptomator-*.asc
|
||||
Cryptomator-*.asc
|
||||
|
||||
allowlist:
|
||||
name: Anti Virus Allowlisting
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-msi, build-exe]
|
||||
steps:
|
||||
- name: Download .msi
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: msi
|
||||
path: msi
|
||||
- name: Download .exe
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: exe
|
||||
path: exe
|
||||
- name: Collect files
|
||||
run: |
|
||||
mkdir files
|
||||
cp msi/*.msi files
|
||||
cp exe/*.exe files
|
||||
- name: Upload to Kaspersky
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.0
|
||||
with:
|
||||
protocol: ftps
|
||||
server: allowlist.kaspersky-labs.com
|
||||
port: 990
|
||||
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
|
||||
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
|
||||
local-dir: files/
|
||||
- name: Upload to Avast
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.0
|
||||
with:
|
||||
protocol: ftp
|
||||
server: whitelisting.avast.com
|
||||
port: 21
|
||||
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
||||
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
||||
local-dir: files/
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||
<module name="cryptomator" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator-Dev" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator-Dev/Plugins" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator-Dev" -Dfuse.experimental="true" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath="~/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="~/AppData/Roaming/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="~/AppData/Roaming/Cryptomator-Dev" -Dcryptomator.pluginDir="~/AppData/Roaming/Cryptomator-Dev/Plugins" -Dcryptomator.keychainPath="~/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.mountPointsDir="~/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://github.com/cryptomator/cryptomator/actions?query=workflow%3ABuild)
|
||||
[](https://snyk.io/test/github/cryptomator/cryptomator)
|
||||
[](https://www.codacy.com/gh/cryptomator/cryptomator/dashboard)
|
||||
[](https://sonarcloud.io/dashboard?id=cryptomator_cryptomator)
|
||||
[](http://twitter.com/Cryptomator)
|
||||
[](https://translate.cryptomator.org/)
|
||||
[](https://github.com/cryptomator/cryptomator/releases/latest)
|
||||
|
||||
+1
-1
@@ -7,5 +7,5 @@ Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Security;FileTools;
|
||||
StartupNotify=true
|
||||
StartupWMClass=org.cryptomator.launcher.Cryptomator
|
||||
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
|
||||
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release date="2022-03-30" version="1.6.8"/>
|
||||
<release date="2021-12-16" version="1.6.5"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
Vendored
+1
-1
@@ -140,4 +140,4 @@ Copy-Item ".\installer\Cryptomator-*.msi" -Destination ".\bundle\resources\Crypt
|
||||
-dAboutUrl="$aboutUrl" `
|
||||
-dHelpUrl="$helpUrl" `
|
||||
-dUpdateUrl="$updateUrl"
|
||||
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\CryptomatorBundle.exe
|
||||
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\Cryptomator-Installer.exe
|
||||
Vendored
+25
-2
@@ -13,7 +13,6 @@
|
||||
<DialogRef Id="BrowseDlg" />
|
||||
<DialogRef Id="DiskCostDlg" />
|
||||
<DialogRef Id="ErrorDlg" />
|
||||
<DialogRef Id="FatalError" />
|
||||
<DialogRef Id="FilesInUse" />
|
||||
<DialogRef Id="MsiRMFilesInUse" />
|
||||
<DialogRef Id="PrepareDlg" />
|
||||
@@ -24,8 +23,9 @@
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3">1</Publish>
|
||||
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4"><![CDATA[NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"]]></Publish>
|
||||
|
||||
<!-- custom end dialog -->
|
||||
<!-- custom end dialogs -->
|
||||
<Publish Dialog="MyExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999">1</Publish>
|
||||
<Publish Dialog="MyFatalErrorDlg" Control="Finish" Event="EndDialog" Value="Return" Order="998">1</Publish>
|
||||
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="LicenseAgreementDlg">NOT Installed</Publish>
|
||||
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg">Installed AND PATCH</Publish>
|
||||
@@ -75,11 +75,34 @@
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<!-- copy pasta from https://github.com/wixtoolset/wix3/blob/develop/src/ext/UIExtension/wixlib/FatalError.wxs with adjustments-->
|
||||
<Dialog Id="MyFatalErrorDlg" Width="370" Height="270" Title="!(loc.FatalError_Title)">
|
||||
<Control Id="Finish" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Cancel="yes" Text="!(loc.WixUIFinish)">
|
||||
<Publish Event="EndDialog" Value="Exit">1</Publish>
|
||||
</Control>
|
||||
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUICancel)" />
|
||||
<Control Id="Bitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="234" TabSkip="no" Text="!(loc.FatalErrorBitmap)" />
|
||||
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Disabled="yes" Text="!(loc.WixUIBack)" />
|
||||
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
|
||||
<Control Id="Title" Type="Text" X="135" Y="20" Width="220" Height="60" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorTitle)" />
|
||||
<Control Id="Description" Type="Text" X="135" Y="70" Width="220" Height="80" Transparent="yes" NoPrefix="yes" Text="!(loc.FatalErrorDescription1) !(loc.FatalErrorDescription2)" />
|
||||
<Control Id="DescriptionReason1" Type="Text" X="135" Y="160" Width="220" Height="20" Transparent="yes" NoPrefix="yes" Hidden="yes" >
|
||||
<Text>Reason:</Text>
|
||||
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
|
||||
</Control>
|
||||
<Control Id="DescriptionReason2" Type="Text" X="135" Y="170" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Hidden="yes" >
|
||||
<Text>Cryptomator was still running during installation.</Text>
|
||||
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
|
||||
</Control>
|
||||
</Dialog>
|
||||
|
||||
<InstallUISequence>
|
||||
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
|
||||
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
|
||||
</InstallUISequence>
|
||||
<AdminUISequence>
|
||||
<Show Dialog="MyExitDialog" Overridable="yes" OnExit="success"/>
|
||||
<Show Dialog="MyFatalErrorDlg" Overridable="yes" OnExit="error"/>
|
||||
</AdminUISequence>
|
||||
|
||||
</UI>
|
||||
|
||||
Vendored
+20
-1
@@ -127,6 +127,20 @@
|
||||
<!-- WebDAV patches -->
|
||||
<CustomAction Id="PatchWebDAV" Impersonate="no" ExeCommand="[INSTALLDIR]patchWebDAV.bat" Directory="INSTALLDIR" Execute="deferred" Return="asyncWait" />
|
||||
|
||||
<!-- Running App detection and exit -->
|
||||
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
||||
<util:CloseApplication
|
||||
Id="CloseCryptomator"
|
||||
Target="cryptomator.exe"
|
||||
CloseMessage="no"
|
||||
RebootPrompt="no"
|
||||
PromptToContinue="yes"
|
||||
Description="A running instance of Cryptomator is found. Please close it to continue."
|
||||
Property="FOUNDRUNNINGAPP"
|
||||
>
|
||||
</util:CloseApplication>
|
||||
<CustomAction Id="FailOnRunningApp" Impersonate="no" ExeCommand="[SystemFolder]\cmd.exe /c "exit 1"" Directory="INSTALLDIR" Execute="immediate" Return="check" />
|
||||
|
||||
<?ifdef JpIcon ?>
|
||||
<Property Id="ARPPRODUCTICON" Value="JpARPPRODUCTICON"/>
|
||||
<Icon Id="JpARPPRODUCTICON" SourceFile="$(var.JpIcon)"/>
|
||||
@@ -155,7 +169,12 @@
|
||||
<?ifndef JpAllowDowngrades ?>
|
||||
<Custom Action="JpDisallowDowngrade" After="FindRelatedProducts">JP_DOWNGRADABLE_FOUND</Custom>
|
||||
<?endif?>
|
||||
<RemoveExistingProducts Before="CostInitialize"/>
|
||||
|
||||
<!-- Check and fail if Cryptomator is running -->
|
||||
<Custom Action="WixCloseApplications" Before="InstallValidate"></Custom>
|
||||
<Custom Action="FailOnRunningApp" After="WixCloseApplications" >FOUNDRUNNINGAPP</Custom>
|
||||
|
||||
<RemoveExistingProducts After="InstallValidate"/>
|
||||
|
||||
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||
</InstallExecuteSequence>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<nonModularGroupIds>com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
|
||||
|
||||
<!-- cryptomator dependencies -->
|
||||
<cryptomator.cryptofs.version>2.3.1</cryptomator.cryptofs.version>
|
||||
<cryptomator.cryptofs.version>2.4.0</cryptomator.cryptofs.version>
|
||||
<cryptomator.integrations.version>1.1.0-beta2</cryptomator.integrations.version>
|
||||
<cryptomator.integrations.win.version>1.1.0-beta1</cryptomator.integrations.win.version>
|
||||
<cryptomator.integrations.mac.version>1.1.0-beta1</cryptomator.integrations.mac.version>
|
||||
@@ -39,7 +39,7 @@
|
||||
<!-- 3rd party dependencies -->
|
||||
<javafx.version>18</javafx.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<jwt.version>3.19.0</jwt.version>
|
||||
<jwt.version>3.19.1</jwt.version>
|
||||
<easybind.version>2.2</easybind.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
<dagger.version>2.41</dagger.version>
|
||||
@@ -53,8 +53,9 @@
|
||||
<mockito.version>4.4.0</mockito.version>
|
||||
<hamcrest.version>2.2</hamcrest.version>
|
||||
|
||||
<!-- build plugin dependencies -->
|
||||
<dependency-check.version>7.0.0</dependency-check.version>
|
||||
<!-- build-time dependencies -->
|
||||
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
|
||||
<dependency-check.version>7.0.2</dependency-check.version>
|
||||
<jacoco.version>0.8.7</jacoco.version>
|
||||
</properties>
|
||||
|
||||
@@ -224,6 +225,13 @@
|
||||
<version>1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>${jetbrains.annotations.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -2,6 +2,8 @@ import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
|
||||
|
||||
module org.cryptomator.desktop {
|
||||
requires static org.jetbrains.annotations;
|
||||
|
||||
requires org.cryptomator.cryptofs;
|
||||
requires org.cryptomator.frontend.dokany;
|
||||
requires org.cryptomator.frontend.fuse;
|
||||
@@ -31,6 +33,8 @@ module org.cryptomator.desktop {
|
||||
|
||||
opens org.cryptomator.common.settings to com.google.gson;
|
||||
|
||||
opens org.cryptomator.launcher to javafx.graphics;
|
||||
|
||||
opens org.cryptomator.common to javafx.fxml;
|
||||
opens org.cryptomator.common.vaults to javafx.fxml;
|
||||
opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
|
||||
|
||||
@@ -89,11 +89,6 @@ public class Environment {
|
||||
return Boolean.getBoolean("cryptomator.showTrayIcon");
|
||||
}
|
||||
|
||||
@Deprecated // TODO: remove as soon as custom mount path works properly on Win+Fuse
|
||||
public boolean useExperimentalFuse() {
|
||||
return Boolean.getBoolean("fuse.experimental");
|
||||
}
|
||||
|
||||
private int getInt(String propertyName, int defaultValue) {
|
||||
String value = System.getProperty(propertyName);
|
||||
try {
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Optional;
|
||||
|
||||
class CustomMountPointChooser implements MountPointChooser {
|
||||
@@ -29,17 +30,15 @@ class CustomMountPointChooser implements MountPointChooser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CustomMountPointChooser.class);
|
||||
|
||||
private final VaultSettings vaultSettings;
|
||||
private final Environment environment;
|
||||
|
||||
@Inject
|
||||
public CustomMountPointChooser(VaultSettings vaultSettings, Environment environment) {
|
||||
public CustomMountPointChooser(VaultSettings vaultSettings) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Volume caller) {
|
||||
return caller.getImplementationType() != VolumeImpl.FUSE || !SystemUtils.IS_OS_WINDOWS || environment.useExperimentalFuse();
|
||||
return caller.getImplementationType() != VolumeImpl.WEBDAV;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface DefaultMountFlags {
|
||||
@interface DefaultMountFlags {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection<Path> pathsToOpen) {
|
||||
|
||||
public enum EventType {
|
||||
REVEAL_APP,
|
||||
OPEN_FILE
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,17 +13,15 @@ import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.ipc.IpcCommunicator;
|
||||
import org.cryptomator.logging.DebugMode;
|
||||
import org.cryptomator.logging.LoggerConfiguration;
|
||||
import org.cryptomator.ui.launcher.UiLauncher;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.IOException;
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Singleton
|
||||
@@ -38,19 +36,15 @@ public class Cryptomator {
|
||||
private final DebugMode debugMode;
|
||||
private final Environment env;
|
||||
private final Lazy<IpcMessageHandler> ipcMessageHandler;
|
||||
private final CountDownLatch shutdownLatch;
|
||||
private final ShutdownHook shutdownHook;
|
||||
private final Lazy<UiLauncher> uiLauncher;
|
||||
|
||||
@Inject
|
||||
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
|
||||
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
|
||||
this.logConfig = logConfig;
|
||||
this.debugMode = debugMode;
|
||||
this.env = env;
|
||||
this.ipcMessageHandler = ipcMessageHandler;
|
||||
this.shutdownLatch = shutdownLatch;
|
||||
this.shutdownHook = shutdownHook;
|
||||
this.uiLauncher = uiLauncher;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -79,7 +73,7 @@ public class Cryptomator {
|
||||
communicator.sendHandleLaunchargs(List.of(args));
|
||||
communicator.sendRevealRunningApp();
|
||||
LOG.info("Found running application instance. Shutting down...");
|
||||
return 2;
|
||||
return 0;
|
||||
} else {
|
||||
shutdownHook.runOnShutdown(communicator::closeUnchecked);
|
||||
var executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("IPC-%d").build());
|
||||
@@ -96,21 +90,38 @@ public class Cryptomator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches the JavaFX application and waits until shutdown is requested.
|
||||
* Launches the JavaFX application, blocking the main thread until shuts down.
|
||||
*
|
||||
* @return Nonzero exit code in case of an error.
|
||||
* @implNote This method blocks until {@link #shutdownLatch} reached zero.
|
||||
*/
|
||||
private int runGuiApplication() {
|
||||
try {
|
||||
uiLauncher.get().launch();
|
||||
shutdownLatch.await();
|
||||
Application.launch(MainApp.class);
|
||||
LOG.info("UI shut down");
|
||||
return 0;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Terminating due to error", e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MainApp extends Application {
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
LOG.info("JavaFX application started.");
|
||||
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
|
||||
.fxApplication(this) //
|
||||
.primaryStage(primaryStage) //
|
||||
.build();
|
||||
component.application().start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
LOG.info("JavaFX application stopped.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,14 +3,16 @@ package org.cryptomator.launcher;
|
||||
import dagger.Component;
|
||||
import org.cryptomator.common.CommonsModule;
|
||||
import org.cryptomator.logging.LoggerModule;
|
||||
import org.cryptomator.ui.launcher.UiLauncherModule;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class})
|
||||
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
|
||||
public interface CryptomatorComponent {
|
||||
|
||||
Cryptomator application();
|
||||
|
||||
FxApplicationComponent.Builder fxAppComponentBuilder();
|
||||
|
||||
}
|
||||
|
||||
@@ -2,20 +2,50 @@ package org.cryptomator.launcher;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
@Module
|
||||
@Module(subcomponents = {FxApplicationComponent.class})
|
||||
class CryptomatorModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("shutdownLatch")
|
||||
static CountDownLatch provideShutdownLatch() {
|
||||
return new CountDownLatch(1);
|
||||
static ResourceBundle provideLocalization() {
|
||||
return ResourceBundle.getBundle("i18n.strings");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("launchEventQueue")
|
||||
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
||||
return new ArrayBlockingQueue<>(10);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||
return UiAppearanceProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<AutoStartProvider> provideAutostartProvider() {
|
||||
return AutoStartProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
|
||||
return TrayIntegrationProvider.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.ui.launcher.AppLaunchEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
class FileOpenRequestHandler {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.launcher;
|
||||
|
||||
import org.cryptomator.ipc.IpcMessageListener;
|
||||
import org.cryptomator.ui.launcher.AppLaunchEvent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
|
||||
|
||||
import javax.inject.Named;
|
||||
@@ -43,12 +43,12 @@ public abstract class AddVaultModule {
|
||||
@Provides
|
||||
@AddVaultWizardWindow
|
||||
@AddVaultWizardScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.initOwner(primaryStage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,25 +2,24 @@ package org.cryptomator.ui.addvaultwizard;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
|
||||
@AddVaultWizardScoped
|
||||
public class AddVaultSuccessController implements FxController {
|
||||
|
||||
private final FxApplication fxApplication;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Stage window;
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
|
||||
@Inject
|
||||
AddVaultSuccessController(FxApplication fxApplication, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
|
||||
this.fxApplication = fxApplication;
|
||||
AddVaultSuccessController(FxApplicationWindows appWindows, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
|
||||
this.appWindows = appWindows;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
}
|
||||
@@ -28,7 +27,7 @@ public class AddVaultSuccessController implements FxController {
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
fxApplication.startUnlockWorkflow(vault.get(), Optional.of(window));
|
||||
appWindows.startUnlockWorkflow(vault.get(), window);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -4,10 +4,10 @@ import dagger.Lazy;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -20,9 +20,6 @@ import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@@ -34,7 +31,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
private final Stage window;
|
||||
private final Lazy<Scene> welcomeScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final ObjectProperty<Path> vaultPath;
|
||||
private final ObjectProperty<Vault> vault;
|
||||
private final VaultListManager vaultListManager;
|
||||
@@ -43,11 +40,11 @@ public class ChooseExistingVaultController implements FxController {
|
||||
private Image screenshot;
|
||||
|
||||
@Inject
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
|
||||
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
|
||||
this.window = window;
|
||||
this.welcomeScene = welcomeScene;
|
||||
this.successScene = successScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultPath = vaultPath;
|
||||
this.vault = vault;
|
||||
this.vaultListManager = vaultListManager;
|
||||
@@ -82,7 +79,7 @@ public class ChooseExistingVaultController implements FxController {
|
||||
window.setScene(successScene.get());
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to open existing vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -10,12 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.cryptolib.api.MasterkeyLoader;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
|
||||
import org.slf4j.Logger;
|
||||
@@ -60,7 +60,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
private final Lazy<Scene> chooseLocationScene;
|
||||
private final Lazy<Scene> recoveryKeyScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final ExecutorService executor;
|
||||
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||
private final StringProperty vaultNameProperty;
|
||||
@@ -83,12 +83,12 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
public NewPasswordController newPasswordSceneController;
|
||||
|
||||
@Inject
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.chooseLocationScene = chooseLocationScene;
|
||||
this.recoveryKeyScene = recoveryKeyScene;
|
||||
this.successScene = successScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.executor = executor;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
this.vaultNameProperty = vaultName;
|
||||
@@ -127,7 +127,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
Files.createDirectory(pathToVault);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to create vault directory.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
window.setScene(recoveryKeyScene.get());
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Failed to initialize vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}).andFinally(() -> {
|
||||
processing.set(false);
|
||||
}).runOnce(executor);
|
||||
@@ -168,7 +168,7 @@ public class CreateNewVaultPasswordController implements FxController {
|
||||
window.setScene(successScene.get());
|
||||
}).onError(IOException.class, e -> {
|
||||
LOG.error("Failed to initialize vault.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}).andFinally(() -> {
|
||||
processing.set(false);
|
||||
}).runOnce(executor);
|
||||
|
||||
@@ -8,10 +8,10 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
|
||||
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
|
||||
@@ -38,9 +37,8 @@ public class ChangePasswordController implements FxController {
|
||||
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final KeychainManager keychain;
|
||||
private final SecureRandom csprng;
|
||||
private final MasterkeyFileAccess masterkeyFileAccess;
|
||||
|
||||
public NiceSecurePasswordField oldPasswordField;
|
||||
@@ -49,12 +47,11 @@ public class ChangePasswordController implements FxController {
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, FxApplicationWindows appWindows, KeychainManager keychain, MasterkeyFileAccess masterkeyFileAccess) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.keychain = keychain;
|
||||
this.csprng = csprng;
|
||||
this.masterkeyFileAccess = masterkeyFileAccess;
|
||||
}
|
||||
|
||||
@@ -95,7 +92,7 @@ public class ChangePasswordController implements FxController {
|
||||
oldPasswordField.requestFocus();
|
||||
} catch (IOException | CryptoException e) {
|
||||
LOG.error("Password change failed. Unable to perform operation.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.Nullable;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@@ -16,34 +15,17 @@ public interface ErrorComponent {
|
||||
@FxmlScene(FxmlFile.ERROR)
|
||||
Scene scene();
|
||||
|
||||
default void showErrorScene() {
|
||||
if (Platform.isFxApplicationThread()) {
|
||||
show();
|
||||
} else {
|
||||
Platform.runLater(this::show);
|
||||
}
|
||||
}
|
||||
|
||||
private void show() {
|
||||
default Stage show() {
|
||||
Stage stage = window();
|
||||
stage.setScene(scene());
|
||||
stage.show();
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder cause(Throwable cause);
|
||||
|
||||
@BindsInstance
|
||||
Builder window(Stage window);
|
||||
|
||||
@BindsInstance
|
||||
Builder returnToScene(@Nullable Scene previousScene);
|
||||
|
||||
ErrorComponent build();
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
|
||||
ErrorComponent create(@BindsInstance Throwable cause, @BindsInstance Stage window, @BindsInstance @Nullable Scene previousScene);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class StageFactory {
|
||||
|
||||
private final Consumer<Stage> initializer;
|
||||
|
||||
public StageFactory(Consumer<Stage> initializer) {
|
||||
@Inject
|
||||
public StageFactory(StageInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
}
|
||||
|
||||
public Stage create() {
|
||||
return create(StageStyle.DECORATED);
|
||||
}
|
||||
|
||||
public Stage create(StageStyle stageStyle) {
|
||||
Stage stage = new Stage(stageStyle);
|
||||
Stage stage = new Stage(StageStyle.DECORATED);
|
||||
initializer.accept(stage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.cryptomator.ui.common;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Performs common setup for all stages
|
||||
*/
|
||||
@FxApplicationScoped
|
||||
public class StageInitializer implements Consumer<Stage> {
|
||||
|
||||
private final List<Image> windowIcons;
|
||||
|
||||
@Inject
|
||||
public StageInitializer() {
|
||||
this.windowIcons = SystemUtils.IS_OS_MAC ? List.of() : List.of( //
|
||||
new Image(StageInitializer.class.getResource("/img/window_icon_32.png").toString()), //
|
||||
new Image(StageInitializer.class.getResource("/img/window_icon_512.png").toString()) //
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(Stage stage) {
|
||||
stage.getIcons().setAll(windowIcons);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
@@ -53,7 +54,9 @@ public class VaultService {
|
||||
*
|
||||
* @param vault The vault to lock
|
||||
* @param forced Whether to attempt a forced lock
|
||||
* @deprecated use {@link org.cryptomator.ui.fxapp.FxApplicationWindows#startLockWorkflow(Vault, Stage)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void lock(Vault vault, boolean forced) {
|
||||
executorService.execute(createLockTask(vault, forced));
|
||||
}
|
||||
@@ -90,7 +93,7 @@ public class VaultService {
|
||||
* @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask
|
||||
*/
|
||||
public Task<Collection<Vault>> createLockAllTask(Collection<Vault> vaults, boolean forced) {
|
||||
List<Task<Vault>> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList());
|
||||
List<Task<Vault>> lockTasks = vaults.stream().<Task<Vault>>map(v -> new LockVaultTask(v, forced)).toList();
|
||||
lockTasks.forEach(executorService::execute);
|
||||
Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks);
|
||||
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
|
||||
|
||||
+16
-15
@@ -1,14 +1,14 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.launcher.AppLaunchEvent;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.application.Platform;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@@ -17,22 +17,25 @@ import java.util.concurrent.ExecutorService;
|
||||
|
||||
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
|
||||
|
||||
@Singleton
|
||||
// TODO: use message bus
|
||||
@FxApplicationScoped
|
||||
class AppLaunchEventHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class);
|
||||
|
||||
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
|
||||
private final ExecutorService executorService;
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultListManager vaultListManager;
|
||||
private final VaultService vaultService;
|
||||
|
||||
@Inject
|
||||
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationStarter fxApplicationStarter, VaultListManager vaultListManager) {
|
||||
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
|
||||
this.launchEventQueue = launchEventQueue;
|
||||
this.executorService = executorService;
|
||||
this.fxApplicationStarter = fxApplicationStarter;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultListManager = vaultListManager;
|
||||
this.vaultService = vaultService;
|
||||
}
|
||||
|
||||
public void startHandlingLaunchEvents() {
|
||||
@@ -52,14 +55,12 @@ class AppLaunchEventHandler {
|
||||
}
|
||||
|
||||
private void handleLaunchEvent(AppLaunchEvent event) {
|
||||
switch (event.getType()) {
|
||||
case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow);
|
||||
case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> {
|
||||
Platform.runLater(() -> {
|
||||
event.getPathsToOpen().forEach(this::addOrRevealVault);
|
||||
});
|
||||
switch (event.type()) {
|
||||
case REVEAL_APP -> appWindows.showMainWindow();
|
||||
case OPEN_FILE -> Platform.runLater(() -> {
|
||||
event.pathsToOpen().forEach(this::addOrRevealVault);
|
||||
});
|
||||
default -> LOG.warn("Unsupported event type: {}", event.getType());
|
||||
default -> LOG.warn("Unsupported event type: {}", event.type());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +76,7 @@ class AppLaunchEventHandler {
|
||||
}
|
||||
|
||||
if (v.isUnlocked()) {
|
||||
fxApplicationStarter.get().thenAccept(app -> app.getVaultService().reveal(v));
|
||||
vaultService.reveal(v);
|
||||
}
|
||||
LOG.debug("Added vault {}", potentialVaultPath);
|
||||
} catch (IOException e) {
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class AutoUnlocker {
|
||||
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
@Inject
|
||||
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows) {
|
||||
this.vaults = vaults;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
vaults.stream().filter(Vault::isLocked).filter(v -> v.getVaultSettings().unlockAfterStartup().get()).forEach(v -> {
|
||||
appWindows.startUnlockWorkflow(v, null);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
|
||||
record ExitingQuitResponse(QuitResponse delegate) implements QuitResponse {
|
||||
|
||||
@Override
|
||||
public void performQuit() {
|
||||
Platform.exit();
|
||||
// TODO wait a moment for javafx to terminate?
|
||||
delegate.performQuit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
delegate.cancelQuit();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,213 +1,70 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Lazy;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.Theme;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.StageStyle;
|
||||
import java.awt.SystemTray;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplication extends Application {
|
||||
public class FxApplication {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
|
||||
|
||||
private final Settings settings;
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final Lazy<QuitComponent> quitWindow;
|
||||
private final Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider;
|
||||
private final Provider<LockComponent.Builder> lockWorkflowBuilderProvider;
|
||||
private final ErrorComponent.Builder errorWindowBuilder;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Optional<UiAppearanceProvider> appearanceProvider;
|
||||
private final VaultService vaultService;
|
||||
private final LicenseHolder licenseHolder;
|
||||
private final ObservableList<Window> visibleWindows;
|
||||
private final BooleanBinding hasVisibleWindows;
|
||||
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
|
||||
private final AppLaunchEventHandler launchEventHandler;
|
||||
private final Lazy<TrayMenuComponent> trayMenu;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final FxApplicationStyle applicationStyle;
|
||||
private final FxApplicationTerminator applicationTerminator;
|
||||
private final AutoUnlocker autoUnlocker;
|
||||
|
||||
@Inject
|
||||
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
|
||||
FxApplication(Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
|
||||
this.settings = settings;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
|
||||
this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
|
||||
this.quitWindow = quitWindow;
|
||||
this.errorWindowBuilder = errorWindowBuilder;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.appearanceProvider = appearanceProvider;
|
||||
this.vaultService = vaultService;
|
||||
this.licenseHolder = licenseHolder;
|
||||
this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
|
||||
this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
|
||||
this.launchEventHandler = launchEventHandler;
|
||||
this.trayMenu = trayMenu;
|
||||
this.appWindows = appWindows;
|
||||
this.applicationStyle = applicationStyle;
|
||||
this.applicationTerminator = applicationTerminator;
|
||||
this.autoUnlocker = autoUnlocker;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
LOG.trace("FxApplication.start()");
|
||||
Platform.setImplicitExit(false);
|
||||
applicationStyle.initialize();
|
||||
appWindows.initialize();
|
||||
applicationTerminator.initialize();
|
||||
|
||||
hasVisibleWindows.addListener(this::hasVisibleStagesChanged);
|
||||
|
||||
settings.theme().addListener(this::appThemeChanged);
|
||||
loadSelectedStyleSheet(settings.theme().get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) {
|
||||
throw new UnsupportedOperationException("Use start() instead.");
|
||||
}
|
||||
|
||||
private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
|
||||
LOG.debug("has visible stages: {}", newValue);
|
||||
if (newValue) {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
|
||||
// init system tray
|
||||
final boolean hasTrayIcon;
|
||||
if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
|
||||
trayMenu.get().initializeTrayIcon();
|
||||
Platform.setImplicitExit(false); // don't quit when closing all windows
|
||||
hasTrayIcon = true;
|
||||
} else {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
|
||||
hasTrayIcon = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void showPreferencesWindow(SelectedPreferencesTab selectedTab) {
|
||||
Platform.runLater(() -> {
|
||||
preferencesWindow.get().showPreferencesWindow(selectedTab);
|
||||
LOG.debug("Showing Preferences");
|
||||
});
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showMainWindow() {
|
||||
CompletableFuture<Stage> future = new CompletableFuture<>();
|
||||
Platform.runLater(() -> {
|
||||
var win = mainWindow.get().showMainWindow();
|
||||
LOG.debug("Showing MainWindow");
|
||||
future.complete(win);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
|
||||
unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
|
||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
|
||||
// show main window
|
||||
appWindows.showMainWindow().thenAccept(stage -> {
|
||||
if (settings.startHidden().get()) {
|
||||
if (hasTrayIcon) {
|
||||
stage.hide();
|
||||
} else {
|
||||
stage.setIconified(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
|
||||
Platform.runLater(() -> {
|
||||
if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
|
||||
lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
} else {
|
||||
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showQuitWindow(QuitResponse response) {
|
||||
Platform.runLater(() -> {
|
||||
quitWindow.get().showQuitWindow(response);
|
||||
LOG.debug("Showing QuitWindow");
|
||||
});
|
||||
}
|
||||
|
||||
public VaultService getVaultService() {
|
||||
return vaultService;
|
||||
}
|
||||
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
||||
try {
|
||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to disable automatic theme switching.");
|
||||
}
|
||||
}
|
||||
loadSelectedStyleSheet(newValue);
|
||||
}
|
||||
|
||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
case AUTOMATIC -> {
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
try {
|
||||
appearanceProvider.addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to enable automatic theme switching.");
|
||||
}
|
||||
});
|
||||
applySystemTheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme theme) {
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applySystemTheme() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
||||
} else {
|
||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
||||
applyLightTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLightTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
appearanceProvider.adjustToTheme(Theme.LIGHT);
|
||||
});
|
||||
}
|
||||
|
||||
private void applyDarkTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
|
||||
appearanceProvider.ifPresent(appearanceProvider -> {
|
||||
appearanceProvider.adjustToTheme(Theme.DARK);
|
||||
});
|
||||
launchEventHandler.startHandlingLaunchEvents();
|
||||
autoUnlocker.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@FxApplicationScoped
|
||||
@Subcomponent(modules = FxApplicationModule.class)
|
||||
public interface FxApplicationComponent {
|
||||
@@ -16,6 +20,12 @@ public interface FxApplicationComponent {
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder fxApplication(Application application);
|
||||
|
||||
@BindsInstance
|
||||
Builder primaryStage(@PrimaryStage Stage primaryStage);
|
||||
|
||||
FxApplicationComponent build();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
@@ -15,67 +14,46 @@ import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
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 javax.inject.Named;
|
||||
import javafx.application.Application;
|
||||
import javafx.collections.ObservableSet;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
|
||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
|
||||
abstract class FxApplicationModule {
|
||||
|
||||
@Provides
|
||||
@Named("windowIcons")
|
||||
@FxApplicationScoped
|
||||
static List<Image> provideWindowIcons() {
|
||||
if (SystemUtils.IS_OS_MAC) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
return List.of( //
|
||||
createImageFromResource("/img/window_icon_32.png"), //
|
||||
createImageFromResource("/img/window_icon_512.png") //
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Failed to load embedded resource.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static StageFactory provideStageFactory(@Named("windowIcons") List<Image> windowIcons) {
|
||||
return new StageFactory(stage -> {
|
||||
stage.getIcons().addAll(windowIcons);
|
||||
});
|
||||
}
|
||||
|
||||
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
|
||||
return new Image(in);
|
||||
}
|
||||
}
|
||||
|
||||
@Binds
|
||||
abstract Application bindApplication(FxApplication application);
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@FxApplicationScoped
|
||||
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.integrations.uiappearance.Theme;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import java.util.Optional;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationStyle {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStyle.class);
|
||||
|
||||
private final Settings settings;
|
||||
private final Optional<UiAppearanceProvider> appearanceProvider;
|
||||
private final LicenseHolder licenseHolder;
|
||||
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
|
||||
|
||||
@Inject
|
||||
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder){
|
||||
this.settings = settings;
|
||||
this.appearanceProvider = appearanceProvider;
|
||||
this.licenseHolder = licenseHolder;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
settings.theme().addListener(this::appThemeChanged);
|
||||
loadSelectedStyleSheet(settings.theme().get());
|
||||
}
|
||||
|
||||
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
|
||||
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
|
||||
try {
|
||||
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to disable automatic theme switching.");
|
||||
}
|
||||
}
|
||||
loadSelectedStyleSheet(newValue);
|
||||
}
|
||||
|
||||
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
|
||||
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
case AUTOMATIC -> {
|
||||
appearanceProvider.ifPresent(provider -> {
|
||||
try {
|
||||
provider.addListener(systemInterfaceThemeListener);
|
||||
} catch (UiAppearanceException e) {
|
||||
LOG.error("Failed to enable automatic theme switching.");
|
||||
}
|
||||
});
|
||||
applySystemTheme();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void systemInterfaceThemeChanged(Theme theme) {
|
||||
switch (theme) {
|
||||
case LIGHT -> applyLightTheme();
|
||||
case DARK -> applyDarkTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applySystemTheme() {
|
||||
if (appearanceProvider.isPresent()) {
|
||||
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
|
||||
} else {
|
||||
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
|
||||
applyLightTheme();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyLightTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
|
||||
}
|
||||
|
||||
private void applyDarkTheme() {
|
||||
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
|
||||
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.awt.desktop.QuitStrategy;
|
||||
import java.util.EnumSet;
|
||||
import java.util.EventObject;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationTerminator {
|
||||
|
||||
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
|
||||
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final ShutdownHook shutdownHook;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final AtomicBoolean allowQuitWithoutPrompt = new AtomicBoolean();
|
||||
|
||||
@Inject
|
||||
public FxApplicationTerminator(ObservableList<Vault> vaults, ShutdownHook shutdownHook, FxApplicationWindows appWindows) {
|
||||
this.vaults = vaults;
|
||||
this.shutdownHook = shutdownHook;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
|
||||
// register quit handler
|
||||
if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
|
||||
desktop.setQuitHandler(this::handleQuitRequest);
|
||||
}
|
||||
|
||||
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
|
||||
if (desktop.isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
|
||||
desktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
|
||||
}
|
||||
|
||||
// allow sudden termination?
|
||||
vaultListChanged(vaults);
|
||||
vaults.addListener(this::vaultListChanged);
|
||||
|
||||
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully terminates the application.
|
||||
*/
|
||||
public void terminate() {
|
||||
handleQuitRequest(null, new NoopQuitResponse());
|
||||
}
|
||||
|
||||
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
||||
boolean allowSuddenTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
|
||||
boolean stateChanged = allowQuitWithoutPrompt.compareAndSet(!allowSuddenTermination, allowSuddenTermination);
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
if (stateChanged && desktop.isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
|
||||
if (allowSuddenTermination) {
|
||||
LOG.debug("Enabling sudden termination");
|
||||
desktop.enableSuddenTermination();
|
||||
} else {
|
||||
LOG.debug("Disabling sudden termination");
|
||||
desktop.disableSuddenTermination();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the app to quit. If confirmed, the JavaFX application will exit before giving a {@code response}.
|
||||
*
|
||||
* @param e ignored
|
||||
* @param response a quit response that will be {@link ExitingQuitResponse decorated in order to exit the JavaFX application}.
|
||||
*/
|
||||
private void handleQuitRequest(@SuppressWarnings("unused") @Nullable EventObject e, QuitResponse response) {
|
||||
var exitingResponse = new ExitingQuitResponse(response);
|
||||
if (allowQuitWithoutPrompt.get()) {
|
||||
exitingResponse.performQuit();
|
||||
} else {
|
||||
appWindows.showQuitWindow(exitingResponse);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceUnmountRemainingVaults() {
|
||||
for (Vault vault : vaults) {
|
||||
if (vault.isUnlocked()) {
|
||||
try {
|
||||
vault.lock(true);
|
||||
} catch (Volume.VolumeException e) {
|
||||
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
||||
} catch (LockNotCompletedException e) {
|
||||
LOG.error("Failed to lock vault " + vault.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy QuitResponse that ignores the response.
|
||||
*
|
||||
* To be used with {@link #handleQuitRequest(EventObject, QuitResponse)} if the invoking method is not interested in the response.
|
||||
*/
|
||||
private static class NoopQuitResponse implements QuitResponse {
|
||||
|
||||
@Override
|
||||
public void performQuit() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.lock.LockComponent;
|
||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||
import org.cryptomator.ui.preferences.PreferencesComponent;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.quit.QuitComponent;
|
||||
import org.cryptomator.ui.unlock.UnlockComponent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import javafx.stage.Window;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.desktop.AppReopenedListener;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@FxApplicationScoped
|
||||
public class FxApplicationWindows {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationWindows.class);
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
private final Lazy<MainWindowComponent> mainWindow;
|
||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||
private final Lazy<QuitComponent> quitWindow;
|
||||
private final UnlockComponent.Factory unlockWorkflowFactory;
|
||||
private final LockComponent.Factory lockWorkflowFactory;
|
||||
private final ErrorComponent.Factory errorWindowFactory;
|
||||
private final ExecutorService executor;
|
||||
private final FilteredList<Window> visibleWindows;
|
||||
|
||||
@Inject
|
||||
public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional<TrayIntegrationProvider> trayIntegration, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Lazy<QuitComponent> quitWindow, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.trayIntegration = trayIntegration;
|
||||
this.mainWindow = mainWindow;
|
||||
this.preferencesWindow = preferencesWindow;
|
||||
this.quitWindow = quitWindow;
|
||||
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||
this.errorWindowFactory = errorWindowFactory;
|
||||
this.executor = executor;
|
||||
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
|
||||
// register preferences shortcut
|
||||
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
|
||||
desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||
}
|
||||
|
||||
// register preferences shortcut
|
||||
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
|
||||
desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
|
||||
}
|
||||
|
||||
// register app reopen listener
|
||||
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
|
||||
desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
|
||||
}
|
||||
|
||||
// observe visible windows
|
||||
if (trayIntegration.isPresent()) {
|
||||
visibleWindows.addListener(this::visibleWindowsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private void visibleWindowsChanged(ListChangeListener.Change<? extends Window> change) {
|
||||
int visibleWindows = change.getList().size();
|
||||
LOG.debug("visible windows: {}", visibleWindows);
|
||||
if (visibleWindows > 0) {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
|
||||
} else {
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showMainWindow() {
|
||||
return CompletableFuture.supplyAsync(mainWindow.get()::showMainWindow, Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showPreferencesWindow(SelectedPreferencesTab selectedTab) {
|
||||
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public CompletionStage<Stage> showQuitWindow(QuitResponse response) {
|
||||
return CompletableFuture.supplyAsync(() -> quitWindow.get().showQuitWindow(response), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
|
||||
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
|
||||
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
|
||||
}, Platform::runLater) //
|
||||
.thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
|
||||
.exceptionally(e -> {
|
||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public CompletionStage<Void> startLockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING), "Vault not unlocked.");
|
||||
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
|
||||
return lockWorkflowFactory.create(vault, owner).lockWorkflow();
|
||||
}, Platform::runLater) //
|
||||
.thenCompose(lockWorkflow -> CompletableFuture.runAsync(lockWorkflow, executor)) //
|
||||
.exceptionally(e -> {
|
||||
showErrorWindow(e, owner == null ? primaryStage : owner, null);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the generic error scene in the given window.
|
||||
*
|
||||
* @param cause The exception to show
|
||||
* @param window What window to display the scene in
|
||||
* @param previousScene To what scene to return to when pressing "back". Back button will be hidden, if <code>null</code>
|
||||
* @return A
|
||||
*/
|
||||
public CompletionStage<Stage> showErrorWindow(Throwable cause, Stage window, @Nullable Scene previousScene) {
|
||||
return CompletableFuture.supplyAsync(() -> errorWindowFactory.create(cause, window, previousScene).show(), Platform::runLater).whenComplete(this::reportErrors);
|
||||
}
|
||||
|
||||
private void reportErrors(@Nullable Stage stage, @Nullable Throwable error) {
|
||||
if (error != null) {
|
||||
LOG.error("Failed to display stage", error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.cryptomator.ui.fxapp;
|
||||
|
||||
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)
|
||||
public @interface PrimaryStage {
|
||||
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package org.cryptomator.ui.health;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -15,9 +14,7 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.ListView;
|
||||
import javafx.scene.control.SelectionMode;
|
||||
import javafx.stage.Stage;
|
||||
@@ -37,7 +34,7 @@ public class CheckListController implements FxController {
|
||||
private final ObjectProperty<Check> selectedCheck;
|
||||
private final BooleanBinding mainRunStarted; //TODO: rerunning not considered for now
|
||||
private final BooleanBinding somethingsRunning;
|
||||
private final Lazy<ErrorComponent.Builder> errorComponentBuilder;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final IntegerBinding chosenTaskCount;
|
||||
private final BooleanBinding anyCheckSelected;
|
||||
private final CheckListCellFactory listCellFactory;
|
||||
@@ -46,7 +43,7 @@ public class CheckListController implements FxController {
|
||||
public ListView<Check> checksListView;
|
||||
|
||||
@Inject
|
||||
public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, Lazy<ErrorComponent.Builder> errorComponentBuilder, CheckListCellFactory listCellFactory) {
|
||||
public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, FxApplicationWindows appWindows, CheckListCellFactory listCellFactory) {
|
||||
this.window = window;
|
||||
this.checks = FXCollections.observableList(checks, Check::observables);
|
||||
this.checkExecutor = checkExecutor;
|
||||
@@ -54,7 +51,7 @@ public class CheckListController implements FxController {
|
||||
this.chosenChecks = this.checks.filtered(Check::isChosenForExecution);
|
||||
this.reportWriter = reportWriteTask;
|
||||
this.selectedCheck = selectedCheck;
|
||||
this.errorComponentBuilder = errorComponentBuilder;
|
||||
this.appWindows = appWindows;
|
||||
this.chosenTaskCount = Bindings.size(this.chosenChecks);
|
||||
this.mainRunStarted = Bindings.isEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.RUNNABLE));
|
||||
this.somethingsRunning = Bindings.isNotEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.SCHEDULED || c.getState() == Check.CheckState.RUNNING));
|
||||
@@ -104,7 +101,7 @@ public class CheckListController implements FxController {
|
||||
reportWriter.writeReport(chosenChecks);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to write health check report.", e);
|
||||
errorComponentBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import org.cryptomator.cryptofs.VaultConfig;
|
||||
import org.cryptomator.cryptofs.VaultConfigLoadException;
|
||||
import org.cryptomator.cryptofs.VaultKeyInvalidException;
|
||||
import org.cryptomator.cryptolib.api.Masterkey;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.cryptomator.ui.unlock.UnlockCancelledException;
|
||||
import org.slf4j.Logger;
|
||||
@@ -40,10 +40,10 @@ public class StartController implements FxController {
|
||||
private final AtomicReference<Masterkey> masterkeyRef;
|
||||
private final AtomicReference<VaultConfig> vaultConfigRef;
|
||||
private final Lazy<Scene> checkScene;
|
||||
private final Lazy<ErrorComponent.Builder> errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
@Inject
|
||||
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) {
|
||||
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, FxApplicationWindows appWindows, @Named("unlockWindow") Stage unlockWindow) {
|
||||
this.window = window;
|
||||
this.unlockWindow = unlockWindow;
|
||||
this.vaultConfig = vault.getVaultConfigCache();
|
||||
@@ -52,7 +52,7 @@ public class StartController implements FxController {
|
||||
this.masterkeyRef = masterkeyRef;
|
||||
this.vaultConfigRef = vaultConfigRef;
|
||||
this.checkScene = checkScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -106,10 +106,10 @@ public class StartController implements FxController {
|
||||
// ok
|
||||
} else if (e instanceof VaultKeyInvalidException) {
|
||||
LOG.error("Invalid key"); //TODO: specific error screen
|
||||
errorComponent.get().window(window).cause(e).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
} else {
|
||||
LOG.error("Failed to load key.", e);
|
||||
errorComponent.get().window(window).cause(e).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -129,8 +129,11 @@ public class PassphraseEntryController implements FxController {
|
||||
}
|
||||
|
||||
private void windowClosed(WindowEvent windowEvent) {
|
||||
LOG.debug("Unlock canceled by user.");
|
||||
result.cancel(true);
|
||||
if(!result.isDone()) {
|
||||
result.cancel(true);
|
||||
LOG.debug("Unlock canceled by user.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
|
||||
public class AppLaunchEvent {
|
||||
|
||||
private final EventType type;
|
||||
private final Collection<Path> pathsToOpen;
|
||||
|
||||
public enum EventType {
|
||||
REVEAL_APP,
|
||||
OPEN_FILE
|
||||
}
|
||||
|
||||
public AppLaunchEvent(EventType type, Collection<Path> pathsToOpen) {
|
||||
this.type = type;
|
||||
this.pathsToOpen = pathsToOpen;
|
||||
}
|
||||
|
||||
public EventType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Collection<Path> getPathsToOpen() {
|
||||
return pathsToOpen;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import org.cryptomator.common.ShutdownHook;
|
||||
import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.desktop.AboutEvent;
|
||||
import java.awt.desktop.QuitResponse;
|
||||
import java.awt.desktop.QuitStrategy;
|
||||
import java.util.EnumSet;
|
||||
import java.util.EventObject;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@Singleton
|
||||
public class AppLifecycleListener {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
|
||||
public static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
|
||||
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final CountDownLatch shutdownLatch;
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final AtomicBoolean allowQuitWithoutPrompt;
|
||||
|
||||
@Inject
|
||||
AppLifecycleListener(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList<Vault> vaults) {
|
||||
this.fxApplicationStarter = fxApplicationStarter;
|
||||
this.shutdownLatch = shutdownLatch;
|
||||
this.vaults = vaults;
|
||||
this.allowQuitWithoutPrompt = new AtomicBoolean(true);
|
||||
vaults.addListener(this::vaultListChanged);
|
||||
|
||||
// register preferences shortcut
|
||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
|
||||
Desktop.getDesktop().setPreferencesHandler(this::showPreferencesWindow);
|
||||
}
|
||||
|
||||
// register preferences shortcut
|
||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_ABOUT)) {
|
||||
Desktop.getDesktop().setAboutHandler(this::showAboutWindow);
|
||||
}
|
||||
|
||||
// register quit handler
|
||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
|
||||
Desktop.getDesktop().setQuitHandler(this::handleQuitRequest);
|
||||
}
|
||||
|
||||
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
|
||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
|
||||
Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
|
||||
}
|
||||
|
||||
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully terminates the application.
|
||||
*/
|
||||
public void quit() {
|
||||
handleQuitRequest(null, new QuitResponse() {
|
||||
@Override
|
||||
public void performQuit() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleQuitRequest(@SuppressWarnings("unused") EventObject e, QuitResponse response) {
|
||||
QuitResponse decoratedQuitResponse = decorateQuitResponse(response);
|
||||
if (allowQuitWithoutPrompt.get()) {
|
||||
decoratedQuitResponse.performQuit();
|
||||
} else {
|
||||
fxApplicationStarter.get().thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
|
||||
}
|
||||
}
|
||||
|
||||
private QuitResponse decorateQuitResponse(QuitResponse originalQuitResponse) {
|
||||
return new QuitResponse() {
|
||||
@Override
|
||||
public void performQuit() {
|
||||
Platform.exit(); // will be no-op, if JavaFX never started.
|
||||
shutdownLatch.countDown(); // main thread is waiting for this latch
|
||||
originalQuitResponse.performQuit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelQuit() {
|
||||
originalQuitResponse.cancelQuit();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
||||
assert Platform.isFxApplicationThread();
|
||||
boolean allVaultsAllowTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
|
||||
boolean suddenTerminationChanged = allowQuitWithoutPrompt.compareAndSet(!allVaultsAllowTermination, allVaultsAllowTermination);
|
||||
if (suddenTerminationChanged) {
|
||||
LOG.debug("Allow quitting without prompt: {}", allVaultsAllowTermination);
|
||||
}
|
||||
}
|
||||
|
||||
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
|
||||
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||
}
|
||||
|
||||
private void showAboutWindow(@SuppressWarnings("unused") AboutEvent aboutEvent) {
|
||||
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT));
|
||||
}
|
||||
|
||||
private void forceUnmountRemainingVaults() {
|
||||
for (Vault vault : vaults) {
|
||||
if (vault.isUnlocked()) {
|
||||
try {
|
||||
vault.lock(true);
|
||||
} catch (Volume.VolumeException e) {
|
||||
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
||||
} catch (LockNotCompletedException e) {
|
||||
LOG.error("Failed to lock vault " + vault.getPath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.application.Platform;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Singleton
|
||||
public class FxApplicationStarter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStarter.class);
|
||||
|
||||
private final Lazy<FxApplicationComponent> fxAppComponent;
|
||||
private final ExecutorService executor;
|
||||
private final AtomicBoolean started;
|
||||
private final CompletableFuture<FxApplication> future;
|
||||
|
||||
@Inject
|
||||
public FxApplicationStarter(Lazy<FxApplicationComponent> fxAppComponent, ExecutorService executor) {
|
||||
this.fxAppComponent = fxAppComponent;
|
||||
this.executor = executor;
|
||||
this.started = new AtomicBoolean();
|
||||
this.future = new CompletableFuture<>();
|
||||
}
|
||||
|
||||
public CompletionStage<FxApplication> get() {
|
||||
if (!started.getAndSet(true)) {
|
||||
start();
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
private void start() {
|
||||
executor.submit(() -> {
|
||||
LOG.debug("Starting JavaFX runtime...");
|
||||
Platform.startup(() -> {
|
||||
assert Platform.isFxApplicationThread();
|
||||
LOG.info("JavaFX Runtime started.");
|
||||
FxApplication app = fxAppComponent.get().application();
|
||||
app.start();
|
||||
future.complete(app);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.desktop.AppReopenedListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@Singleton
|
||||
public class UiLauncher {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UiLauncher.class);
|
||||
|
||||
private final Settings settings;
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final Lazy<TrayMenuComponent> trayMenu;
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final AppLaunchEventHandler launchEventHandler;
|
||||
private final Optional<TrayIntegrationProvider> trayIntegration;
|
||||
|
||||
@Inject
|
||||
public UiLauncher(Settings settings, ObservableList<Vault> vaults, Lazy<TrayMenuComponent> trayMenu, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration) {
|
||||
this.settings = settings;
|
||||
this.vaults = vaults;
|
||||
this.trayMenu = trayMenu;
|
||||
this.fxApplicationStarter = fxApplicationStarter;
|
||||
this.launchEventHandler = launchEventHandler;
|
||||
this.trayIntegration = trayIntegration;
|
||||
}
|
||||
|
||||
public void launch() {
|
||||
boolean hidden = settings.startHidden().get();
|
||||
if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
|
||||
trayMenu.get().initializeTrayIcon();
|
||||
launch(true, hidden);
|
||||
} else {
|
||||
launch(false, hidden);
|
||||
}
|
||||
}
|
||||
|
||||
private void launch(boolean withTrayIcon, boolean hidden) {
|
||||
// start hidden, minimized or normal?
|
||||
if (withTrayIcon && hidden) {
|
||||
LOG.debug("Hiding application...");
|
||||
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
|
||||
} else if (!withTrayIcon && hidden) {
|
||||
LOG.debug("Minimizing application...");
|
||||
showMainWindowAsync(true);
|
||||
} else {
|
||||
LOG.debug("Showing application...");
|
||||
showMainWindowAsync(false);
|
||||
}
|
||||
|
||||
// register app reopen listener
|
||||
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(false));
|
||||
|
||||
// auto unlock
|
||||
Collection<Vault> vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock);
|
||||
if (!vaultsToAutoUnlock.isEmpty()) {
|
||||
fxApplicationStarter.get().thenAccept(app -> {
|
||||
for (Vault vault : vaultsToAutoUnlock) {
|
||||
app.startUnlockWorkflow(vault, Optional.empty());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
launchEventHandler.startHandlingLaunchEvents();
|
||||
}
|
||||
|
||||
private boolean shouldAttemptAutoUnlock(Vault vault) {
|
||||
return vault.isLocked() && vault.getVaultSettings().unlockAfterStartup().get();
|
||||
}
|
||||
|
||||
private void showMainWindowAsync(boolean minimize) {
|
||||
fxApplicationStarter.get().thenCompose(FxApplication::showMainWindow).thenAccept(win -> win.setIconified(minimize));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.cryptomator.ui.launcher;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.cryptomator.common.PluginClassLoader;
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationComponent;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
@Module(subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class})
|
||||
public abstract class UiLauncherModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static FxApplicationComponent provideFxApplicationComponent(FxApplicationComponent.Builder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
|
||||
return UiAppearanceProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<AutoStartProvider> provideAutostartProvider() {
|
||||
return AutoStartProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
|
||||
return TrayIntegrationProvider.get();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
static ResourceBundle provideLocalization() {
|
||||
return ResourceBundle.getBundle("i18n.strings");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("launchEventQueue")
|
||||
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
|
||||
return new ArrayBlockingQueue<>(10);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,11 +2,11 @@ package org.cryptomator.ui.lock;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.Nullable;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -25,15 +25,9 @@ public interface LockComponent {
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
LockComponent.Builder vault(@LockWindow Vault vault);
|
||||
|
||||
@BindsInstance
|
||||
LockComponent.Builder owner(@Named("lockWindowOwner") Optional<Stage> owner);
|
||||
|
||||
LockComponent build();
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
LockComponent create(@BindsInstance @LockWindow Vault vault, @BindsInstance @Named("lockWindowOwner") @Nullable Stage owner);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ 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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
@@ -19,7 +20,6 @@ import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -43,12 +43,12 @@ abstract class LockModule {
|
||||
@Provides
|
||||
@LockWindow
|
||||
@LockScoped
|
||||
static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional<Stage> owner) {
|
||||
static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Nullable @Named("lockWindowOwner") Stage owner) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(vault.getDisplayName());
|
||||
stage.setResizable(false);
|
||||
if (owner.isPresent()) {
|
||||
stage.initOwner(owner.get());
|
||||
if (owner != null) {
|
||||
stage.initOwner(owner);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
} else {
|
||||
stage.initModality(Modality.APPLICATION_MODAL);
|
||||
|
||||
@@ -5,9 +5,9 @@ import org.cryptomator.common.vaults.LockNotCompletedException;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -40,16 +40,16 @@ public class LockWorkflow extends Task<Void> {
|
||||
private final AtomicReference<CompletableFuture<Boolean>> forceRetryDecision;
|
||||
private final Lazy<Scene> lockForcedScene;
|
||||
private final Lazy<Scene> lockFailedScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
@Inject
|
||||
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference<CompletableFuture<Boolean>> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene, ErrorComponent.Builder errorComponent) {
|
||||
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference<CompletableFuture<Boolean>> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene, FxApplicationWindows appWindows) {
|
||||
this.lockWindow = lockWindow;
|
||||
this.vault = vault;
|
||||
this.forceRetryDecision = forceRetryDecision;
|
||||
this.lockForcedScene = lockForcedScene;
|
||||
this.lockFailedScene = lockFailedScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +109,7 @@ public class LockWorkflow extends Task<Void> {
|
||||
lockWindow.setScene(lockFailedScene.get());
|
||||
lockWindow.show();
|
||||
} else {
|
||||
errorComponent.cause(throwable).window(lockWindow).build().showErrorScene();
|
||||
appWindows.showErrorWindow(throwable, lockWindow, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface MainWindow {
|
||||
@interface MainWindow {
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ 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 org.cryptomator.ui.common.StageInitializer;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
import org.cryptomator.ui.migration.MigrationComponent;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
@@ -34,6 +36,18 @@ import java.util.ResourceBundle;
|
||||
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
|
||||
abstract class MainWindowModule {
|
||||
|
||||
@Provides
|
||||
@MainWindow
|
||||
@MainWindowScoped
|
||||
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
|
||||
initializer.accept(stage);
|
||||
stage.setTitle("Cryptomator");
|
||||
stage.initStyle(StageStyle.UNDECORATED);
|
||||
stage.setMinWidth(650);
|
||||
stage.setMinHeight(440);
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MainWindowScoped
|
||||
static ObjectProperty<Vault> provideSelectedVault() {
|
||||
@@ -47,22 +61,11 @@ abstract class MainWindowModule {
|
||||
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MainWindow
|
||||
@MainWindowScoped
|
||||
static Stage provideStage(StageFactory factory) {
|
||||
Stage stage = factory.create(StageStyle.UNDECORATED);
|
||||
stage.setMinWidth(650);
|
||||
stage.setMinHeight(440);
|
||||
stage.setTitle("Cryptomator");
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@MainWindowScoped
|
||||
@Named("errorWindow")
|
||||
static Stage provideErrorStage(@MainWindow Stage window, StageFactory factory, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create(StageStyle.DECORATED);
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("main.vaultDetail.error.windowTitle"));
|
||||
stage.initModality(Modality.APPLICATION_MODAL);
|
||||
stage.initOwner(window);
|
||||
|
||||
@@ -3,9 +3,9 @@ package org.cryptomator.ui.mainwindow;
|
||||
import org.cryptomator.common.LicenseHolder;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationTerminator;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.fxapp.UpdateChecker;
|
||||
import org.cryptomator.ui.launcher.AppLifecycleListener;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
@@ -25,9 +25,9 @@ public class MainWindowTitleController implements FxController {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class);
|
||||
|
||||
private final AppLifecycleListener appLifecycle;
|
||||
private final Stage window;
|
||||
private final FxApplication application;
|
||||
private final FxApplicationTerminator terminator;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final boolean trayMenuInitialized;
|
||||
private final UpdateChecker updateChecker;
|
||||
private final BooleanBinding updateAvailable;
|
||||
@@ -40,10 +40,10 @@ public class MainWindowTitleController implements FxController {
|
||||
private double yOffset;
|
||||
|
||||
@Inject
|
||||
MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
|
||||
this.appLifecycle = appLifecycle;
|
||||
MainWindowTitleController(@MainWindow Stage window, FxApplicationTerminator terminator, FxApplicationWindows appWindows, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
|
||||
this.window = window;
|
||||
this.application = application;
|
||||
this.terminator = terminator;
|
||||
this.appWindows = appWindows;
|
||||
this.trayMenuInitialized = trayMenu.isInitialized();
|
||||
this.updateChecker = updateChecker;
|
||||
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
|
||||
@@ -96,7 +96,7 @@ public class MainWindowTitleController implements FxController {
|
||||
if (trayMenuInitialized) {
|
||||
window.close();
|
||||
} else {
|
||||
appLifecycle.quit();
|
||||
terminator.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,17 +107,17 @@ public class MainWindowTitleController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void showPreferences() {
|
||||
application.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showGeneralPreferences() {
|
||||
application.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
|
||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showDonationKeyPreferences() {
|
||||
application.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
|
||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
|
||||
}
|
||||
|
||||
/* Getter/Setter */
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import com.tobiasdiez.easybind.Subscription;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
@@ -9,9 +8,9 @@ import org.cryptomator.ui.common.AutoAnimator;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||
import org.cryptomator.ui.controls.FontAwesome5IconView;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
@@ -22,7 +21,7 @@ import javafx.fxml.FXML;
|
||||
public class VaultDetailController implements FxController {
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final FxApplication application;
|
||||
private final Application application;
|
||||
private final Binding<FontAwesome5Icon> glyph;
|
||||
private final BooleanBinding anyVaultSelected;
|
||||
|
||||
@@ -33,7 +32,7 @@ public class VaultDetailController implements FxController {
|
||||
|
||||
|
||||
@Inject
|
||||
VaultDetailController(ObjectProperty<Vault> vault, FxApplication application) {
|
||||
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.glyph = EasyBind.select(vault) //
|
||||
|
||||
@@ -4,8 +4,7 @@ import com.tobiasdiez.easybind.EasyBind;
|
||||
import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
|
||||
@@ -14,25 +13,23 @@ import javafx.beans.binding.BooleanExpression;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultDetailLockedController implements FxController {
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final FxApplication application;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
private final KeychainManager keychain;
|
||||
private final Stage mainWindow;
|
||||
private final BooleanExpression passwordSaved;
|
||||
|
||||
@Inject
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
this.keychain = keychain;
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -45,7 +42,7 @@ public class VaultDetailLockedController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void unlock() {
|
||||
application.startUnlockWorkflow(vault.get(), Optional.of(mainWindow));
|
||||
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package org.cryptomator.ui.mainwindow;
|
||||
|
||||
import com.tobiasdiez.easybind.EasyBind;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultListManager;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javafx.beans.binding.Binding;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
@@ -18,21 +16,21 @@ import javafx.stage.Stage;
|
||||
public class VaultDetailUnknownErrorController implements FxController {
|
||||
|
||||
private final ObjectProperty<Vault> vault;
|
||||
private final ErrorComponent.Builder errorComponentBuilder;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Stage errorWindow;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
|
||||
@Inject
|
||||
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault, ErrorComponent.Builder errorComponentBuilder, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
|
||||
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
|
||||
this.vault = vault;
|
||||
this.errorComponentBuilder = errorComponentBuilder;
|
||||
this.appWindows = appWindows;
|
||||
this.errorWindow = errorWindow;
|
||||
this.removeVault = removeVault;
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void showError() {
|
||||
errorComponentBuilder.window(errorWindow).cause(vault.get().getLastKnownException()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(vault.get().getLastKnownException(), errorWindow, null);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -6,7 +6,7 @@ import com.google.common.cache.LoadingCache;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -14,22 +14,21 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
|
||||
@MainWindowScoped
|
||||
public class VaultDetailUnlockedController implements FxController {
|
||||
|
||||
private final ReadOnlyObjectProperty<Vault> vault;
|
||||
private final FxApplication application;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultService vaultService;
|
||||
private final Stage mainWindow;
|
||||
private final LoadingCache<Vault, VaultStatisticsComponent> vaultStats;
|
||||
private final VaultStatisticsComponent.Builder vaultStatsBuilder;
|
||||
|
||||
@Inject
|
||||
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplication application, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, @MainWindow Stage mainWindow) {
|
||||
public VaultDetailUnlockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultService vaultService, VaultStatisticsComponent.Builder vaultStatsBuilder, @MainWindow Stage mainWindow) {
|
||||
this.vault = vault;
|
||||
this.application = application;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultService = vaultService;
|
||||
this.mainWindow = mainWindow;
|
||||
this.vaultStats = CacheBuilder.newBuilder().weakValues().build(CacheLoader.from(this::buildVaultStats));
|
||||
@@ -47,7 +46,7 @@ public class VaultDetailUnlockedController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void lock() {
|
||||
application.startLockWorkflow(vault.get(), Optional.of(mainWindow));
|
||||
appWindows.startLockWorkflow(vault.get(), mainWindow);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -7,7 +7,8 @@ import org.cryptomator.common.keychain.KeychainManager;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||
@@ -18,7 +19,6 @@ import javafx.beans.property.ObjectProperty;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.cryptomator.common.vaults.VaultState.Value.*;
|
||||
|
||||
@@ -27,7 +27,8 @@ public class VaultListContextMenuController implements FxController {
|
||||
|
||||
private final ObservableOptionalValue<Vault> selectedVault;
|
||||
private final Stage mainWindow;
|
||||
private final FxApplication application;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final VaultService vaultService;
|
||||
private final KeychainManager keychain;
|
||||
private final RemoveVaultComponent.Builder removeVault;
|
||||
private final VaultOptionsComponent.Builder vaultOptionsWindow;
|
||||
@@ -38,10 +39,11 @@ public class VaultListContextMenuController implements FxController {
|
||||
private final Binding<Boolean> selectedVaultLockable;
|
||||
|
||||
@Inject
|
||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplication application, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
|
||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplicationWindows appWindows, VaultService vaultService, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Builder vaultOptionsWindow) {
|
||||
this.selectedVault = EasyBind.wrapNullable(selectedVault);
|
||||
this.mainWindow = mainWindow;
|
||||
this.application = application;
|
||||
this.appWindows = appWindows;
|
||||
this.vaultService = vaultService;
|
||||
this.keychain = keychain;
|
||||
this.removeVault = removeVault;
|
||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||
@@ -74,22 +76,20 @@ public class VaultListContextMenuController implements FxController {
|
||||
@FXML
|
||||
public void didClickUnlockVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.startUnlockWorkflow(v, Optional.of(mainWindow));
|
||||
appWindows.startUnlockWorkflow(v, mainWindow);
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickLockVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.startLockWorkflow(v, Optional.of(mainWindow));
|
||||
appWindows.startLockWorkflow(v, mainWindow);
|
||||
});
|
||||
}
|
||||
|
||||
@FXML
|
||||
public void didClickRevealVault() {
|
||||
selectedVault.ifValuePresent(v -> {
|
||||
application.getVaultService().reveal(v);
|
||||
});
|
||||
selectedVault.ifValuePresent(vaultService::reveal);
|
||||
}
|
||||
|
||||
// Getter and Setter
|
||||
|
||||
@@ -2,9 +2,9 @@ package org.cryptomator.ui.migration;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
@@ -12,13 +12,13 @@ public class MigrationImpossibleController implements FxController {
|
||||
|
||||
private static final String HELP_URI = "https://docs.cryptomator.org/en/1.5/help/manual-migration/";
|
||||
|
||||
private final FxApplication fxApplication;
|
||||
private final Application application;
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
|
||||
@Inject
|
||||
MigrationImpossibleController(FxApplication fxApplication, @MigrationWindow Stage window, @MigrationWindow Vault vault) {
|
||||
this.fxApplication = fxApplication;
|
||||
MigrationImpossibleController(Application application, @MigrationWindow Stage window, @MigrationWindow Vault vault) {
|
||||
this.application = application;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public class MigrationImpossibleController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void getMigrationHelp() {
|
||||
fxApplication.getHostServices().showDocument(HELP_URI);
|
||||
application.getHostServices().showDocument(HELP_URI);
|
||||
}
|
||||
|
||||
/* Getter/Setters */
|
||||
|
||||
@@ -6,13 +6,13 @@ import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
@@ -37,7 +37,7 @@ abstract class MigrationModule {
|
||||
@Provides
|
||||
@MigrationWindow
|
||||
@MigrationScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage owner, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("migration.title"));
|
||||
stage.setResizable(false);
|
||||
|
||||
@@ -12,12 +12,12 @@ import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.Tasks;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -58,7 +58,7 @@ public class MigrationRunController implements FxController {
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private final KeychainManager keychain;
|
||||
private final ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Lazy<Scene> startScene;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final Lazy<Scene> impossibleScene;
|
||||
@@ -73,14 +73,14 @@ public class MigrationRunController implements FxController {
|
||||
public NiceSecurePasswordField passwordField;
|
||||
|
||||
@Inject
|
||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy<Scene> capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy<Scene> impossibleScene, ErrorComponent.Builder errorComponent) {
|
||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, KeychainManager keychain, @Named("capabilityErrorCause") ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy<Scene> capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_IMPOSSIBLE) Lazy<Scene> impossibleScene, FxApplicationWindows appWindows) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.executor = executor;
|
||||
this.scheduler = scheduler;
|
||||
this.keychain = keychain;
|
||||
this.missingCapability = missingCapability;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.startScene = startScene;
|
||||
this.successScene = successScene;
|
||||
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
||||
@@ -146,12 +146,12 @@ public class MigrationRunController implements FxController {
|
||||
}).onError(FileNameTooLongException.class, e -> {
|
||||
LOG.error("Migration failed because the underlying file system does not support long filenames.", e);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, startScene.get());
|
||||
window.setScene(impossibleScene.get());
|
||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
||||
LOG.error("Migration failed for technical reasons.", e);
|
||||
vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
|
||||
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, startScene.get());
|
||||
}).andFinally(() -> {
|
||||
passwordField.setDisable(false);
|
||||
progressSyncTask.cancel(true);
|
||||
|
||||
@@ -2,25 +2,24 @@ package org.cryptomator.ui.migration;
|
||||
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
|
||||
@MigrationScoped
|
||||
public class MigrationSuccessController implements FxController {
|
||||
|
||||
private final FxApplication fxApplication;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final Stage window;
|
||||
private final Vault vault;
|
||||
private final Stage mainWindow;
|
||||
|
||||
@Inject
|
||||
MigrationSuccessController(FxApplication fxApplication, @MigrationWindow Stage window, @MigrationWindow Vault vault, @MainWindow Stage mainWindow) {
|
||||
this.fxApplication = fxApplication;
|
||||
MigrationSuccessController(FxApplicationWindows appWindows, @MigrationWindow Stage window, @MigrationWindow Vault vault, @PrimaryStage Stage mainWindow) {
|
||||
this.appWindows = appWindows;
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.mainWindow = mainWindow;
|
||||
@@ -29,7 +28,7 @@ public class MigrationSuccessController implements FxController {
|
||||
@FXML
|
||||
public void unlockAndClose() {
|
||||
close();
|
||||
fxApplication.startUnlockWorkflow(vault, Optional.of(mainWindow));
|
||||
appWindows.startUnlockWorkflow(vault, mainWindow);
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
||||
@@ -7,8 +7,8 @@ import org.cryptomator.common.settings.UiTheme;
|
||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -30,7 +30,6 @@ import javafx.util.StringConverter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
@PreferencesScoped
|
||||
public class GeneralPreferencesController implements FxController {
|
||||
@@ -48,7 +47,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
private final Application application;
|
||||
private final Environment environment;
|
||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
public ChoiceBox<UiTheme> themeChoiceBox;
|
||||
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
||||
public CheckBox showMinimizeButtonCheckbox;
|
||||
@@ -60,9 +59,8 @@ public class GeneralPreferencesController implements FxController {
|
||||
public RadioButton nodeOrientationLtr;
|
||||
public RadioButton nodeOrientationRtl;
|
||||
|
||||
|
||||
@Inject
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, ErrorComponent.Builder errorComponent) {
|
||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, TrayMenuComponent trayMenu, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, ObjectProperty<SelectedPreferencesTab> selectedTabProperty, LicenseHolder licenseHolder, ResourceBundle resourceBundle, Application application, Environment environment, FxApplicationWindows appWindows) {
|
||||
this.window = window;
|
||||
this.settings = settings;
|
||||
this.trayMenuInitialized = trayMenu.isInitialized();
|
||||
@@ -74,7 +72,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.application = application;
|
||||
this.environment = environment;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -143,7 +141,7 @@ public class GeneralPreferencesController implements FxController {
|
||||
} catch (ToggleAutoStartFailedException e) {
|
||||
autoStartCheckbox.setSelected(!enableAutoStart); // restore previous state
|
||||
LOG.error("Failed to toggle autostart.", e);
|
||||
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, window.getScene());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.ui.common.Animations;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -33,18 +33,18 @@ public class RecoveryKeyCreationController implements FxController {
|
||||
private final ExecutorService executor;
|
||||
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||
private final StringProperty recoveryKeyProperty;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
public NiceSecurePasswordField passwordField;
|
||||
|
||||
@Inject
|
||||
public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, ErrorComponent.Builder errorComponent) {
|
||||
public RecoveryKeyCreationController(@RecoveryKeyWindow Stage window, @FxmlScene(FxmlFile.RECOVERYKEY_SUCCESS) Lazy<Scene> successScene, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, FxApplicationWindows appWindows) {
|
||||
this.window = window;
|
||||
this.successScene = successScene;
|
||||
this.vault = vault;
|
||||
this.executor = executor;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
this.recoveryKeyProperty = recoveryKey;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -63,7 +63,7 @@ public class RecoveryKeyCreationController implements FxController {
|
||||
Animations.createShakeWindowAnimation(window).play();
|
||||
} else {
|
||||
LOG.error("Creation of recovery key failed.", task.getException());
|
||||
errorComponent.cause(task.getException()).window(window).returnToScene(window.getScene()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(task.getException(), window, window.getScene());
|
||||
}
|
||||
});
|
||||
executor.submit(task);
|
||||
|
||||
+5
-5
@@ -2,11 +2,11 @@ package org.cryptomator.ui.recoverykey;
|
||||
|
||||
import dagger.Lazy;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.NewPasswordController;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -31,19 +31,19 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
private final ExecutorService executor;
|
||||
private final StringProperty recoveryKey;
|
||||
private final Lazy<Scene> recoverScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
|
||||
public NewPasswordController newPasswordController;
|
||||
|
||||
@Inject
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene, ErrorComponent.Builder errorComponent) {
|
||||
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene, FxApplicationWindows appWindows) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||
this.executor = executor;
|
||||
this.recoveryKey = recoveryKey;
|
||||
this.recoverScene = recoverScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -64,7 +64,7 @@ public class RecoveryKeyResetPasswordController implements FxController {
|
||||
});
|
||||
task.setOnFailed(event -> {
|
||||
LOG.error("Resetting password failed.", task.getException());
|
||||
errorComponent.cause(task.getException()).window(window).returnToScene(recoverScene.get()).build().showErrorScene();
|
||||
appWindows.showErrorWindow(task.getException(), window, recoverScene.get());
|
||||
});
|
||||
executor.submit(task);
|
||||
}
|
||||
|
||||
@@ -5,13 +5,13 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javafx.scene.Scene;
|
||||
@@ -33,12 +33,12 @@ abstract class RemoveVaultModule {
|
||||
@Provides
|
||||
@RemoveVaultWindow
|
||||
@RemoveVaultScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("removeVault.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.initOwner(primaryStage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import org.cryptomator.integrations.tray.SeparatorItem;
|
||||
import org.cryptomator.integrations.tray.SubMenuItem;
|
||||
import org.cryptomator.integrations.tray.TrayMenuController;
|
||||
import org.cryptomator.integrations.tray.TrayMenuItem;
|
||||
import org.cryptomator.ui.fxapp.FxApplication;
|
||||
import org.cryptomator.ui.launcher.AppLifecycleListener;
|
||||
import org.cryptomator.ui.launcher.FxApplicationStarter;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationTerminator;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -23,7 +23,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@TrayMenuScoped
|
||||
public class TrayMenuBuilder {
|
||||
@@ -32,18 +31,20 @@ public class TrayMenuBuilder {
|
||||
private static final String TRAY_ICON = "/img/tray_icon.png";
|
||||
|
||||
private final ResourceBundle resourceBundle;
|
||||
private final AppLifecycleListener appLifecycle;
|
||||
private final FxApplicationStarter fxApplicationStarter;
|
||||
private final VaultService vaultService;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final FxApplicationTerminator appTerminator;
|
||||
private final ObservableList<Vault> vaults;
|
||||
private final TrayMenuController trayMenu;
|
||||
|
||||
private volatile boolean initialized;
|
||||
|
||||
@Inject
|
||||
TrayMenuBuilder(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList<Vault> vaults, Optional<TrayMenuController> trayMenu) {
|
||||
TrayMenuBuilder(ResourceBundle resourceBundle, VaultService vaultService, FxApplicationWindows appWindows, FxApplicationTerminator appTerminator, ObservableList<Vault> vaults, Optional<TrayMenuController> trayMenu) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.appLifecycle = appLifecycle;
|
||||
this.fxApplicationStarter = fxApplicationStarter;
|
||||
this.vaultService = vaultService;
|
||||
this.appWindows = appWindows;
|
||||
this.appTerminator = appTerminator;
|
||||
this.vaults = vaults;
|
||||
this.trayMenu = trayMenu.orElse(null);
|
||||
}
|
||||
@@ -96,14 +97,13 @@ public class TrayMenuBuilder {
|
||||
|
||||
private List<TrayMenuItem> buildSubmenu(Vault vault) {
|
||||
if (vault.isLocked()) {
|
||||
return List.of(
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.unlock"), () -> this.unlockVault(vault))
|
||||
return List.of( //
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.unlock"), () -> this.unlockVault(vault)) //
|
||||
);
|
||||
} else if (vault.isUnlocked()) {
|
||||
return List.of(
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.lock"), () -> this.lockVault(vault)),
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.reveal"), () -> this.revealVault(vault))
|
||||
|
||||
return List.of( //
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.lock"), () -> this.lockVault(vault)), //
|
||||
new ActionItem(resourceBundle.getString("traymenu.vault.reveal"), () -> this.revealVault(vault)) //
|
||||
);
|
||||
} else {
|
||||
return List.of();
|
||||
@@ -113,35 +113,31 @@ public class TrayMenuBuilder {
|
||||
/* action listeners: */
|
||||
|
||||
private void quitApplication() {
|
||||
appLifecycle.quit();
|
||||
appTerminator.terminate();
|
||||
}
|
||||
|
||||
private void unlockVault(Vault vault) {
|
||||
showMainAppAndThen(app -> app.startUnlockWorkflow(vault, Optional.empty()));
|
||||
appWindows.startUnlockWorkflow(vault, null);
|
||||
}
|
||||
|
||||
private void lockVault(Vault vault) {
|
||||
showMainAppAndThen(app -> app.startLockWorkflow(vault, Optional.empty()));
|
||||
appWindows.startLockWorkflow(vault, null);
|
||||
}
|
||||
|
||||
private void lockAllVaults() {
|
||||
showMainAppAndThen(app -> app.getVaultService().lockAll(vaults.filtered(Vault::isUnlocked), false));
|
||||
vaultService.lockAll(vaults.filtered(Vault::isUnlocked), false);
|
||||
}
|
||||
|
||||
private void revealVault(Vault vault) {
|
||||
showMainAppAndThen(app -> app.getVaultService().reveal(vault));
|
||||
vaultService.reveal(vault);
|
||||
}
|
||||
|
||||
void showMainWindow() {
|
||||
showMainAppAndThen(FxApplication::showMainWindow);
|
||||
appWindows.showMainWindow();
|
||||
}
|
||||
|
||||
private void showPreferencesWindow() {
|
||||
showMainAppAndThen(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||
}
|
||||
|
||||
private void showMainAppAndThen(Consumer<FxApplication> action) {
|
||||
fxApplicationStarter.get().thenAccept(action);
|
||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ package org.cryptomator.ui.unlock;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import dagger.Subcomponent;
|
||||
import org.cryptomator.common.Nullable;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
@@ -29,16 +29,9 @@ public interface UnlockComponent {
|
||||
return workflow;
|
||||
}
|
||||
|
||||
@Subcomponent.Builder
|
||||
interface Builder {
|
||||
|
||||
@BindsInstance
|
||||
Builder vault(@UnlockWindow Vault vault);
|
||||
|
||||
@BindsInstance
|
||||
Builder owner(@Named("unlockWindowOwner") Optional<Stage> owner);
|
||||
|
||||
UnlockComponent build();
|
||||
@Subcomponent.Factory
|
||||
interface Factory {
|
||||
UnlockComponent create(@BindsInstance @UnlockWindow Vault vault, @BindsInstance @Named("unlockWindowOwner") @Nullable Stage owner);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingComponent;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
@@ -21,7 +22,6 @@ import javafx.scene.Scene;
|
||||
import javafx.stage.Modality;
|
||||
import javafx.stage.Stage;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
@Module(subcomponents = {KeyLoadingComponent.class})
|
||||
@@ -37,12 +37,12 @@ abstract class UnlockModule {
|
||||
@Provides
|
||||
@UnlockWindow
|
||||
@UnlockScoped
|
||||
static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Named("unlockWindowOwner") Optional<Stage> owner) {
|
||||
static Stage provideStage(StageFactory factory, @UnlockWindow Vault vault, @Nullable @Named("unlockWindowOwner") Stage owner) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(vault.getDisplayName());
|
||||
stage.setResizable(false);
|
||||
if (owner.isPresent()) {
|
||||
stage.initOwner(owner.get());
|
||||
if (owner != null) {
|
||||
stage.initOwner(owner);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
} else {
|
||||
stage.initModality(Modality.APPLICATION_MODAL);
|
||||
|
||||
@@ -9,10 +9,10 @@ import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.vaults.VaultState;
|
||||
import org.cryptomator.common.vaults.Volume.VolumeException;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.ui.common.ErrorComponent;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.VaultService;
|
||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -42,17 +42,17 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
private final VaultService vaultService;
|
||||
private final Lazy<Scene> successScene;
|
||||
private final Lazy<Scene> invalidMountPointScene;
|
||||
private final ErrorComponent.Builder errorComponent;
|
||||
private final FxApplicationWindows appWindows;
|
||||
private final KeyLoadingStrategy keyLoadingStrategy;
|
||||
|
||||
@Inject
|
||||
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, ErrorComponent.Builder errorComponent, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
|
||||
UnlockWorkflow(@UnlockWindow Stage window, @UnlockWindow Vault vault, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, FxApplicationWindows appWindows, @UnlockWindow KeyLoadingStrategy keyLoadingStrategy) {
|
||||
this.window = window;
|
||||
this.vault = vault;
|
||||
this.vaultService = vaultService;
|
||||
this.successScene = successScene;
|
||||
this.invalidMountPointScene = invalidMountPointScene;
|
||||
this.errorComponent = errorComponent;
|
||||
this.appWindows = appWindows;
|
||||
this.keyLoadingStrategy = keyLoadingStrategy;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ public class UnlockWorkflow extends Task<Boolean> {
|
||||
|
||||
private void handleGenericError(Throwable e) {
|
||||
LOG.error("Unlock failed for technical reasons.", e);
|
||||
errorComponent.cause(e).window(window).build().showErrorScene();
|
||||
appWindows.showErrorWindow(e, window, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,7 @@ 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 org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||
|
||||
import javax.inject.Provider;
|
||||
@@ -44,14 +44,14 @@ abstract class VaultOptionsModule {
|
||||
@Provides
|
||||
@VaultOptionsWindow
|
||||
@VaultOptionsScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, @VaultOptionsWindow Vault vault) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, @VaultOptionsWindow Vault vault) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(vault.getDisplayName());
|
||||
stage.setResizable(true);
|
||||
stage.setMinWidth(400);
|
||||
stage.setMinHeight(300);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
stage.initOwner(owner);
|
||||
stage.initOwner(primaryStage);
|
||||
return stage;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import dagger.multibindings.IntoMap;
|
||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxController;
|
||||
import org.cryptomator.ui.common.FxControllerKey;
|
||||
import org.cryptomator.ui.common.FxmlFile;
|
||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||
import org.cryptomator.ui.common.FxmlScene;
|
||||
import org.cryptomator.ui.common.StageFactory;
|
||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javafx.scene.Scene;
|
||||
@@ -33,11 +33,11 @@ abstract class WrongFileAlertModule {
|
||||
@Provides
|
||||
@WrongFileAlertWindow
|
||||
@WrongFileAlertScoped
|
||||
static Stage provideStage(StageFactory factory, @MainWindow Stage mainWindow, ResourceBundle resourceBundle) {
|
||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
|
||||
Stage stage = factory.create();
|
||||
stage.setTitle(resourceBundle.getString("wrongFileAlert.title"));
|
||||
stage.setResizable(false);
|
||||
stage.initOwner(mainWindow);
|
||||
stage.initOwner(primaryStage);
|
||||
stage.initModality(Modality.WINDOW_MODAL);
|
||||
return stage;
|
||||
}
|
||||
|
||||
@@ -94,7 +94,8 @@ forgetPassword.confirmBtn=نسيت كلمة المرور
|
||||
unlock.passwordPrompt=أدخل كلمة السر لـ "%s":
|
||||
unlock.savePassword=تذكر كلمة المرور
|
||||
unlock.unlockBtn=افتح
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=اختر…
|
||||
unlock.chooseMasterkey.filePickerTitle=اختر ملف الـ Masterkey
|
||||
## Success
|
||||
unlock.success.message=تم فتح "%s" بنجاح! يمكنك الآن الوصول لمخزنك عن طريق القرص الافتراضي الخاص به.
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
# Locale Specific CSS files such as CJK, RTL,...
|
||||
|
||||
# Generics
|
||||
## Button
|
||||
generic.button.apply=প্রয়োগ করুন
|
||||
generic.button.back=পিছনে
|
||||
generic.button.cancel=বাতিল করুন
|
||||
generic.button.change=পরিবর্তন করুন
|
||||
generic.button.close=বন্ধ করুন
|
||||
generic.button.copy=কপি
|
||||
generic.button.copied=কপি হয়েছে!
|
||||
generic.button.done=সম্পন্ন হয়েছে
|
||||
generic.button.next=পরবর্তী
|
||||
generic.button.print=প্রিন্ট
|
||||
## Error
|
||||
generic.error.title=ত্রুটি %s
|
||||
generic.error.instruction=ওহো! ক্রিপ্টোমেটর এটা যে হবে তা আশা করেনি. আপনি এই ত্রুটির সমাধানটি খুঁজে দেখুন. ত্রুটিটি সম্পর্কে যদি বিবরণ না পান, আপনি সেটি রিপোর্ট করতে পারেন.
|
||||
generic.error.hyperlink.lookup=ত্রুটিটি খুঁজে দেখুন
|
||||
generic.error.hyperlink.report=ত্রুটিটি রিপোর্ট করুন
|
||||
generic.error.technicalDetails=বিস্তারিত:
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=ভোল্ট
|
||||
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=দেখান
|
||||
traymenu.lockAllVaults=সব লক করুন
|
||||
traymenu.quitApplication=বের হোন
|
||||
traymenu.vault.unlock=আনলক করুন
|
||||
traymenu.vault.lock=লক করুন
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=ভোল্ট যুক্ত করুন
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=নতুন ভোল্ট তৈরি করুন
|
||||
addvaultwizard.welcome.existingButton=বিদ্যমান কোনো ভোল্ট খুলুন
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=ভোল্ট এর একটি নাম দেন
|
||||
addvaultwizard.new.namePrompt=ভোল্ট এর নাম
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=ভোল্ট এর এনক্রিপ্টেড ফাইলগুলো ক্রিপ্টোমেটর কোথায় সংরক্ষণ করবে?
|
||||
addvaultwizard.new.locationLabel=স্টোরেজ লোকেশন
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerButton=নির্বাচন করুন…
|
||||
addvaultwizard.new.directoryPickerTitle=ডিরেক্টরি নির্বাচন
|
||||
addvaultwizard.new.locationDoesNotExist=নিদিষ্ট করা পথে একটি ডিরেক্টরি বিদ্যমান নয় অথবা প্রবেশ করা যাচ্ছে না
|
||||
addvaultwizard.new.locationIsNotWritable=নিদিষ্ট করা পথে কোনো কিছু পরিবর্তন করার অনুমতি নেই
|
||||
addvaultwizard.new.locationIsOk=আপনার ভোল্টের জন্য উপযুক্ত ঠিকানা
|
||||
addvaultwizard.new.invalidName=ভোল্টের নাম অনুপযুক্ত. অনুগ্রহ করে ভোল্টের জন্য একটি সাধারণ নাম ব্যবহার করুন.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=ভোল্ট তৈরি করুন
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=পাসওয়ার্ড ছাড়া আপনি আপনার তথ্যগুলো ব্যবহার করতে পারবেন না. তাই আপনি কি একটি পুনরুদ্ধার চাবি চান যদি আপনি পাসওয়ার্ড হারিয়ে ফেলেন?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=হ্যাঁ অবশ্যই, দুঃখিত হওয়ার চেয়ে নিরাপদ থাকাই ভালো
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=না ধন্যবাদ, আমি আমার পাসওয়ার্ড হারাবো না
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=গুরুত্বপূর্ণ.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ ভোল্ট ফাইলস ⚠️
|
||||
addvault.new.readme.storageLocation.2=এটি হচ্ছে আপনার ভোল্টের স্টোরেজ ঠিকানা.
|
||||
addvault.new.readme.storageLocation.3=যা করবেন না
|
||||
addvault.new.readme.storageLocation.4=• কোন ফাইলে কোন ধরনের পরিবর্তন
|
||||
addvault.new.readme.storageLocation.5=• এনক্রিপশনের জন্য কোন ফাইল এই ডিরেক্টরিতে পেশ করা.
|
||||
## Existing
|
||||
addvaultwizard.existing.chooseBtn=নির্বাচন করুন…
|
||||
## Success
|
||||
|
||||
# Remove Vault
|
||||
|
||||
# Change Password
|
||||
|
||||
# Forget Password
|
||||
|
||||
# Unlock
|
||||
unlock.unlockBtn=আনলক করুন
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=নির্বাচন করুন…
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.retryBtn=পুনরায় চেষ্টা করুন
|
||||
## Failure
|
||||
|
||||
# Migration
|
||||
## Start
|
||||
## Run
|
||||
## Success
|
||||
## Missing file system capabilities
|
||||
## Impossible
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
## Start Failure
|
||||
## Check Selection
|
||||
## Detail view
|
||||
## Fix Application
|
||||
|
||||
# Preferences
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
## Read
|
||||
## Write
|
||||
|
||||
# Main Window
|
||||
main.closeBtn.tooltip=বন্ধ করুন
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.lock=লক করুন
|
||||
main.vaultlist.addVaultBtn=ভোল্ট যুক্ত করুন
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
### Unlocked
|
||||
main.vaultDetail.lockBtn=লক করুন
|
||||
### Missing
|
||||
### Needs Migration
|
||||
### Error
|
||||
|
||||
# Wrong File Alert
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general.vaultName=ভোল্ট এর নাম
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=নির্বাচন করুন…
|
||||
## Master Key
|
||||
|
||||
|
||||
# Recovery Key
|
||||
|
||||
# New Password
|
||||
|
||||
# Quit
|
||||
@@ -94,7 +94,8 @@ forgetPassword.confirmBtn=Zaboravili ste šifru
|
||||
unlock.passwordPrompt=Unesite lozinku za "%s":
|
||||
unlock.savePassword=Zapamti šifru
|
||||
unlock.unlockBtn=Otključaj
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=Odaberi…
|
||||
unlock.chooseMasterkey.filePickerTitle=Odaberite Masterkey Datoteku
|
||||
## Success
|
||||
unlock.success.message=Uspješno ste otključali "%s"! Vaš sef je sada dostupan putem svog virtualnog diska.
|
||||
|
||||
@@ -102,8 +102,9 @@ unlock.title=Desbloca "%s"
|
||||
unlock.passwordPrompt=Introduïu la contrasenya de "%s":
|
||||
unlock.savePassword=Recorda la contrasenya
|
||||
unlock.unlockBtn=Desbloqueja
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.prompt=No es pot trobar el fitxer de clau mestra per aquesta bòveda a la ubicació esperada. Escull el fixer manualment.
|
||||
unlock.chooseMasterkey.chooseBtn=Trieu…
|
||||
unlock.chooseMasterkey.filePickerTitle=Seleccioneu el fitxer de Clau Mestra
|
||||
## Success
|
||||
unlock.success.message=S'ha desblocat %s correctament! Podeu accedir a la vostra caixa forta a través de la unitat virtual.
|
||||
|
||||
@@ -102,8 +102,10 @@ unlock.title=Odemknout "%s"
|
||||
unlock.passwordPrompt=Zadejte heslo pro "%s":
|
||||
unlock.savePassword=Zapamatovat heslo
|
||||
unlock.unlockBtn=Odemknout
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Vyberte soubor s hlavním klíčem "%s"
|
||||
unlock.chooseMasterkey.prompt=Nepodařilo se najít soubor hlavního klíče pro tento trezor v očekávaném umístění. Vyberte prosím soubor klíče ručně.
|
||||
unlock.chooseMasterkey.chooseBtn=Vybrat...
|
||||
unlock.chooseMasterkey.filePickerTitle=Vyberte soubor s hlavním klíčem
|
||||
## Success
|
||||
unlock.success.message=Trezor "%s" byl úspěšně odemčen a nyní je dostupný jako virtuální jednotka.
|
||||
@@ -114,6 +116,7 @@ unlock.error.heading=Nelze odemknout trezor
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Připojovací bod %s není složkou, není prázdný nebo neexistuje.
|
||||
unlock.error.invalidMountPoint.existing=Připojovací bod %s již existuje nebo nadřazená složka chybí.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Písmeno „%s“ už je používáno pro jiný disk.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
@@ -301,6 +304,7 @@ main.vaultDetail.missing.changeLocation=Změnit umístění trezoru…
|
||||
main.vaultDetail.migrateButton=Upgrade trezoru
|
||||
main.vaultDetail.migratePrompt=Váš trezor musí být aktualizován na nový formát, než k němu budete mít přístup
|
||||
### Error
|
||||
main.vaultDetail.error.info=Došlo k chybě při načítání trezoru z disku.
|
||||
main.vaultDetail.error.reload=Obnovit
|
||||
main.vaultDetail.error.windowTitle=Chyba při načítání trezoru
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ traymenu.vault.reveal=Anzeigen
|
||||
addvaultwizard.title=Tresor hinzufügen
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Neuen Tresor erstellen
|
||||
addvaultwizard.welcome.existingButton=Existierenden Tresor öffnen
|
||||
addvaultwizard.welcome.existingButton=Bestehenden Tresor öffnen
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Wähle einen Namen für den Tresor
|
||||
@@ -44,10 +44,10 @@ addvaultwizard.new.namePrompt=Tresorname
|
||||
addvaultwizard.new.locationInstruction=Wo soll Cryptomator die verschlüsselten Dateien deines Tresors ablegen?
|
||||
addvaultwizard.new.locationLabel=Speicherort
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Eigener Ort
|
||||
addvaultwizard.new.directoryPickerLabel=Benutzerdefinierter Ort
|
||||
addvaultwizard.new.directoryPickerButton=Durchsuchen …
|
||||
addvaultwizard.new.directoryPickerTitle=Verzeichnis auswählen
|
||||
addvaultwizard.new.fileAlreadyExists=Eine Datei oder ein Ordner mit diesem Namen ist bereits vorhanden
|
||||
addvaultwizard.new.fileAlreadyExists=Eine Datei oder ein Ordner dieses Namens besteht bereits
|
||||
addvaultwizard.new.locationDoesNotExist=Ein Ordner im angegebenen Pfad existiert nicht oder kann nicht geöffnet werden
|
||||
addvaultwizard.new.locationIsNotWritable=Kein Schreibzugriff auf den angegebenen Pfad
|
||||
addvaultwizard.new.locationIsOk=Geeigneter Ort für deinen Tresor
|
||||
@@ -59,7 +59,7 @@ addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja bitte, sicher ist sicher
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Nein danke, ich werde mein Passwort nicht verlieren
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=WICHTIG.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ TRESOR-DATEIEN ⚠️
|
||||
addvault.new.readme.storageLocation.1=⚠️ TRESORDATEIEN ⚠️
|
||||
addvault.new.readme.storageLocation.2=Dies ist der Speicherort deines Tresors.
|
||||
addvault.new.readme.storageLocation.3=NICHT
|
||||
addvault.new.readme.storageLocation.4=• Dateien in diesem Verzeichnis ändern oder
|
||||
@@ -68,16 +68,16 @@ addvault.new.readme.storageLocation.6=Falls du Dateien verschlüsseln und den In
|
||||
addvault.new.readme.storageLocation.7=1. Füge diesen Tresor zu Cryptomator hinzu.
|
||||
addvault.new.readme.storageLocation.8=2. Entsperre den Tresor in Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Öffne den Zugangsort durch Klicken auf die Schaltfläche „Anzeigen“.
|
||||
addvault.new.readme.storageLocation.10=Falls Du Hilfe brauchst, lies die Dokumentation: %s
|
||||
addvault.new.readme.storageLocation.10=Falls du Hilfe brauchst, lies die Dokumentation: %s
|
||||
addvault.new.readme.accessLocation.fileName=WILLKOMMEN.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ VERSCHLÜSSELTES LAUFWERK 🔐️
|
||||
addvault.new.readme.accessLocation.2=Dies ist der Zugangsort deines Tresors.
|
||||
addvault.new.readme.accessLocation.3=Alle zu diesem Laufwerk hinzugefügten Dateien werden von Cryptomator verschlüsselt. Du kannst mit diesem arbeiten wie mit jedem anderen Laufwerk bzw. Ordner. Dies ist lediglich eine unverschlüsselte Ansicht des Laufwerkinhalts; auf deiner Festplatte bleiben deine Dateien weiterhin verschlüsselt.
|
||||
addvault.new.readme.accessLocation.4=Diese Datei kannst du löschen.
|
||||
addvault.new.readme.accessLocation.4=Du kannst diese Datei löschen.
|
||||
## Existing
|
||||
addvaultwizard.existing.instruction=Wähle die Datei "vault.cryptomator" deines bestehenden Tresors aus. Falls nur eine Datei mit der Bezeichnung "masterkey.cryptomator" vorhanden ist, nutze stattdessen diese.
|
||||
addvaultwizard.existing.chooseBtn=Durchsuchen…
|
||||
addvaultwizard.existing.filePickerTitle=Tresor Datei auswählen
|
||||
addvaultwizard.existing.instruction=Wähle die Datei „vault.cryptomator“ deines bestehenden Tresors aus. Falls nur eine Datei mit der Bezeichnung „masterkey.cryptomator“ vorhanden ist, nutze stattdessen diese.
|
||||
addvaultwizard.existing.chooseBtn=Durchsuchen …
|
||||
addvaultwizard.existing.filePickerTitle=Tresordatei auswählen
|
||||
## Success
|
||||
addvaultwizard.success.nextStepsInstructions=Tresor „%s“ hinzugefügt.\nUm auf Inhalte zuzugreifen oder welche hinzuzufügen, musst du den Tresor entsperren. Du kannst ihn aber auch zu jedem späteren Zeitpunkt entsperren.
|
||||
addvaultwizard.success.unlockNow=Jetzt entsperren
|
||||
@@ -98,22 +98,25 @@ forgetPassword.information=Dies löscht das gespeicherte Passwort dieses Tresors
|
||||
forgetPassword.confirmBtn=Passwort vergessen
|
||||
|
||||
# Unlock
|
||||
unlock.title="%s" entsperren
|
||||
unlock.title=„%s“ entsperren
|
||||
unlock.passwordPrompt=Gib das Passwort für „%s“ ein:
|
||||
unlock.savePassword=Passwort merken
|
||||
unlock.unlockBtn=Entsperren
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Masterkey von „%s“ auswählen
|
||||
unlock.chooseMasterkey.prompt=Die Masterkey-Datei dieses Tresors konnte nicht gefunden werden. Bitte wähle die Masterkey-Datei manuell aus.
|
||||
unlock.chooseMasterkey.chooseBtn=Durchsuchen …
|
||||
unlock.chooseMasterkey.filePickerTitle=Masterkey-Datei auswählen
|
||||
## Success
|
||||
unlock.success.message=„%s“ erfolgreich entsperrt! Nun kannst du über das virtuelle Laufwerk auf deinen Tresor zugreifen.
|
||||
unlock.success.message=„%s“ erfolgreich entsperrt! Du kannst nun über das virtuelle Laufwerk auf deinen Tresor zugreifen.
|
||||
unlock.success.rememberChoice=Auswahl speichern und nicht mehr anzeigen
|
||||
unlock.success.revealBtn=Laufwerk anzeigen
|
||||
## Failure
|
||||
unlock.error.heading=Tresor konnte nicht entsperrt werden
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Einhängepunkt ist kein leeres Verzeichnis oder existiert nicht: %s
|
||||
unlock.error.invalidMountPoint.existing=Einhängepunkt/Ordner bereits vorhanden oder übergeordneter Ordner fehlt: %s.
|
||||
unlock.error.invalidMountPoint.notExisting=Einhängepunkt „%s“ ist kein leeres Verzeichnis oder existiert nicht.
|
||||
unlock.error.invalidMountPoint.existing=Einhängepunkt „%s“ besteht bereits oder übergeordneter Ordner fehlt.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Laufwerksbuchstabe „%s“ wird bereits verwendet.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
@@ -151,11 +154,11 @@ migration.impossible.moreInfo=Der Tresor kann auch jetzt noch mit einer älteren
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
health.title=Integritätstest von "%s"
|
||||
health.intro.header=Zustandsprüfung
|
||||
health.intro.text=Der Zustandscheck ist eine Sammlung von Tests, um Probleme mit der internen Struktur deines Tresores zu finden und möglicherweise zu reparieren. Bitte bedenke:
|
||||
health.title=Integritätsprüfung von „%s“
|
||||
health.intro.header=Integritätsprüfung
|
||||
health.intro.text=Die Integritätsprüfung ist eine Sammlung von Tests, um Probleme mit der internen Struktur deines Tresors zu finden und möglicherweise zu reparieren. Bitte bedenke:
|
||||
health.intro.remarkSync=Stelle sicher, dass alle Geräte vollständig synchronisiert sind. Dies löst die meisten Probleme.
|
||||
health.intro.remarkFix=Nicht alle Probleme können gelöst werden.
|
||||
health.intro.remarkFix=Nicht alle Probleme können behoben werden.
|
||||
health.intro.remarkBackup=Wenn Daten beschädigt sind, kann nur ein Backup helfen.
|
||||
health.intro.affirmation=Ich habe die obenstehende Information gelesen und verstanden
|
||||
## Start Failure
|
||||
@@ -164,24 +167,24 @@ health.fail.ioError=Beim Lesezugriff auf die Konfigurationsdatei ist ein Fehler
|
||||
health.fail.parseError=Beim Parsen der Tresor-Konfiguration ist ein Fehler aufgetreten.
|
||||
health.fail.moreInfo=Weitere Informationen
|
||||
## Check Selection
|
||||
health.checkList.description=Markiere Prüfungen in der linken Liste oder benutze die Knöpfe darunter.
|
||||
health.checkList.description=Markiere Prüfungen in der linken Liste oder benutze die Schaltflächen darunter.
|
||||
health.checkList.selectAllButton=Alle Prüfungen auswählen
|
||||
health.checkList.deselectAllButton=Alle Prüfungen abwählen
|
||||
health.check.runBatchBtn=Ausgewählte Prüfungen ausführen
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=Wähle für die Ergebnisse eine abgeschlossene Integritätsprüfung in der Liste links aus.
|
||||
health.check.detail.noSelectedCheck=Wähle für die Ergebnisse eine abgeschlossene Intregritätsprüfung in der Liste links aus.
|
||||
health.check.detail.checkScheduled=Die Prüfung ist geplant.
|
||||
health.check.detail.checkRunning=Prüfung läuft…
|
||||
health.check.detail.checkRunning=Die Prüfung läuft derzeit …
|
||||
health.check.detail.checkSkipped=Die Prüfung wurde nicht zur Ausführung ausgewählt.
|
||||
health.check.detail.checkFinished=Die Prüfung wurde erfolgreich abgeschlossen.
|
||||
health.check.detail.checkFinishedAndFound=Die Überprüfung wurde beendet. Bitte sichte die Ergebnisse.
|
||||
health.check.detail.checkFailed=Die Prüfung wurde wegen eines Fehlers beendet.
|
||||
health.check.detail.checkFinishedAndFound=Die Prüfung wurde beendet. Bitte überprüfe die Ergebnisse.
|
||||
health.check.detail.checkFailed=Die Prüfung wurde wegen eines Fehlers abgebrochen.
|
||||
health.check.detail.checkCancelled=Die Prüfung wurde abgebrochen.
|
||||
health.check.exportBtn=Bericht exportieren
|
||||
## Fix Application
|
||||
health.fix.fixBtn=Beheben
|
||||
health.fix.successTip=Fehlerbehebung erfolgreich
|
||||
health.fix.failTip=Reparatur fehlgeschlagen, siehe Log für Details
|
||||
health.fix.failTip=Reparatur fehlgeschlagen, siehe Protokoll für Details
|
||||
|
||||
# Preferences
|
||||
preferences.title=Einstellungen
|
||||
@@ -216,7 +219,7 @@ preferences.updates.updateAvailable=Update auf Version %s verfügbar.
|
||||
## Contribution
|
||||
preferences.contribute=Unterstütze uns
|
||||
preferences.contribute.registeredFor=Supporter-Zertifikat registriert für %s
|
||||
preferences.contribute.noCertificate=Unterstütze Cryptomator und erhalte ein Supporter-Zertifikat. Es ist wie ein Lizenzschlüssel, aber für großartige Menschen, die freie Software verwenden. ;-)
|
||||
preferences.contribute.noCertificate=Unterstütze Cryptomator und erhalte ein Supporter-Zertifikat. Es ist eine Art Lizenzschlüssel, aber für großartige Menschen, die freie Software verwenden. ;-)
|
||||
preferences.contribute.getCertificate=Du hast noch keines? Erfahre, wie du es erhalten kannst.
|
||||
preferences.contribute.promptText=Code des Supporter-Zertifikats hier einfügen
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
@@ -228,9 +231,9 @@ preferences.about=Über
|
||||
stats.title=Statistiken für %s
|
||||
stats.cacheHitRate=Cache-Trefferrate
|
||||
## Read
|
||||
stats.read.throughput.idle=Lesen: Leerlauf
|
||||
stats.read.throughput.kibs=Lesen: %.2f kiB/s
|
||||
stats.read.throughput.mibs=Lesen: %.2f MiB/s
|
||||
stats.read.throughput.idle=Leserate: inaktiv
|
||||
stats.read.throughput.kibs=Leserate: %.2f kiB/s
|
||||
stats.read.throughput.mibs=Leserate: %.2f MiB/s
|
||||
stats.read.total.data.none=Gelesen: -
|
||||
stats.read.total.data.kib=Gelesen: %.1f kiB
|
||||
stats.read.total.data.mib=Gelesen: %.1f MiB
|
||||
@@ -241,9 +244,9 @@ stats.decr.total.data.mib=Entschlüsselt: %.1f MiB
|
||||
stats.decr.total.data.gib=Entschlüsselt: %.1f GiB
|
||||
stats.read.accessCount=Lesezugriffe: %d
|
||||
## Write
|
||||
stats.write.throughput.idle=Schreiben: Leerlauf
|
||||
stats.write.throughput.kibs=Schreiben: %.2f kiB/s
|
||||
stats.write.throughput.mibs=Schreiben: %.2f MiB/s
|
||||
stats.write.throughput.idle=Schreibrate: inaktiv
|
||||
stats.write.throughput.kibs=Schreibrate: %.2f kiB/s
|
||||
stats.write.throughput.mibs=Schreibrate: %.2f MiB/s
|
||||
stats.write.total.data.none=Geschrieben: -
|
||||
stats.write.total.data.kib=Geschrieben: %.1f kiB
|
||||
stats.write.total.data.mib=Geschrieben: %.1f MiB
|
||||
@@ -286,9 +289,9 @@ main.vaultDetail.unlockedStatus=ENTSPERRT
|
||||
main.vaultDetail.accessLocation=Dein Tresorinhalt ist hier erreichbar:
|
||||
main.vaultDetail.revealBtn=Laufwerk anzeigen
|
||||
main.vaultDetail.lockBtn=Sperren
|
||||
main.vaultDetail.bytesPerSecondRead=Gelesen:
|
||||
main.vaultDetail.bytesPerSecondWritten=Schreiben:
|
||||
main.vaultDetail.throughput.idle=Leerlauf
|
||||
main.vaultDetail.bytesPerSecondRead=Leserate:
|
||||
main.vaultDetail.bytesPerSecondWritten=Schreibrate:
|
||||
main.vaultDetail.throughput.idle=inaktiv
|
||||
main.vaultDetail.throughput.kbps=%.1f kiB/s
|
||||
main.vaultDetail.throughput.mbps=%.1f MiB/s
|
||||
main.vaultDetail.stats=Tresorstatistik
|
||||
@@ -302,7 +305,7 @@ main.vaultDetail.migrateButton=Tresor aktualisieren
|
||||
main.vaultDetail.migratePrompt=Dein Tresor muss auf ein neues Format aktualisiert werden, bevor du auf ihn zugreifen kannst
|
||||
### Error
|
||||
main.vaultDetail.error.info=Beim Laden des Tresors von der Festplatte ist ein Fehler aufgetreten.
|
||||
main.vaultDetail.error.reload=Erneut laden
|
||||
main.vaultDetail.error.reload=Neu laden
|
||||
main.vaultDetail.error.windowTitle=Fehler beim Laden des Tresors
|
||||
|
||||
# Wrong File Alert
|
||||
@@ -334,7 +337,7 @@ vaultOptions.mount.readonly=Schreibgeschützt
|
||||
vaultOptions.mount.customMountFlags=Benutzerdefinierte Einhänge-Optionen
|
||||
vaultOptions.mount.winDriveLetterOccupied=belegt
|
||||
vaultOptions.mount.mountPoint=Einhängepunkt
|
||||
vaultOptions.mount.mountPoint.auto=Wähle automatisch einen geeigneten Ort aus
|
||||
vaultOptions.mount.mountPoint.auto=Automatisch einen geeigneten Ort auswählen
|
||||
vaultOptions.mount.mountPoint.driveLetter=Laufwerksbuchstaben zuweisen
|
||||
vaultOptions.mount.mountPoint.custom=Eigener Pfad
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Durchsuchen …
|
||||
@@ -343,7 +346,7 @@ vaultOptions.mount.mountPoint.directoryPickerTitle=Wähle ein leeres Verzeichnis
|
||||
vaultOptions.masterkey=Passwort
|
||||
vaultOptions.masterkey.changePasswordBtn=Passwort ändern
|
||||
vaultOptions.masterkey.forgetSavedPasswordBtn=Gespeichertes Passwort vergessen
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=Dein Wiederherstellungsschlüssel gehört nur dir! Dieser wird benötigt, um deinen Tresor wiederherzustellen, für den Fall das du dein Passwort verloren hast.
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=Ein Wiederherstellungsschlüssel ist bei Passwortverlust deine einzige Möglichkeit, den Zugriff auf einen Tresor wiederherzustellen.
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=Wiederherstellungsschlüssel anzeigen
|
||||
vaultOptions.masterkey.recoverPasswordBtn=Passwort wiederherstellen
|
||||
|
||||
|
||||
@@ -102,8 +102,10 @@ unlock.title=Ξεκλειδώστε "%s"
|
||||
unlock.passwordPrompt=Εισάγετε τον κωδικό για "%s":
|
||||
unlock.savePassword=Απομνημόνευση κωδικού πρόσβασης
|
||||
unlock.unlockBtn=Ξεκλείδωμα
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Επιλέξτε το Masterkey του "%s"
|
||||
unlock.chooseMasterkey.prompt=Αδυναμία εύρεσης του αρχείου masterkey για αυτό το vault στην αναμενόμενη τοποθεσία. Παρακαλώ επιλέξτε το αρχείο χειροκίνητα.
|
||||
unlock.chooseMasterkey.chooseBtn=Επιλογή…
|
||||
unlock.chooseMasterkey.filePickerTitle=Επιλέξτε το αρχείο Masterkey
|
||||
## Success
|
||||
unlock.success.message=Ξεκλειδώθηκε "%s" επιτυχώς! Το vault σας είναι διαθέσιμο μέσω του εικονικού δίσκου του.
|
||||
@@ -114,6 +116,7 @@ unlock.error.heading=Αδυναμία ξεκλειδώματος vault
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Το σημείο προσάρτησης δεν είναι κενός φάκελος ή δεν υπάρχει: %s
|
||||
unlock.error.invalidMountPoint.existing=Το σημείο/φάκελος προσάρτησης υπάρχει ήδη ή ο γονικός φάκελος λείπει: %s
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Το Γράμμα Δίσκου "%s" χρησιμοποιείται ήδη.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
|
||||
@@ -68,7 +68,7 @@ addvault.new.readme.storageLocation.6=Cuando se quiere cifrar archivos y ver el
|
||||
addvault.new.readme.storageLocation.7=1. Agregar esta bóveda a Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. Desbloquear la bóveda en Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Abrir el lugar de acceso haciendo clic en el botón "Revelar".
|
||||
addvault.new.readme.storageLocation.10=Si se necesita ayuda, visitar la documentación: %s
|
||||
addvault.new.readme.storageLocation.10=Si necesita ayuda, visite la documentación: %s
|
||||
addvault.new.readme.accessLocation.fileName=BIENVENIDA.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ VOLUMEN CIFRADO 🔐️
|
||||
addvault.new.readme.accessLocation.2=Este es el lugar de acceso de la bóveda.
|
||||
@@ -102,8 +102,10 @@ unlock.title=Desbloquear "%s"
|
||||
unlock.passwordPrompt=Ingresar contraseña para "%s":
|
||||
unlock.savePassword=Recordar contraseña
|
||||
unlock.unlockBtn=Desbloquear
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Seleccionar clave maestra de "%s"
|
||||
unlock.chooseMasterkey.prompt=No se pudo encontrar el archivo de la clave maestra de esta bóveda en la ubicación esperada. Por favor, elija manualmente el archivo de la clave.
|
||||
unlock.chooseMasterkey.chooseBtn=Elegir…
|
||||
unlock.chooseMasterkey.filePickerTitle=Seleccione el archivo de la clave maestra
|
||||
## Success
|
||||
unlock.success.message=¡Desbloqueo de "%s" exitoso! Su bóveda ahora es accesible a través de su unidad virtual.
|
||||
@@ -112,8 +114,9 @@ unlock.success.revealBtn=Revelar unidad
|
||||
## Failure
|
||||
unlock.error.heading=No se puede desbloquear la bóveda
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=El punto de montaje no es un directorio vacío o no existe: %s
|
||||
unlock.error.invalidMountPoint.existing=El punto de montaje/carpeta ya existe o falta la carpeta padre: %s
|
||||
unlock.error.invalidMountPoint.notExisting=El punto de montaje "%s"no es un directorio, no está vacío o no existe.
|
||||
unlock.error.invalidMountPoint.existing=El punto de montaje "%s" ya existe o falta la carpeta padre.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=La letra de unidad "%s" ya está en uso.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
@@ -122,7 +125,7 @@ lock.forced.message=El bloqueo de "%s" fue bloqueado por operaciones pendientes
|
||||
lock.forced.retryBtn=Reintentar
|
||||
lock.forced.forceBtn=Forzar bloqueo
|
||||
## Failure
|
||||
lock.fail.heading=Falló al bloquear la bóveda.
|
||||
lock.fail.heading=Error al bloquear la bóveda.
|
||||
lock.fail.message=No se pudo bloquear la bóveda "%s". Asegúrese de que el trabajo no guardado se ha guardado en otro lugar y las operaciones de lectura/escritura importantes han finalizado. Para cerrar la bóveda termine el proceso de Cryptomator.
|
||||
|
||||
# Migration
|
||||
@@ -169,7 +172,7 @@ health.checkList.selectAllButton=Seleccionar todas las comprobaciones
|
||||
health.checkList.deselectAllButton=Deseleccionar todas las comprobaciones
|
||||
health.check.runBatchBtn=Ejecutar las comprobaciones seleccionadas
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=Para los resultados seleccione una comprobación del estado en la lista de la izquierda
|
||||
health.check.detail.noSelectedCheck=Para los resultados seleccione una comprobación del estado finalizada en la lista de la izquierda.
|
||||
health.check.detail.checkScheduled=La comprobación está programada.
|
||||
health.check.detail.checkRunning=La comprobación se está ejecutando…
|
||||
health.check.detail.checkSkipped=No se ha seleccionado la comprobación para ejecutarse.
|
||||
@@ -217,7 +220,7 @@ preferences.updates.updateAvailable=Actualización a la versión %s disponible.
|
||||
preferences.contribute=Apóyenos
|
||||
preferences.contribute.registeredFor=Certificado de soporte registrado para %s
|
||||
preferences.contribute.noCertificate=Apoye a Cryptomator y reciba un certificado de seguidor. Es como una clave de licencia, pero para gente asombrosa usando software libre. ;-)
|
||||
preferences.contribute.getCertificate=¿Aún no tiene una? Aprenda cómo puede obtenerlo.
|
||||
preferences.contribute.getCertificate=¿Aún no tiene uno? Aprenda cómo puede obtenerlo.
|
||||
preferences.contribute.promptText=Pegue aquí el código de certificado de seguidor
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
@@ -244,7 +247,7 @@ stats.read.accessCount=Total leídos: %d
|
||||
stats.write.throughput.idle=Escritura: inactivo
|
||||
stats.write.throughput.kibs=Escritura: %.2f kiB/s
|
||||
stats.write.throughput.mibs=Escritura: %.2f MiB/s
|
||||
stats.write.total.data.none=Datos escritos:-
|
||||
stats.write.total.data.none=Datos escritos: -
|
||||
stats.write.total.data.kib=Datos escritos: %.1f kiB
|
||||
stats.write.total.data.mib=Datos escritos: %.1f MiB
|
||||
stats.write.total.data.gib=Datos escritos: %.1f GiB
|
||||
@@ -259,12 +262,12 @@ main.closeBtn.tooltip=Cerrar
|
||||
main.minimizeBtn.tooltip=Minimizar
|
||||
main.preferencesBtn.tooltip=Preferencias
|
||||
main.debugModeEnabled.tooltip=Modo de depuración activado
|
||||
main.donationKeyMissing.tooltip=Por favor, considera donar
|
||||
main.donationKeyMissing.tooltip=Por favor, considere donar
|
||||
## Drag 'n' Drop
|
||||
main.dropZone.dropVault=Añadir esta bóveda
|
||||
main.dropZone.unknownDragboardContent=Si desea añadir una bóveda, arrástrela a esta ventana
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Hacer clic aquí para añadir una bóveda
|
||||
main.vaultlist.emptyList.onboardingInstruction=Haga clic aquí para añadir una bóveda
|
||||
main.vaultlist.contextMenu.remove=Eliminar…
|
||||
main.vaultlist.contextMenu.lock=Bloquear
|
||||
main.vaultlist.contextMenu.unlock=Desbloquear…
|
||||
@@ -274,9 +277,9 @@ main.vaultlist.contextMenu.reveal=Revelar unidad
|
||||
main.vaultlist.addVaultBtn=Añadir bóveda
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger los archivos. En caso de necesitar ayuda, revisar nuestras guías:
|
||||
main.vaultDetail.welcomeOnboarding=Gracias por elegir Cryptomator para proteger sus archivos. En caso de necesitar ayuda, revise nuestras guías:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=BLOQUEADO
|
||||
main.vaultDetail.lockedStatus=BLOQUEADA
|
||||
main.vaultDetail.unlockBtn=Desbloquear…
|
||||
main.vaultDetail.unlockNowBtn=Desbloquear ahora
|
||||
main.vaultDetail.optionsBtn=Opciones de la bóveda
|
||||
@@ -286,7 +289,7 @@ main.vaultDetail.unlockedStatus=DESBLOQUEADO
|
||||
main.vaultDetail.accessLocation=El contenido de la bóveda es accesible aquí:
|
||||
main.vaultDetail.revealBtn=Revelar unidad
|
||||
main.vaultDetail.lockBtn=Bloquear
|
||||
main.vaultDetail.bytesPerSecondRead=Leído:
|
||||
main.vaultDetail.bytesPerSecondRead=Lectura:
|
||||
main.vaultDetail.bytesPerSecondWritten=Escritura:
|
||||
main.vaultDetail.throughput.idle=inactivo
|
||||
main.vaultDetail.throughput.kbps=%.1f kiB/s
|
||||
@@ -298,7 +301,7 @@ main.vaultDetail.missing.recheck=Volver a comprobar
|
||||
main.vaultDetail.missing.remove=Eliminar de la lista de bóveda…
|
||||
main.vaultDetail.missing.changeLocation=Cambiar ubicación de la bóveda…
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=Mejorar bóveda
|
||||
main.vaultDetail.migrateButton=Actualizar bóveda
|
||||
main.vaultDetail.migratePrompt=Su bóveda necesita ser actualizada a un formato nuevo antes de poder acceder a ella
|
||||
### Error
|
||||
main.vaultDetail.error.info=Se produjo un error al cargar la bóveda del disco.
|
||||
@@ -306,13 +309,13 @@ main.vaultDetail.error.reload=Recargar
|
||||
main.vaultDetail.error.windowTitle=Error al cargar la bóveda
|
||||
|
||||
# Wrong File Alert
|
||||
wrongFileAlert.title=Cómo encriptar archivos
|
||||
wrongFileAlert.header.title=¿Se intentan cifrar estos archivos?
|
||||
wrongFileAlert.title=Cómo cifrar archivos
|
||||
wrongFileAlert.header.title=¿Intenta cifrar estos archivos?
|
||||
wrongFileAlert.header.lead=Para este propósito, Cryptomator proporciona un volumen en su administrador de archivos del sistema.
|
||||
wrongFileAlert.instruction.0=Para cifrar archivos, seguir estos pasos:
|
||||
wrongFileAlert.instruction.1=1. Desbloquear la bóveda.
|
||||
wrongFileAlert.instruction.2=2. Hacer clic en "Revelar" para abrir el volumen en el administrador de archivos.
|
||||
wrongFileAlert.instruction.3=3. Añadir los archivos a este volumen.
|
||||
wrongFileAlert.instruction.0=Para cifrar archivos, siga estos pasos:
|
||||
wrongFileAlert.instruction.1=1. Desbloquee su bóveda.
|
||||
wrongFileAlert.instruction.2=2. Haga clic en "Revelar" para abrir el volumen en el administrador de archivos.
|
||||
wrongFileAlert.instruction.3=3. Añada los archivos a este volumen.
|
||||
wrongFileAlert.link=Para más ayuda, visite
|
||||
|
||||
# Vault Options
|
||||
@@ -322,7 +325,7 @@ vaultOptions.general.vaultName=Nombre de la bóveda
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=Bloquear después de
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=minutos
|
||||
vaultOptions.general.unlockAfterStartup=Desbloquear bóveda al iniciar Cryptomator
|
||||
vaultOptions.general.actionAfterUnlock=Después de desbloquear exitosamente
|
||||
vaultOptions.general.actionAfterUnlock=Después de desbloquear con éxito
|
||||
vaultOptions.general.actionAfterUnlock.ignore=No hacer nada
|
||||
vaultOptions.general.actionAfterUnlock.reveal=Revelar unidad
|
||||
vaultOptions.general.actionAfterUnlock.ask=Preguntar
|
||||
@@ -362,7 +365,7 @@ newPassword.promptText=Ingrese una contraseña nueva
|
||||
newPassword.reenterPassword=Confirme la contraseña nueva
|
||||
newPassword.passwordsMatch=¡Las contraseñas coinciden!
|
||||
newPassword.passwordsDoNotMatch=Las contraseñas no coinciden
|
||||
passwordStrength.messageLabel.tooShort=Usar al menos %d caracteres
|
||||
passwordStrength.messageLabel.tooShort=Use al menos %d caracteres
|
||||
passwordStrength.messageLabel.0=Muy débil
|
||||
passwordStrength.messageLabel.1=Débil
|
||||
passwordStrength.messageLabel.2=Aceptable
|
||||
|
||||
@@ -61,7 +61,8 @@ addvaultwizard.existing.chooseBtn=Mamili…
|
||||
|
||||
# Unlock
|
||||
unlock.unlockBtn=I-unlock
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=Mamili…
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
@@ -63,7 +63,7 @@ addvault.new.readme.storageLocation.1=Fichiers de coffre-fort
|
||||
addvault.new.readme.storageLocation.2=Ceci est le chemin de votre coffre-fort.
|
||||
addvault.new.readme.storageLocation.3=NE PAS
|
||||
addvault.new.readme.storageLocation.4=Modifier n'importe quel fichier dans ce répertoire ou
|
||||
addvault.new.readme.storageLocation.5=Collez n'importe quel fichier à chiffrer dans ce répertoire.
|
||||
addvault.new.readme.storageLocation.5=• coller de fichier à chiffrer dans ce répertoire.
|
||||
addvault.new.readme.storageLocation.6=Si vous voulez chiffrer les fichiers et afficher le contenu du coffre, faites ce qui suit :
|
||||
addvault.new.readme.storageLocation.7=1. Ajouter ce coffre à Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. Déverrouillez le coffre-fort dans Cryptomator.
|
||||
@@ -102,8 +102,10 @@ unlock.title=Déverrouiller %s
|
||||
unlock.passwordPrompt=Entrez le mot de passe pour “%s” :
|
||||
unlock.savePassword=Mémoriser le mot de passe
|
||||
unlock.unlockBtn=Déverrouiller
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Sélectionner la clé principale de "%s"
|
||||
unlock.chooseMasterkey.prompt=Impossible de trouver le fichier clef à l'adresse attendue pour ce coffre. Veuillez sélectionner le fichier clef manuellement.
|
||||
unlock.chooseMasterkey.chooseBtn=Choisir...
|
||||
unlock.chooseMasterkey.filePickerTitle=Sélectionner le fichier clef
|
||||
## Success
|
||||
unlock.success.message=“%s” déverrouillé ! Le contenu de votre coffre est maintenant accessible par son lecteur virtuel.
|
||||
@@ -114,6 +116,7 @@ unlock.error.heading=Impossible de déverrouiller le coffre
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Le point de montage «%s» n'est pas un répertoire, n'est pas vide ou n'existe pas.
|
||||
unlock.error.invalidMountPoint.existing=Le point de montage/le répertoire existe déjà ou le répertoire parent est manquant: %s
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Le lecteur "%s" est en déjà utilisé.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
@@ -318,7 +321,7 @@ wrongFileAlert.link=Pour toute aide supplémentaire, visitez
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general=Général
|
||||
vaultOptions.general.vaultName=Nom de voûte
|
||||
vaultOptions.general.vaultName=Nom du coffre-fort
|
||||
vaultOptions.general.autoLock.lockAfterTimePart1=Verrouiler en cas d'inactivité pendant
|
||||
vaultOptions.general.autoLock.lockAfterTimePart2=minutes
|
||||
vaultOptions.general.unlockAfterStartup=Déverrouiller le coffre au démarrage
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# Locale Specific CSS files such as CJK, RTL,...
|
||||
|
||||
# Generics
|
||||
## Button
|
||||
## Error
|
||||
|
||||
# Defaults
|
||||
|
||||
# Tray Menu
|
||||
|
||||
# Add Vault Wizard
|
||||
## Welcome
|
||||
## New
|
||||
### Name
|
||||
### Location
|
||||
### Password
|
||||
### Information
|
||||
## Existing
|
||||
## Success
|
||||
|
||||
# Remove Vault
|
||||
|
||||
# Change Password
|
||||
|
||||
# Forget Password
|
||||
|
||||
# Unlock
|
||||
## Select
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.retryBtn=Tentar de novo
|
||||
## Failure
|
||||
|
||||
# Migration
|
||||
## Start
|
||||
## Run
|
||||
## Success
|
||||
## Missing file system capabilities
|
||||
## Impossible
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
## Start Failure
|
||||
## Check Selection
|
||||
## Detail view
|
||||
## Fix Application
|
||||
|
||||
# Preferences
|
||||
## General
|
||||
## Volume
|
||||
## Updates
|
||||
## Contribution
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
|
||||
# Vault Statistics
|
||||
## Read
|
||||
## Write
|
||||
|
||||
# Main Window
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
### Unlocked
|
||||
### Missing
|
||||
### Needs Migration
|
||||
### Error
|
||||
|
||||
# Wrong File Alert
|
||||
|
||||
# Vault Options
|
||||
## General
|
||||
|
||||
## Mount
|
||||
## Master Key
|
||||
|
||||
|
||||
# Recovery Key
|
||||
|
||||
# New Password
|
||||
|
||||
# Quit
|
||||
@@ -32,7 +32,7 @@ traymenu.vault.lock=נעילה
|
||||
traymenu.vault.reveal=חשוף
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=הוסף כספת
|
||||
addvaultwizard.title=יצירת כספת
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=צור כספת חדשה
|
||||
addvaultwizard.welcome.existingButton=פתח כספת קיימת
|
||||
@@ -43,9 +43,10 @@ addvaultwizard.new.namePrompt=שם הכספת
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=היכן Cryptomator צריך לשמור את הקבצים המוצפנים של הכספת שלך?
|
||||
addvaultwizard.new.locationLabel=מיקום אחסון
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=מיקום מותאם אישית
|
||||
addvaultwizard.new.directoryPickerButton=בחר...
|
||||
addvaultwizard.new.directoryPickerTitle=בחירת ספרייה
|
||||
addvaultwizard.new.directoryPickerTitle=בחירת תיקייה
|
||||
addvaultwizard.new.fileAlreadyExists=שם הקובץ או שם התיקייה עם שם הכספת כבר קיים
|
||||
addvaultwizard.new.locationDoesNotExist=מחיצה בנתיב הנקוב לא קיימת או אין אפשרות לקבל אליה גישה
|
||||
addvaultwizard.new.locationIsNotWritable=אין הרשאת כתיבה בנתיב הנקוב
|
||||
@@ -74,32 +75,74 @@ addvault.new.readme.accessLocation.2=זהו מיקום גישה לכספת של
|
||||
addvault.new.readme.accessLocation.3=כל קובץ אשר יצורף לספרייה זו יעבור הצפנה באמצעות Cryptomator. את/ה תוכל/י לעבוד עליו כמו עם כל קבוץ/מחיצה רגילים. זהו מצב הצגה מפוענח של התוכן, הקבצים שלך נשארים מוצפנים על הדיסק הקשיח שלך בכל רגע.
|
||||
addvault.new.readme.accessLocation.4=תרגיש/י בנוח להסיר את הקובץ הזה.
|
||||
## Existing
|
||||
addvaultwizard.existing.instruction=בחר את קובץ "vault.cryptomator" של כספת קיימת. אם קיים קובץ בשם "masterkey.cryptomator" בלבד, בחר/י אותו במקום.
|
||||
addvaultwizard.existing.chooseBtn=בחר...
|
||||
addvaultwizard.existing.filePickerTitle=בחר קובץ כספת
|
||||
## Success
|
||||
addvaultwizard.success.nextStepsInstructions=נוספה כספת "%s".\nהנך צריך/ה לבטל נעילת כספת זו בכדי לקבל גישה או להוסיף קבצים. לחילופין תוכל/י לבטל נעילה בכל נקודת זמן מאוחרת יותר.
|
||||
addvaultwizard.success.unlockNow=בטל נעילה כעת
|
||||
|
||||
# Remove Vault
|
||||
removeVault.title=הסר כספת
|
||||
removeVault.information=זה יגרום ל-Cryptomator לשכוח מהכספת הזו. תוכל/י להוסיף אותה שוב מאוחר יותר. קבצים מוצפנים לא ימחקו מהכונן שלך.
|
||||
removeVault.confirmBtn=הסר כספת
|
||||
|
||||
# Change Password
|
||||
changepassword.title=שנה סיסמה
|
||||
changepassword.enterOldPassword=הקש את הסיסמה הנוכחית ל-"%s"
|
||||
changepassword.finalConfirmation=אני מבין/ה שלא אוכל לגשת למידע שלי במקרה ואשכח את הסיסמה שלי
|
||||
|
||||
# Forget Password
|
||||
forgetPassword.title=שכח סיסמה
|
||||
forgetPassword.information=זה ימחק את הסיסמה השמורה של הכספת הזו ממחזיק מפתחות המערכת.
|
||||
forgetPassword.confirmBtn=שכח סיסמה
|
||||
|
||||
# Unlock
|
||||
unlock.title=בטל/י נעילה ל-"%s"
|
||||
unlock.passwordPrompt=הכנס/י סיסמה ל-"%s":
|
||||
unlock.savePassword=זכור סיסמה
|
||||
unlock.unlockBtn=בטל נעילה
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=בחר/י מפתח מאסטר של "%s"
|
||||
unlock.chooseMasterkey.prompt=מפתח מאסטר של כספת זו לא נמצא במיקום הצפוי. בחר/י בבקשה את המפתח באופן ידני.
|
||||
unlock.chooseMasterkey.chooseBtn=בחר...
|
||||
unlock.chooseMasterkey.filePickerTitle=בחר/י קובץ מפתח מאסטר
|
||||
## Success
|
||||
unlock.success.message=הנעילה ל-"%s" בוטלה בהצלחה! הכספת שלך נגישה כעת דרך כונן ווירטואלי.
|
||||
unlock.success.rememberChoice=זכור בחירה, אל תראה שוב
|
||||
unlock.success.revealBtn=חשוף את הכונן
|
||||
## Failure
|
||||
unlock.error.heading=לא מתאפשר לבטל נעילת הכספת
|
||||
### Invalid Mount Point
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.heading=הנעילה נכשלה
|
||||
lock.forced.retryBtn=נסה שנית
|
||||
lock.forced.forceBtn=הכרח נעילה
|
||||
## Failure
|
||||
lock.fail.heading=נעילת הכספת נשכלה.
|
||||
|
||||
# Migration
|
||||
migration.title=שדרג הכספת
|
||||
## Start
|
||||
migration.start.confirm=כן, הכספת שלי מסונכרנת במלואה
|
||||
## Run
|
||||
migration.run.enterPassword=הכנס/י סיסמה עבור "%s"
|
||||
migration.run.startMigrationBtn=העבר הכספת
|
||||
migration.run.progressHint=הדבר יקח זמן מה…
|
||||
## Success
|
||||
migration.success.nextStepsInstructions="%s" הועברה בהצלחה.\nהנך יכול/ה לפתוח את הכספת שלך.
|
||||
migration.success.unlockNow=בטל נעילה כעת
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=מערכת קבצים אינה נתמכת
|
||||
migration.error.missingFileSystemCapabilities.description=העברה לא החלה, כיוון שהכספת שלך נמצאת במערכת קבצים לא מתאימה.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=מערכת קבצים אינה תומכת בשמות קבצים ארוכים.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=מערכת קבצים אינה תומכת בנתיבים ארוכים.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=מערכת הקבצים אינה מאפשרת את קריאתה.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=מערכת הקבצים אינה מאפשרת את הכתיבה אליה.
|
||||
## Impossible
|
||||
migration.impossible.heading=לא יכול להעביר את הכספת
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
@@ -128,14 +171,19 @@ main.preferencesBtn.tooltip=העדפות
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.lock=נעילה
|
||||
main.vaultlist.addVaultBtn=הוסף כספת
|
||||
main.vaultlist.contextMenu.unlockNow=בטל נעילה כעת
|
||||
main.vaultlist.contextMenu.reveal=חשוף את הכונן
|
||||
main.vaultlist.addVaultBtn=יצירת כספת
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
main.vaultDetail.unlockNowBtn=בטל נעילה כעת
|
||||
### Unlocked
|
||||
main.vaultDetail.revealBtn=חשוף את הכונן
|
||||
main.vaultDetail.lockBtn=נעילה
|
||||
### Missing
|
||||
### Needs Migration
|
||||
main.vaultDetail.migrateButton=שדרג הכספת
|
||||
### Error
|
||||
|
||||
# Wrong File Alert
|
||||
@@ -143,10 +191,12 @@ main.vaultDetail.lockBtn=נעילה
|
||||
# Vault Options
|
||||
## General
|
||||
vaultOptions.general.vaultName=שם הכספת
|
||||
vaultOptions.general.actionAfterUnlock.reveal=חשוף את הכונן
|
||||
|
||||
## Mount
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=בחר...
|
||||
## Master Key
|
||||
vaultOptions.masterkey.changePasswordBtn=שנה סיסמה
|
||||
|
||||
|
||||
# Recovery Key
|
||||
|
||||
@@ -58,6 +58,7 @@ addvault.new.readme.storageLocation.5=• • इस डायरेक्ट
|
||||
## Existing
|
||||
addvaultwizard.existing.chooseBtn=चुनें…
|
||||
## Success
|
||||
addvaultwizard.success.unlockNow=अब अनलॉक करें
|
||||
|
||||
# Remove Vault
|
||||
|
||||
@@ -68,7 +69,8 @@ changepassword.title=पासवर्ड बदलें
|
||||
|
||||
# Unlock
|
||||
unlock.unlockBtn=अनलॉक करें
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=चुनें…
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
@@ -83,6 +85,7 @@ migration.title=वाउल्ट को अपग्रेड करें
|
||||
## Start
|
||||
## Run
|
||||
## Success
|
||||
migration.success.unlockNow=अब अनलॉक करें
|
||||
## Missing file system capabilities
|
||||
## Impossible
|
||||
|
||||
@@ -114,10 +117,12 @@ main.preferencesBtn.tooltip=प्राथमिकताएं
|
||||
## Drag 'n' Drop
|
||||
## Vault List
|
||||
main.vaultlist.contextMenu.lock=लॉक करें
|
||||
main.vaultlist.contextMenu.unlockNow=अब अनलॉक करें
|
||||
main.vaultlist.addVaultBtn=वाउल्ट डालें
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
### Locked
|
||||
main.vaultDetail.unlockNowBtn=अब अनलॉक करें
|
||||
main.vaultDetail.optionsBtn=वॉल्ट के विकल्प
|
||||
### Unlocked
|
||||
main.vaultDetail.accessLocation=आपके वॉल्ट की चीजें यहाँ एक्सेस कर सकतें हैं:
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
# Forget Password
|
||||
|
||||
# Unlock
|
||||
##
|
||||
## Select
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
@@ -102,8 +102,9 @@ unlock.title="%s" feloldása
|
||||
unlock.passwordPrompt=Írja be a jelszavát a következő széfhez "%s":
|
||||
unlock.savePassword=Jelszó megjegyzése
|
||||
unlock.unlockBtn=Feloldás
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.prompt=Nem található a tároló kulcsfájlja a várt helyen. Kérjük válassza ki a kulcsfájlt manuálisan.
|
||||
unlock.chooseMasterkey.chooseBtn=Választás…
|
||||
unlock.chooseMasterkey.filePickerTitle=Mesterkulcs fájl kiválasztása
|
||||
## Success
|
||||
unlock.success.message="%s" sikreresen feloldásra került! Mostmár hozzáférhet a virtuális trezorhoz.
|
||||
|
||||
@@ -13,66 +13,66 @@ generic.button.done=Selesai
|
||||
generic.button.next=Lanjut
|
||||
generic.button.print=Cetak
|
||||
## Error
|
||||
generic.error.title=Kesalahan %s
|
||||
generic.error.instruction=Ups! Cryptomator tidak mengharapkan ini terjadi. Anda dapat mencari solusi yang ada untuk kesalahan ini. Atau jika belum dilaporkan, jangan ragu untuk melakukannya.
|
||||
generic.error.hyperlink.lookup=Cari kesalahan
|
||||
generic.error.hyperlink.report=Laporkan kesalahan
|
||||
generic.error.title=Error %s
|
||||
generic.error.instruction=Ups! Cryptomator tidak menyangka hal ini terjadi. Anda dapat mencari solusi yang tersedia untuk error ini. Atau jika error ini belum pernah dilaporkan, tidak perlu sungkan untuk melaporkannya.
|
||||
generic.error.hyperlink.lookup=Cari error berikut
|
||||
generic.error.hyperlink.report=Laporkan error berikut
|
||||
generic.error.technicalDetails=Rincian:
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Brankas
|
||||
defaults.vault.vaultName=Vault
|
||||
|
||||
# Tray Menu
|
||||
traymenu.showMainWindow=Tampilkan
|
||||
traymenu.showPreferencesWindow=Preferensi
|
||||
traymenu.lockAllVaults=Gembok Semua
|
||||
traymenu.lockAllVaults=Kunci Semua
|
||||
traymenu.quitApplication=Keluar
|
||||
traymenu.vault.unlock=Buka Gembok
|
||||
traymenu.vault.lock=Gembok
|
||||
traymenu.vault.unlock=Buka Kunci
|
||||
traymenu.vault.lock=Kunci
|
||||
traymenu.vault.reveal=Perlihatkan
|
||||
|
||||
# Add Vault Wizard
|
||||
addvaultwizard.title=Tambah Brankas
|
||||
addvaultwizard.title=Tambah Vault
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=Buat Brankas Baru
|
||||
addvaultwizard.welcome.existingButton=Buka Brankas yg Sudah Ada
|
||||
addvaultwizard.welcome.newButton=Buat Vault Baru
|
||||
addvaultwizard.welcome.existingButton=Buka Vault yang Tersedia
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Buat sebuah nama untuk brankas
|
||||
addvaultwizard.new.namePrompt=Nama Brankas
|
||||
addvaultwizard.new.nameInstruction=Beri nama untuk vault berikut
|
||||
addvaultwizard.new.namePrompt=Nama Vault
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=Dimana Cryptomator seharusnya menyimpan brankas berkas terenkripsi kamu?
|
||||
addvaultwizard.new.locationInstruction=Dimana Cryptomator seharusnya menyimpan vault berisi file enkripsi Anda?
|
||||
addvaultwizard.new.locationLabel=Lokasi penyimpanan
|
||||
addvaultwizard.new.locationPrompt=…
|
||||
addvaultwizard.new.directoryPickerLabel=Sesuaikan Lokasi
|
||||
addvaultwizard.new.directoryPickerLabel=Lokasi khusus
|
||||
addvaultwizard.new.directoryPickerButton=Pilih…
|
||||
addvaultwizard.new.directoryPickerTitle=Pilih Folder
|
||||
addvaultwizard.new.fileAlreadyExists=Sudah ada file atau direktori dengan nama yang sama
|
||||
addvaultwizard.new.locationDoesNotExist=Direktori pada path yang dipilih tidak ada atau tidak dapat diakses
|
||||
addvaultwizard.new.directoryPickerTitle=Pilih Direktori
|
||||
addvaultwizard.new.fileAlreadyExists=Sudah ada file atau direktori dengan nama vault tersebut
|
||||
addvaultwizard.new.locationDoesNotExist=Direktori pada path yang dipilih tidak tersedia atau tidak dapat diakses
|
||||
addvaultwizard.new.locationIsNotWritable=Anda tidak memiliki hak akses untuk menulis pada path yang dipilih
|
||||
addvaultwizard.new.locationIsOk=Lokasi yang sesuai dengan vault Anda
|
||||
addvaultwizard.new.invalidName=Nama brankas salah. Harap pilih nama folder yang umum.
|
||||
addvaultwizard.new.invalidName=Nama vault tidak sesuai. Harap pilih nama direktori yang umum digunakan.
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=Buat Brankas
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Kamu tidak dapat mengakses data tanpa kata sandi kamu. Apa kamu ingin sebuah kunci pemulihan buat jaga-jaga?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ya, sedia payung sebelum hujan
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Tidak, terima kasih, Saya tidak akan kehilangan kata sandi saya
|
||||
addvaultwizard.new.createVaultBtn=Buat Vault
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=Anda tidak dapat mengakses data tanpa kata sandi yang Anda miliki. Apa Anda ingin sebuah kunci pemulihan untuk berjaga-jaga jika seandainya Anda kehilangan kata sandi?
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ya tolong, Lebih baik aman daripada menyesal
|
||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Tidak terima kasih, Saya tidak akan kehilangan kata sandi saya
|
||||
### Information
|
||||
addvault.new.readme.storageLocation.fileName=PENTING.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ BERKAS BRANKAS ⚠️
|
||||
addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan brankas kamu.
|
||||
addvault.new.readme.storageLocation.3=JANGAN
|
||||
addvault.new.readme.storageLocation.4=• mengubah file dalam direktori ini, atau
|
||||
addvault.new.readme.storageLocation.5=• menempelkan file untuk dienkripsi ke dalam direktori ini.
|
||||
addvault.new.readme.storageLocation.6=Jika kamu ingin mengenkripsi berkas dan melihat isi brankas, lakukan hal berikut:
|
||||
addvault.new.readme.storageLocation.7=1. Tambahkan brankas ini ke Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. Buka gembok brankas di Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Buka akses lokasi dengan mengklik tombol "Perlihatkan".
|
||||
addvault.new.readme.storageLocation.10=Jika kamu butuh bantuan, kunjungi dokumentasi: %s
|
||||
addvault.new.readme.accessLocation.fileName=SELAMAT_DATANG.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ ISI TERENKRIPSI 🔐️
|
||||
addvault.new.readme.accessLocation.2=Ini adalah lokasi akses brankas kamu.
|
||||
addvault.new.readme.accessLocation.3=File yang ditambahkan ke volume ini akan dienkripsi oleh Cryptomator. Anda dapat mempergunakan isi vault seperti dalam folder lain. Saat ini Anda sedang mengakses tampilan versi dekripsi, file Anda selalu terenkripsi di dalam cakram keras Anda.
|
||||
addvault.new.readme.storageLocation.1=⚠️ FILE VAULT ⚠️
|
||||
addvault.new.readme.storageLocation.2=Ini adalah lokasi penyimpanan vault Anda.
|
||||
addvault.new.readme.storageLocation.3=DILARANG
|
||||
addvault.new.readme.storageLocation.4=• mengubah file apapun di direktori ini, atau
|
||||
addvault.new.readme.storageLocation.5=• menyalin file untuk dienkripsi ke dalam direktori ini.
|
||||
addvault.new.readme.storageLocation.6=Jika Anda ingin mengenkripsi file dan melihat isi konten dari vault, lakukan hal berikut:
|
||||
addvault.new.readme.storageLocation.7=1. Tambahkan vault ini ke Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. Buka vault melalui Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Buka lokasi akses dengan mengklik tombol "Perlihatkan".
|
||||
addvault.new.readme.storageLocation.10=Jika Anda butuh bantuan, kunjungi dokumentasi: %s
|
||||
addvault.new.readme.accessLocation.fileName=SELAMAT DATANG.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ VOLUME TERENKRIPSI 🔐️
|
||||
addvault.new.readme.accessLocation.2=Ini adalah lokasi akses dari vault Anda.
|
||||
addvault.new.readme.accessLocation.3=Seluruh file yang ditambahkan ke volume ini akan dienkripsi oleh Cryptomator. Anda dapat mempergunakan isi vault seperti dalam folder/drive lain pada umumnya. Tampilan yang saat ini Anda lihat adalah tampilan dalam bentuk terdekripsi, yang sebenarnya terjadi adalah file-file Anda selalu dalam kondisi terenkripsi di dalam hard drive Anda.
|
||||
addvault.new.readme.accessLocation.4=Anda dapat menghapus file ini.
|
||||
## Existing
|
||||
addvaultwizard.existing.instruction=Pilih file "vault.cryptomator" Anda dari vault yang ada. Jika hanya ada file bernama "masterkey.cryptomator", pilih file tersebut.
|
||||
@@ -84,46 +84,49 @@ addvaultwizard.success.unlockNow=Buka Kunci Sekarang
|
||||
|
||||
# Remove Vault
|
||||
removeVault.title=Hapus Vault
|
||||
removeVault.information=Cryptomator hanya akan melupakan vault ini. Anda dapat menambahkan vault ini lagi nantinya. File yang telah dienkripsi tidak akan dihapus dari cakram keras Anda.
|
||||
removeVault.information=Tindakan ini hanya akan membuat Cryptomator melupakan vault ini. Anda dapat menambahkan vault ini lagi nanti. File yang telah dienkripsi tidak akan dihapus dari hard drive Anda.
|
||||
removeVault.confirmBtn=Hapus Vault
|
||||
|
||||
# Change Password
|
||||
changepassword.title=Ubah Kata Sandi
|
||||
changepassword.enterOldPassword=Masukkan kata sandi untuk "%s" saat ini
|
||||
changepassword.enterOldPassword=Masukkan kata sandi "%s" saat ini
|
||||
changepassword.finalConfirmation=Saya mengerti bahwa saya tidak akan dapat mengakses data saya apabila saya lupa kata sandi saya
|
||||
|
||||
# Forget Password
|
||||
forgetPassword.title=Lupa Kata Sandi
|
||||
forgetPassword.information=Kata sandi vault yang tersimpan akan dihapus dari keychain.
|
||||
forgetPassword.information=Tindakan ini akan menghapus kata sandi vault berikut yang tersimpan dari sistem keychain Anda.
|
||||
forgetPassword.confirmBtn=Lupa Kata Sandi
|
||||
|
||||
# Unlock
|
||||
unlock.title=Membuka "%s"
|
||||
unlock.passwordPrompt=Masukkan kata sandi untuk "%s":
|
||||
unlock.savePassword=Simpan Kata Sandi
|
||||
unlock.unlockBtn=Buka Gembok
|
||||
##
|
||||
unlock.title=Buka Kunci "%s"
|
||||
unlock.passwordPrompt=Masukkan kata sandi "%s":
|
||||
unlock.savePassword=Ingat Kata Sandi
|
||||
unlock.unlockBtn=Buka Kunci
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Pilih Masterkey of "%s"
|
||||
unlock.chooseMasterkey.prompt=Tidak dapat menemukan file masterkey untuk vault ini pada lokasi yang dicari. Mohon pilih file kunci secara manual.
|
||||
unlock.chooseMasterkey.filePickerTitle=Pilih File Kunci Induk
|
||||
unlock.chooseMasterkey.chooseBtn=Pilih…
|
||||
unlock.chooseMasterkey.filePickerTitle=Pilih File Masterkey
|
||||
## Success
|
||||
unlock.success.message="%s" telat terbuka! Vault Anda sekarang dapat diakses melalui drive virtual.
|
||||
unlock.success.message="%s" berhasil dibuka! Vault Anda sekarang dapat diakses melalui drive virtual.
|
||||
unlock.success.rememberChoice=Ingat pilihan saya, jangan perlihatkan lagi
|
||||
unlock.success.revealBtn=Buka Drive
|
||||
unlock.success.revealBtn=Tampilkan Drive
|
||||
## Failure
|
||||
unlock.error.heading=Tidak dapat membuka vault
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Poin mount "%s" bukan direktori, dan tidak kosong atau tidak ada.
|
||||
unlock.error.invalidMountPoint.existing=Poin mount "%s" sudah ada atau folder induknya tidak ada.
|
||||
unlock.error.invalidMountPoint.notExisting=Mount point "%s" bukanlah sebuah direktori, tidak sedang kosong, atau bahkan tidak ada sama sekali.
|
||||
unlock.error.invalidMountPoint.existing=Mount point "%s" sudah ada atau folder induknya tidak ditemukan.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Drive Letter "%s" sedang digunakan.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.heading=Gagal mengunci
|
||||
lock.forced.message=Penguncian "%s" terblokir oleh operasi yang sedang berjalan atau file yang masih terbuka. Anda dapat mengunci paksa vault ini, namun ada kemungkinan mengganggu I/O akan menghilangkan data yang belum disimpan.
|
||||
lock.forced.message=Penguncian "%s" terblokir oleh operasi yang sedang berjalan atau file yang masih terbuka. Anda dapat mengunci paksa vault ini, namun ada kemungkinan mengganggu I/O yang berakibat kehilangan data yang belum disimpan.
|
||||
lock.forced.retryBtn=Coba lagi
|
||||
lock.forced.forceBtn=Kunci Paksa
|
||||
lock.forced.forceBtn=Paksa Kunci
|
||||
## Failure
|
||||
lock.fail.heading=Gagal mengunci vault.
|
||||
lock.fail.message=Vault "%s" tidak dapat dikunci. Pastikan bahwa file yang belum tersimpan telah disimpan di lokasi lain, dan operasi Baca/Tulis yang penting telah selesai. Untuk menutup vault ini, matikan proses Cryptomator.
|
||||
lock.fail.message=Vault "%s" tidak dapat dikunci. Pastikan bahwa file yang belum tersimpan telah disimpan di lokasi lain, dan operasi Baca/Tulis telah selesai dilakukan. Untuk menutup vault ini, matikan proses Cryptomator.
|
||||
|
||||
# Migration
|
||||
migration.title=Tingkatkan Vault
|
||||
@@ -146,17 +149,17 @@ migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Sistem file tid
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Sistem file tidak mengizinkan untuk ditulis.
|
||||
## Impossible
|
||||
migration.impossible.heading=Tidak dapat memindahkan vault
|
||||
migration.impossible.reason=Vault tidak dapat dipindahkan secara otomatis karena lokasi penyimpanan atau poin akses tidak sesuai.
|
||||
migration.impossible.moreInfo=Vault dapat dibuka dengan program versi lebih lama. Apabila Anda ingin memindahkan vault secara manual, silahkan buka
|
||||
migration.impossible.reason=Vault tidak dapat dipindahkan secara otomatis karena lokasi penyimpanan atau access point tidak sesuai.
|
||||
migration.impossible.moreInfo=Vault masih bisa dibuka dengan aplikasi versi terdahulu. Untuk panduan memindahkan vault secara manual, silahkan buka
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
health.title=Pemeriksaan Kesehatan "%s"
|
||||
health.intro.header=Pemeriksaan Kesehatan
|
||||
health.intro.text=Pemeriksaan Kesehatan adalah kumpulan pemeriksaan untuk mendeteksi dan mungkin memperbaiki masalah dalam struktur internal brankas Anda. Harap diingat:
|
||||
health.intro.remarkSync=Pastikan semua perangkat disinkronkan sepenuhnya, ini menyelesaikan sebagian besar masalah.
|
||||
health.intro.text=Pemeriksaan Kesehatan adalah kumpulan pemeriksaan untuk mendeteksi dan memperbaiki masalah di dalam struktur internal vault Anda. Perlu diperhatikan:
|
||||
health.intro.remarkSync=Pastikan semua perangkat tersinkronisasi sepenuhnya, sebagian besar masalah teratasi dengan cara ini.
|
||||
health.intro.remarkFix=Tidak semua masalah bisa diperbaiki.
|
||||
health.intro.remarkBackup=Jika data rusak, hanya cadangan yang dapat membantu.
|
||||
health.intro.remarkBackup=Jika data rusak, hanya cadangan yang dapat memulihkannya.
|
||||
health.intro.affirmation=Saya telah membaca dan memahami informasi di atas
|
||||
## Start Failure
|
||||
health.fail.header=Kesalahan saat memuat Konfigurasi Vault
|
||||
@@ -167,14 +170,14 @@ health.fail.moreInfo=Info Selengkapnya
|
||||
health.checkList.description=Centang daftar di bagian kiri atau gunakan tombol di bawah.
|
||||
health.checkList.selectAllButton=Centang Semua
|
||||
health.checkList.deselectAllButton=Jangan Centang Semua
|
||||
health.check.runBatchBtn=Cek Jalankan yang dipilih
|
||||
health.check.runBatchBtn=Jalankan Centang yang Terpilih
|
||||
## Detail view
|
||||
health.check.detail.noSelectedCheck=Untuk hasil, pilih pemeriksaan kesehatan yang sudah selesai di sebelah kiri.
|
||||
health.check.detail.checkScheduled=Pemeriksaan dijadwalkan.
|
||||
health.check.detail.noSelectedCheck=Pilih pemeriksaan kesehatan yang sudah selesai di sebelah kiri untuk melihat hasilnya.
|
||||
health.check.detail.checkScheduled=Pemeriksaan telah terjadwal.
|
||||
health.check.detail.checkRunning=Saat ini pemeriksaan sedang berjalan…
|
||||
health.check.detail.checkSkipped=Pemeriksaan tidak dipilih untuk dijalankan.
|
||||
health.check.detail.checkFinished=Pemeriksaan berhasil diselesaikan.
|
||||
health.check.detail.checkFinishedAndFound=Pemeriksaan selesai berjalan. Harap tinjau hasilnya.
|
||||
health.check.detail.checkSkipped=Pemeriksaan tidak dajalankan karena tidak dipilih.
|
||||
health.check.detail.checkFinished=Pemeriksaan berhasil dilakukan.
|
||||
health.check.detail.checkFinishedAndFound=Pemeriksaan selesai. Silahkan tinjau hasilnya.
|
||||
health.check.detail.checkFailed=Pemeriksaan terhenti karena terjadi kesalahan.
|
||||
health.check.detail.checkCancelled=Pemeriksaan dibatalkan.
|
||||
health.check.exportBtn=Ekspor Laporan
|
||||
@@ -187,17 +190,17 @@ health.fix.failTip=Perbaikan gagal, lihat log untuk detailnya
|
||||
preferences.title=Preferensi
|
||||
## General
|
||||
preferences.general=Umum
|
||||
preferences.general.theme=Lihat dan rasakan
|
||||
preferences.general.theme=Tampilan & Suasana
|
||||
preferences.general.theme.automatic=Otomatis
|
||||
preferences.general.theme.light=Terang
|
||||
preferences.general.theme.dark=Gelap
|
||||
preferences.general.unlockThemes=Buka mode gelap
|
||||
preferences.general.showMinimizeButton=Perkecil tampilan tombol
|
||||
preferences.general.showTrayIcon=\nTampilkan ikon baki (memerlukan mulai ulang)
|
||||
preferences.general.startHidden=Sembunyikan halaman saat memulai Cryptomator
|
||||
preferences.general.showMinimizeButton=Tampilkan tombol perkecil
|
||||
preferences.general.showTrayIcon=Tampilkan tray icon (diperlukan muat ulang)
|
||||
preferences.general.startHidden=Sembunyikan jendela saat memulai Cryptomator
|
||||
preferences.general.debugLogging=Aktifkan pencatatan debug
|
||||
preferences.general.debugDirectory=Buka file log
|
||||
preferences.general.autoStart=Luncurkan Cryptomator pada awal sistem
|
||||
preferences.general.debugDirectory=Perlihatkan file log
|
||||
preferences.general.autoStart=Jalankan Cryptomator saat sistem dimulai
|
||||
preferences.general.keychainBackend=Simpan kata sandi dengan
|
||||
preferences.general.interfaceOrientation=Orientasi Antarmuka
|
||||
preferences.general.interfaceOrientation.ltr=Kiri ke kanan
|
||||
@@ -205,20 +208,20 @@ preferences.general.interfaceOrientation.rtl=Kanan ke kiri
|
||||
## Volume
|
||||
preferences.volume=Drive Virtual
|
||||
preferences.volume.type=Jenis Volume
|
||||
preferences.volume.webdav.port=Kabel WebDAV
|
||||
preferences.volume.webdav.port=Port WebDAV
|
||||
preferences.volume.webdav.scheme=Skema WebDAV
|
||||
## Updates
|
||||
preferences.updates=Pembaharuan
|
||||
preferences.updates.currentVersion=Versi Sekarang: %s
|
||||
preferences.updates.autoUpdateCheck=Periksa untuk update otomatis
|
||||
preferences.updates.currentVersion=Versi Saat Ini: %s
|
||||
preferences.updates.autoUpdateCheck=Otomatis periksa update
|
||||
preferences.updates.checkNowBtn=Periksa Sekarang
|
||||
preferences.updates.updateAvailable=Pembaharuan ke versi %s tersedia.
|
||||
## Contribution
|
||||
preferences.contribute=Dukung Kami
|
||||
preferences.contribute.registeredFor=Sertifikat pendukung terdaftar untuk %s
|
||||
preferences.contribute.noCertificate=Dukung Cryptomator dan terima sertifikat pendukung. Ini seperti kunci lisensi tetapi untuk orang-orang hebat yang menggunakan perangkat lunak gratis. ;-)
|
||||
preferences.contribute.registeredFor=Sertifikat supporter terdaftar atas nama %s
|
||||
preferences.contribute.noCertificate=Dukung Cryptomator dan terima sebuah sertifikat supporter. Sertifikat ini layaknya kunci lisensi, tetapi hanya untuk orang-orang hebat yang menggunakan aplikasi versi gratis. ;-)
|
||||
preferences.contribute.getCertificate=Belum punya? Pelajari bagaimana Anda bisa mendapatkannya.
|
||||
preferences.contribute.promptText=Tempel kode sertifikat pendukung di sini
|
||||
preferences.contribute.promptText=Tempel kode sertifikat supporter di sini
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
@@ -228,22 +231,22 @@ preferences.about=Tentang
|
||||
stats.title=Statistik untuk %s
|
||||
stats.cacheHitRate=Tingkat Hit Cache
|
||||
## Read
|
||||
stats.read.throughput.idle=Read: idle
|
||||
stats.read.throughput.kibs=Read: %.2f kiB/s
|
||||
stats.read.throughput.mibs=Read: %.2f MiB/s
|
||||
stats.read.total.data.none=Data read: -
|
||||
stats.read.total.data.kib=Data read: %.1f kiB
|
||||
stats.read.total.data.mib=Data read: %.1f MiB
|
||||
stats.read.total.data.gib=Data read: %.1f GiB
|
||||
stats.decr.total.data.none=Data decrypted: -
|
||||
stats.decr.total.data.kib=Data decrypted: %.1f kiB
|
||||
stats.decr.total.data.mib=Data decrypted: %.1f MiB
|
||||
stats.decr.total.data.gib=Data decrypted: %.1f GiB
|
||||
stats.read.accessCount=Total reads: %d
|
||||
stats.read.throughput.idle=Baca: diam
|
||||
stats.read.throughput.kibs=Baca: %.2f kiB/detik
|
||||
stats.read.throughput.mibs=Baca: %.2f MiB/detik
|
||||
stats.read.total.data.none=Data dibaca: -
|
||||
stats.read.total.data.kib=Data dibaca: %.1f kiB
|
||||
stats.read.total.data.mib=Data dibaca: %.1f MiB
|
||||
stats.read.total.data.gib=Data dibaca: %.1f GiB
|
||||
stats.decr.total.data.none=Data terdekripsi: -
|
||||
stats.decr.total.data.kib=Data terdekripsi: %.1f kiB
|
||||
stats.decr.total.data.mib=Data terdeksripsi: %.1f MiB
|
||||
stats.decr.total.data.gib=Data terdeksripsi: %.1f GiB
|
||||
stats.read.accessCount=Total dibaca: %d
|
||||
## Write
|
||||
stats.write.throughput.idle=Write: idle
|
||||
stats.write.throughput.kibs=Write: %.2f kiB/s
|
||||
stats.write.throughput.mibs=Write: %.2f MiB/s
|
||||
stats.write.throughput.idle=Tulis: diam
|
||||
stats.write.throughput.kibs=Tulis: %.2f kiB/detik
|
||||
stats.write.throughput.mibs=Tulis: %.2f MiB/detik
|
||||
stats.write.total.data.none=Data tertulis: -
|
||||
stats.write.total.data.kib=Data tertulis: %.1f kiB
|
||||
stats.write.total.data.mib=Data tertulis: %.1f MiB
|
||||
@@ -252,22 +255,22 @@ stats.encr.total.data.none=Data terenkripsi: -
|
||||
stats.encr.total.data.kib=Data terenkripsi: %.1f kiB
|
||||
stats.encr.total.data.mib=Data terenkripsi: %.1f MiB
|
||||
stats.encr.total.data.gib=Data terenkripsi: %.1f GiB
|
||||
stats.write.accessCount=Total menulis: %d
|
||||
stats.write.accessCount=Total ditulis: %d
|
||||
|
||||
# Main Window
|
||||
main.closeBtn.tooltip=Tutup
|
||||
main.minimizeBtn.tooltip=Minimalkan
|
||||
main.minimizeBtn.tooltip=Perkecil
|
||||
main.preferencesBtn.tooltip=Preferensi
|
||||
main.debugModeEnabled.tooltip=Mode Debug diaktifkan
|
||||
main.donationKeyMissing.tooltip=Pertimbangjan untuk donasi
|
||||
main.donationKeyMissing.tooltip=Tolong pertimbangkan untuk melakukan donasi
|
||||
## Drag 'n' Drop
|
||||
main.dropZone.dropVault=Tambah vault ini
|
||||
main.dropZone.unknownDragboardContent=Jika Anda ingin menambahkan vault, seret ke jendela ini
|
||||
## Vault List
|
||||
main.vaultlist.emptyList.onboardingInstruction=Klik untuk tambah vault
|
||||
main.vaultlist.emptyList.onboardingInstruction=Klik di sini untuk menambahkan vault
|
||||
main.vaultlist.contextMenu.remove=Hapus…
|
||||
main.vaultlist.contextMenu.lock=Gembok
|
||||
main.vaultlist.contextMenu.unlock=Membuka…
|
||||
main.vaultlist.contextMenu.lock=Kunci
|
||||
main.vaultlist.contextMenu.unlock=Buka Kunci…
|
||||
main.vaultlist.contextMenu.unlockNow=Buka Kunci Sekarang
|
||||
main.vaultlist.contextMenu.vaultoptions=Tampilkan Opsi Vault
|
||||
main.vaultlist.contextMenu.reveal=Buka Drive
|
||||
@@ -277,7 +280,7 @@ main.vaultlist.addVaultBtn=Tambah Brankas
|
||||
main.vaultDetail.welcomeOnboarding=Terima kasih telah memilih Cryptomator untuk melindungi file Anda. Jika Anda memerlukan bantuan, lihat panduan awal kami:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=TERKUNCI
|
||||
main.vaultDetail.unlockBtn=Membuka…
|
||||
main.vaultDetail.unlockBtn=Buka Kunci…
|
||||
main.vaultDetail.unlockNowBtn=Buka Kunci Sekarang
|
||||
main.vaultDetail.optionsBtn=Opsi Vault
|
||||
main.vaultDetail.passwordSavedInKeychain=Kata Sandi tersimpan
|
||||
@@ -332,10 +335,10 @@ vaultOptions.general.startHealthCheckBtn=Mulai Pemeriksaan Kesehatan
|
||||
vaultOptions.mount=Pemasangan
|
||||
vaultOptions.mount.readonly=Read-Only
|
||||
vaultOptions.mount.customMountFlags=Custom Mount Flags
|
||||
vaultOptions.mount.winDriveLetterOccupied=terisi
|
||||
vaultOptions.mount.winDriveLetterOccupied=terpakai
|
||||
vaultOptions.mount.mountPoint=Titik Pasang
|
||||
vaultOptions.mount.mountPoint.auto=Secara otomatis memilih lokasi yang sesuai
|
||||
vaultOptions.mount.mountPoint.driveLetter=Gunakan huruf drive yang ditetapkan
|
||||
vaultOptions.mount.mountPoint.driveLetter=Gunakan drive letter yang sudah ditetapkan
|
||||
vaultOptions.mount.mountPoint.custom=Path khusus
|
||||
vaultOptions.mount.mountPoint.directoryPickerButton=Pilih…
|
||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Pilih direktori kosong
|
||||
@@ -370,5 +373,5 @@ passwordStrength.messageLabel.3=Kuat
|
||||
passwordStrength.messageLabel.4=Sangat kuat
|
||||
|
||||
# Quit
|
||||
quit.prompt=Keluar aplikasi? Terdapat brankas yg belum digembok.
|
||||
quit.lockAndQuit=Gembok dan Keluar
|
||||
quit.prompt=Keluar aplikasi? Masih ada vault yg belum dikunci.
|
||||
quit.lockAndQuit=Kunci dan Keluar
|
||||
|
||||
@@ -102,8 +102,10 @@ unlock.title=Sblocca "%s"
|
||||
unlock.passwordPrompt=Inserisci la password per "%s":
|
||||
unlock.savePassword=Ricorda la Password
|
||||
unlock.unlockBtn=Sblocca
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Seleziona Masterkey di "%s"
|
||||
unlock.chooseMasterkey.prompt=Impossibile trovare il file Masterkey per questa cassaforte alla sua posizione prevista. Sei pregato di sceglierlo manualmente.
|
||||
unlock.chooseMasterkey.chooseBtn=Scegli…
|
||||
unlock.chooseMasterkey.filePickerTitle=Seleziona il File Masterkey
|
||||
## Success
|
||||
unlock.success.message="%s" sbloccato correttamente! La tua cassaforte è ora accessibile tramite la sua unità virtuale.
|
||||
@@ -114,11 +116,14 @@ unlock.error.heading=Impossibile sbloccare la cassaforte
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Il punto di montaggio "%s" non è una cartella, non è vuoto o non esiste.
|
||||
unlock.error.invalidMountPoint.existing=Il punto di montaggio "%s" esiste già o la cartella madre è mancante.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=La lettera di unità "%s" è già in uso.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.heading=Blocco non riuscito
|
||||
lock.forced.message=Il bloccaggio di "%s" è stato impedito dalle operazioni in sospeso o dai file aperti. Puoi forzare il blocco di questa cassaforte, tuttavia, interrompere I/O potrebbe risultare nella perdita dei dati non salvati.
|
||||
lock.forced.retryBtn=Riprova
|
||||
lock.forced.forceBtn=Forza Blocco
|
||||
## Failure
|
||||
lock.fail.heading=Blocco della cassaforte fallito.
|
||||
lock.fail.message=Impossibile bloccare la cassaforte "%s". Assicurati che il lavoro non salvato sia salvato altrove e che le importanti operazioni di Lettura/Scrittura siano terminate. Per chiudere la cassaforte, termina il processo di Cryptomator.
|
||||
|
||||
@@ -35,7 +35,7 @@ traymenu.vault.reveal=表示
|
||||
addvaultwizard.title=金庫を追加
|
||||
## Welcome
|
||||
addvaultwizard.welcome.newButton=新しい金庫を作成
|
||||
addvaultwizard.welcome.existingButton=すでにある金庫を開く
|
||||
addvaultwizard.welcome.existingButton=既存の金庫を開く
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=金庫の名前を入力してください
|
||||
@@ -51,7 +51,7 @@ addvaultwizard.new.fileAlreadyExists=金庫名と同じ名前のファイルま
|
||||
addvaultwizard.new.locationDoesNotExist=指定されたパスのディレクトリが存在しないかアクセスできません
|
||||
addvaultwizard.new.locationIsNotWritable=指定されたパスに書き込み権限がありません
|
||||
addvaultwizard.new.locationIsOk=金庫に適した場所
|
||||
addvaultwizard.new.invalidName=無効な金庫の名前です。一般的なディレクトリの名前を検討してください。
|
||||
addvaultwizard.new.invalidName=使用できない金庫の名前です。一般的なディレクトリの名前にしてください。
|
||||
### Password
|
||||
addvaultwizard.new.createVaultBtn=金庫を作成
|
||||
addvaultwizard.new.generateRecoveryKeyChoice=データにアクセスするにはパスワードが必須です。パスワードを紛失したときのためにリカバリーキーは必要ですか?
|
||||
@@ -102,18 +102,21 @@ unlock.title="%s" を解錠
|
||||
unlock.passwordPrompt="%s" のパスワードを入力してください:
|
||||
unlock.savePassword=パスワードを記憶させる
|
||||
unlock.unlockBtn=解錠
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title="%s" の Masterkey ファイルを選択
|
||||
unlock.chooseMasterkey.prompt=必要な場所にこの金庫の masterkey ファイルがありませんでした。ファイルを手動で選択してください。
|
||||
unlock.chooseMasterkey.chooseBtn=選択...
|
||||
unlock.chooseMasterkey.filePickerTitle=Masterkey ファイルを選択
|
||||
## Success
|
||||
unlock.success.message="%s" の解錠に成功しました! 仮想ドライブから金庫にアクセス可能です。
|
||||
unlock.success.rememberChoice=選択を記憶させて、再度表示しない
|
||||
unlock.success.revealBtn=ドライブを表示
|
||||
unlock.success.revealBtn=金庫内を表示
|
||||
## Failure
|
||||
unlock.error.heading=金庫の解錠に失敗
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=マウントポイントが空のディレクトリか存在していません: %s
|
||||
unlock.error.invalidMountPoint.existing=マウント ポイント "%s" が既に存在するか、親フォルダーがありません。
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=ドライブレター「%s」は既に使用されています。
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
@@ -187,21 +190,21 @@ health.fix.failTip=修正に失敗しました。詳細はログを参照して
|
||||
preferences.title=設定
|
||||
## General
|
||||
preferences.general=基本設定
|
||||
preferences.general.theme=外見 & 操作性
|
||||
preferences.general.theme=外見と操作性
|
||||
preferences.general.theme.automatic=自動
|
||||
preferences.general.theme.light=ライト
|
||||
preferences.general.theme.dark=ダーク
|
||||
preferences.general.unlockThemes=ダークモードの解錠
|
||||
preferences.general.unlockThemes=ダークモードを解除
|
||||
preferences.general.showMinimizeButton=最小化ボタンを表示
|
||||
preferences.general.showTrayIcon=トレイアイコンを表示 (再起動が必要)
|
||||
preferences.general.startHidden=Cryptomator を開始したときウィンドウを隠す
|
||||
preferences.general.debugLogging=ログを有効にする
|
||||
preferences.general.debugDirectory=ログ ファイルを表示
|
||||
preferences.general.debugDirectory=ログファイルを表示
|
||||
preferences.general.autoStart=システム開始時にCryptomatorを起動する
|
||||
preferences.general.keychainBackend=次を利用してパスワードを保存する
|
||||
preferences.general.interfaceOrientation=インターフェイスの向き
|
||||
preferences.general.interfaceOrientation.ltr=左から右
|
||||
preferences.general.interfaceOrientation.rtl=右から左
|
||||
preferences.general.interfaceOrientation.ltr=左横書き
|
||||
preferences.general.interfaceOrientation.rtl=右横書き
|
||||
## Volume
|
||||
preferences.volume=仮想ドライブ
|
||||
preferences.volume.type=マウント方法
|
||||
@@ -214,11 +217,11 @@ preferences.updates.autoUpdateCheck=自動的に更新を確認する
|
||||
preferences.updates.checkNowBtn=今すぐ確認
|
||||
preferences.updates.updateAvailable=利用可能なバージョン %s に更新します。
|
||||
## Contribution
|
||||
preferences.contribute=サポートする
|
||||
preferences.contribute=支援する
|
||||
preferences.contribute.registeredFor=サポーター証明書が %s に登録されました
|
||||
preferences.contribute.noCertificate=Cryptomator を支援し、サポーター証明書を受け取りましょう。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-)
|
||||
preferences.contribute.noCertificate=Cryptomator を支援し、サポーター証明書を受け取りましょう。ライセンスキーに似ていますが自由ソフトウェアを使う寄付者向けのキーです。 ;-)
|
||||
preferences.contribute.getCertificate=まだ証明書を手に入れていませんか? 詳細はこちらから確認できます。
|
||||
preferences.contribute.promptText=サポーター証明書をここに張り付けてください
|
||||
preferences.contribute.promptText=サポーター証明書をここに貼り付け
|
||||
#<-- Add entries for donations and code/translation/documentation contribution -->
|
||||
|
||||
## About
|
||||
@@ -274,7 +277,7 @@ main.vaultlist.contextMenu.reveal=ドライブを表示
|
||||
main.vaultlist.addVaultBtn=金庫を追加
|
||||
## Vault Detail
|
||||
### Welcome
|
||||
main.vaultDetail.welcomeOnboarding=ファイル保護するために Cryptomator を選んでいただきありがとうございます。ヘルプが必要であれば、スタートガイドをご覧ください:
|
||||
main.vaultDetail.welcomeOnboarding=ファイルを保護するために Cryptomator を選んでいただきありがとうございます。ヘルプが必要であれば、スタートガイドをご覧ください:
|
||||
### Locked
|
||||
main.vaultDetail.lockedStatus=施錠済み
|
||||
main.vaultDetail.unlockBtn=解錠...
|
||||
@@ -293,7 +296,7 @@ main.vaultDetail.throughput.kbps=%.1f kiB/s
|
||||
main.vaultDetail.throughput.mbps=%.1f MiB/s
|
||||
main.vaultDetail.stats=金庫の統計情報
|
||||
### Missing
|
||||
main.vaultDetail.missing.info=Cryptomator はこのパスの金庫を見つけることができませんでした。
|
||||
main.vaultDetail.missing.info=Cryptomator はこの場所に金庫を見つけることができませんでした。
|
||||
main.vaultDetail.missing.recheck=再確認
|
||||
main.vaultDetail.missing.remove=金庫のリストから削除...
|
||||
main.vaultDetail.missing.changeLocation=金庫の場所を変更...
|
||||
@@ -343,7 +346,7 @@ vaultOptions.mount.mountPoint.directoryPickerTitle=空のディレクトリを
|
||||
vaultOptions.masterkey=パスワード
|
||||
vaultOptions.masterkey.changePasswordBtn=パスワードの変更
|
||||
vaultOptions.masterkey.forgetSavedPasswordBtn=保存したパスワードを削除する
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=回復キーはパスワードを忘れてしまった場合でも、金庫へのアクセスを回復する唯一の手段です。
|
||||
vaultOptions.masterkey.recoveryKeyExplanation=回復キーはパスワードを忘れてしまった場合に、金庫へのアクセスを回復する唯一の手段です。
|
||||
vaultOptions.masterkey.showRecoveryKeyBtn=回復キーを表示
|
||||
vaultOptions.masterkey.recoverPasswordBtn=パスワードの回復
|
||||
|
||||
@@ -351,8 +354,8 @@ vaultOptions.masterkey.recoverPasswordBtn=パスワードの回復
|
||||
# Recovery Key
|
||||
recoveryKey.title=回復キー
|
||||
recoveryKey.enterPassword.prompt="%s" の回復キーを表示するためのパスワードを入力してください:
|
||||
recoveryKey.display.message="%s" へのアクセス権限を復元するリカバリーキー:
|
||||
recoveryKey.display.StorageHints=とても安全な場所に保存してください、例えば:\n • パスワード管理ソフトに保存\n • USB フラッシュドライブに保存\n • 紙に印刷
|
||||
recoveryKey.display.message="%s" へのアクセス権限を復元する回復キー:
|
||||
recoveryKey.display.StorageHints=十分に安全な場所に保存してください。例えば:\n • パスワード管理ソフトに保存\n • USB フラッシュドライブに保存\n • 紙に印刷
|
||||
recoveryKey.recover.prompt="%s" の回復キーを入力してください:
|
||||
recoveryKey.recover.validKey=有効な回復キー
|
||||
recoveryKey.printout.heading=Cryptomator 回復キー\n"%s"\n
|
||||
|
||||
@@ -98,8 +98,9 @@ unlock.title="%s" 잠금 해제
|
||||
unlock.passwordPrompt="%s"의 비밀번호를 입력하십시요.
|
||||
unlock.savePassword=비밀번호 기억
|
||||
unlock.unlockBtn=잠금해제
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.prompt=추정되는 위치에서 이 Vault의 마스터 키를 찾지 못했습니다. 마스터 키 위치를 수동으로 선택하여 주십시요.
|
||||
unlock.chooseMasterkey.chooseBtn=선택
|
||||
unlock.chooseMasterkey.filePickerTitle=마스터키 파일 선택
|
||||
## Success
|
||||
unlock.success.message="%s"이(가) 성공적으로 잠금해제되었습니다. 이제 이 Vault를 가상드라이브로 접근할 수 있습니다.
|
||||
|
||||
@@ -88,7 +88,8 @@ forgetPassword.confirmBtn=Aizmirst paroli
|
||||
# Unlock
|
||||
unlock.passwordPrompt=Ievadiet "%s" paroli:
|
||||
unlock.unlockBtn=Atslēgt
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=Izvēlies...
|
||||
unlock.chooseMasterkey.filePickerTitle=Atlasīt galveno atslēgas datni
|
||||
## Success
|
||||
unlock.success.revealBtn=Atklāt disku
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
# Forget Password
|
||||
|
||||
# Unlock
|
||||
##
|
||||
## Select
|
||||
## Success
|
||||
## Failure
|
||||
### Invalid Mount Point
|
||||
|
||||
@@ -13,6 +13,11 @@ generic.button.done=Ferdig
|
||||
generic.button.next=Neste
|
||||
generic.button.print=Skriv ut
|
||||
## Error
|
||||
generic.error.title=Feilkode %s
|
||||
generic.error.instruction=Oops! Cryptomator forventet ikke at dette skulle skje. Du kan slå opp mulige løsninger for denne feilen. Hvis feilen ikke er rapportert så kan du gjerne gjøre det.
|
||||
generic.error.hyperlink.lookup=Slå opp denne feilen
|
||||
generic.error.hyperlink.report=Rapporter denne feilen
|
||||
generic.error.technicalDetails=Detaljer:
|
||||
|
||||
# Defaults
|
||||
defaults.vault.vaultName=Hvelv
|
||||
@@ -33,7 +38,7 @@ addvaultwizard.welcome.newButton=Lag et nytt hvelv
|
||||
addvaultwizard.welcome.existingButton=Åpne et eksisterende hvelv
|
||||
## New
|
||||
### Name
|
||||
addvaultwizard.new.nameInstruction=Velg et navn på hvelvet
|
||||
addvaultwizard.new.nameInstruction=Navngi hvelvet
|
||||
addvaultwizard.new.namePrompt=Navn på hvelvet
|
||||
### Location
|
||||
addvaultwizard.new.locationInstruction=Hvor skal Cryptomator lagre de krypterte filene dine?
|
||||
@@ -43,6 +48,8 @@ addvaultwizard.new.directoryPickerLabel=Tilpasset lagringssted
|
||||
addvaultwizard.new.directoryPickerButton=Velg…
|
||||
addvaultwizard.new.directoryPickerTitle=Velg mappe
|
||||
addvaultwizard.new.fileAlreadyExists=En fil eller mappe med det hvelvnavnet finnes allerede
|
||||
addvaultwizard.new.locationDoesNotExist=En mappe i den angitte stien finnes ikke eller kan ikke nås
|
||||
addvaultwizard.new.locationIsNotWritable=Ingen skrivetilgang på den angitte stien
|
||||
addvaultwizard.new.locationIsOk=Egnet sted for hvelvet ditt
|
||||
addvaultwizard.new.invalidName=Ugyldig navn på hvelvet. Vennligst vurder et vanlig mappenavn.
|
||||
### Password
|
||||
@@ -55,13 +62,13 @@ addvault.new.readme.storageLocation.fileName=VIKTIG.rtf
|
||||
addvault.new.readme.storageLocation.1=⚠️ HVELVFILER ⚠️
|
||||
addvault.new.readme.storageLocation.2=Dette er hvelvets lagringssted.
|
||||
addvault.new.readme.storageLocation.3=IKKE
|
||||
addvault.new.readme.storageLocation.4=• endre hvilke filer som helst i denne mappen eller
|
||||
addvault.new.readme.storageLocation.5=• i denne mappen limer du inn alle filer du ønsker skal krypteres.
|
||||
addvault.new.readme.storageLocation.4=• endre noen filer i denne mappen eller
|
||||
addvault.new.readme.storageLocation.5=• lim inn filer du ønsker skal krypteres.
|
||||
addvault.new.readme.storageLocation.6=Hvis du vil kryptere filer og se innholdet i hvelvet, gjør du følgende:
|
||||
addvault.new.readme.storageLocation.7=1. Legg til dette hvelvet i Cryptomator.
|
||||
addvault.new.readme.storageLocation.8=2. Lås opp hvelvet i Cryptomator.
|
||||
addvault.new.readme.storageLocation.9=3. Åpne tilgangspunktet ved å klikke på "Gjør synlig" -knappen.
|
||||
addvault.new.readme.storageLocation.10=Hvis du trenger hjelp, kan du gå til dokumentasjonen: %s
|
||||
addvault.new.readme.storageLocation.10=Hvis du trenger hjelp, kan du se på dokumentasjonen: %s
|
||||
addvault.new.readme.accessLocation.fileName=VELKOMMEN.rtf
|
||||
addvault.new.readme.accessLocation.1=🔐️ KRYPTERT VOLUM 🔐️
|
||||
addvault.new.readme.accessLocation.2=Dette er hvelvets lagringssted.
|
||||
@@ -69,6 +76,7 @@ addvault.new.readme.accessLocation.3=Alle filer som er lagt til i dette volumet,
|
||||
addvault.new.readme.accessLocation.4=Denne filen kan fjernes hvis ønskelig.
|
||||
## Existing
|
||||
addvaultwizard.existing.chooseBtn=Velg…
|
||||
addvaultwizard.existing.filePickerTitle=Velg hvelvfil
|
||||
## Success
|
||||
addvaultwizard.success.nextStepsInstructions=Lagt til hvelvet "%s".\nDu må låse opp dette hvelvet for å få tilgang til eller legge til innhold. Alternativt kan du låse det opp på et hvilket som helst senere tidspunkt.
|
||||
addvaultwizard.success.unlockNow=Lås opp nå
|
||||
@@ -93,7 +101,8 @@ unlock.title=Lås opp "%s"
|
||||
unlock.passwordPrompt=Skriv inn passordet for "%s":
|
||||
unlock.savePassword=Husk passord
|
||||
unlock.unlockBtn=Lås opp
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=Velg…
|
||||
unlock.chooseMasterkey.filePickerTitle=Velg hovednøkkelfil
|
||||
## Success
|
||||
unlock.success.rememberChoice=Husk valget - ikke vis dette igjen
|
||||
@@ -106,8 +115,10 @@ unlock.error.invalidMountPoint.existing=Monteringspunktet "%s" finnes enten alle
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
lock.forced.heading=Låsing mislyktes
|
||||
lock.forced.message=Låsing "%s" ble blokkert av ventende operasjoner eller åpne filer. Du kan tvinge låsing av dette hvelvet, men avbrytelse av I/O kan føre til tap av ulagrede data.
|
||||
lock.forced.retryBtn=Prøv igjen
|
||||
lock.forced.forceBtn=Tving låsing
|
||||
## Failure
|
||||
lock.fail.heading=Låsing av hvelvet mislyktes.
|
||||
lock.fail.message=Hvelvet "%s" kunne ikke låses. Forsikre deg om at ulagrede arbeider lagres andre steder, og at viktige lese/skrive-operasjoner er fullført. For å lukke hvelvet må du avbryte Cryptomatorprosessen.
|
||||
@@ -119,22 +130,22 @@ migration.start.prompt=Hvelvet ditt "%s" må oppdateres til et nyere format. Fø
|
||||
migration.start.confirm=Ja, hvelvet mitt er fullstendig synkronisert
|
||||
## Run
|
||||
migration.run.enterPassword=Skriv inn passordet for "%s"
|
||||
migration.run.startMigrationBtn=Overfør hvelv
|
||||
migration.run.startMigrationBtn=Oppgrader hvelv
|
||||
migration.run.progressHint=Dette kan ta litt tid…
|
||||
## Success
|
||||
migration.success.nextStepsInstructions=Vellykket overføring av "%s".\nDu kan nå låse opp hvelvet ditt.
|
||||
migration.success.nextStepsInstructions=Vellykket oppgradering av "%s".\nDu kan nå låse opp hvelvet ditt.
|
||||
migration.success.unlockNow=Lås opp nå
|
||||
## Missing file system capabilities
|
||||
migration.error.missingFileSystemCapabilities.title=Filsystemet er ikke støttet
|
||||
migration.error.missingFileSystemCapabilities.description=Overføringen ble ikke startet fordi hvelvet ditt ligger i et mangelfullt filsystem.
|
||||
migration.error.missingFileSystemCapabilities.description=Oppgraderingen ble ikke startet fordi hvelvet ditt ligger i et mangelfullt filsystem.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Filsystemet støtter ikke lange filnavn.
|
||||
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Filsystemet støtter ikke lange søkestier.
|
||||
migration.error.missingFileSystemCapabilities.reason.READ_ACCESS=Filsystemet tillater ikke lesing.
|
||||
migration.error.missingFileSystemCapabilities.reason.WRITE_ACCESS=Filsystemet tillater ikke å bli skrevet på.
|
||||
## Impossible
|
||||
migration.impossible.heading=Kunne ikke overføre hvelvet
|
||||
migration.impossible.heading=Kunne ikke oppgradere hvelvet
|
||||
migration.impossible.reason=Hvelvet kan ikke overføres automatisk fordi lagringsstedet eller tilgangspunkt ikke er kompatibelt.
|
||||
migration.impossible.moreInfo=Hvelvet kan fortsatt åpnes hvis du bruker en eldre versjon. For instruksjoner om hvordan man overfører et hvelv, besøk
|
||||
migration.impossible.moreInfo=Hvelvet kan fortsatt åpnes hvis du bruker en eldre versjon. For veiledning om hvordan man oppgraderer et hvelv, besøk
|
||||
|
||||
# Health Check
|
||||
## Start
|
||||
@@ -143,6 +154,7 @@ health.intro.remarkFix=Ikke alle problemer kan løses.
|
||||
health.fail.moreInfo=Mer informasjon
|
||||
## Check Selection
|
||||
## Detail view
|
||||
health.check.exportBtn=Eksporter rapport
|
||||
## Fix Application
|
||||
health.fix.fixBtn=Reparer
|
||||
health.fix.successTip=Vellykket reparering
|
||||
@@ -175,7 +187,7 @@ preferences.volume.webdav.scheme=WebDAV-ordning
|
||||
## Updates
|
||||
preferences.updates=Oppdateringer
|
||||
preferences.updates.currentVersion=Gjeldende versjon: %s
|
||||
preferences.updates.autoUpdateCheck=Se etter oppdateringer automatisk
|
||||
preferences.updates.autoUpdateCheck=Se automatisk etter oppdateringer
|
||||
preferences.updates.checkNowBtn=Sjekk nå
|
||||
preferences.updates.updateAvailable=Oppdatering til versjon %s er tilgjengelig.
|
||||
## Contribution
|
||||
@@ -220,7 +232,7 @@ main.closeBtn.tooltip=Lukk
|
||||
main.minimizeBtn.tooltip=Minimer
|
||||
main.preferencesBtn.tooltip=Innstillinger
|
||||
main.debugModeEnabled.tooltip=Feilsøkingsmodus er aktivert
|
||||
main.donationKeyMissing.tooltip=Vennligst vurder å donere
|
||||
main.donationKeyMissing.tooltip=Gjerne vurdér en donasjon
|
||||
## Drag 'n' Drop
|
||||
main.dropZone.dropVault=Legg til dette hvelvet
|
||||
main.dropZone.unknownDragboardContent=Hvis du vil legge til et hvelv, kan du dra det til dette vinduet
|
||||
|
||||
@@ -102,8 +102,10 @@ unlock.title=Ontgrendel "%s"
|
||||
unlock.passwordPrompt=Voer wachtwoord voor "%s" in:
|
||||
unlock.savePassword=Wachtwoord Onthouden
|
||||
unlock.unlockBtn=Ontgrendel
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.title=Selecteer Masterkey van "%s"
|
||||
unlock.chooseMasterkey.prompt=Kon het sleutelbestand voor deze kluis niet vinden op de gewenste locatie. Kies het sleutelbestand handmatig.
|
||||
unlock.chooseMasterkey.chooseBtn=Kies…
|
||||
unlock.chooseMasterkey.filePickerTitle=Selecteer het Masterkey-bestand
|
||||
## Success
|
||||
unlock.success.message="%s" is met succes ontgrendeld! Uw kluis is nu toegankelijk via zijn virtuele schijf.
|
||||
@@ -114,6 +116,7 @@ unlock.error.heading=Kan kluis niet ontgrendelen
|
||||
### Invalid Mount Point
|
||||
unlock.error.invalidMountPoint.notExisting=Koppelpunt "%s" is geen map, is niet leeg of bestaat niet.
|
||||
unlock.error.invalidMountPoint.existing=Koppelpunt "%s" bestaat reeds of de bovenliggende map ontbreekt.
|
||||
unlock.error.invalidMountPoint.driveLetterOccupied=Schijf "%s" is al in gebruik.
|
||||
|
||||
# Lock
|
||||
## Force
|
||||
|
||||
@@ -88,7 +88,8 @@ forgetPassword.confirmBtn=Gløym passord
|
||||
# Unlock
|
||||
unlock.passwordPrompt=Skriv inn passordet for "%s":
|
||||
unlock.unlockBtn=Låse opp
|
||||
##
|
||||
## Select
|
||||
unlock.chooseMasterkey.chooseBtn=Vel…
|
||||
unlock.chooseMasterkey.filePickerTitle=Vel hovudnøkkelfil
|
||||
## Success
|
||||
unlock.success.rememberChoice=Hugs valet - ikkje vis dette igjen
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user