mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-21 12:11:28 +00:00
Compare commits
379 Commits
1.12.3-bet
...
2ndfactor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1fb987607c | ||
|
|
5d4066d29a | ||
|
|
d3909134d5 | ||
|
|
fd9d1bf0cf | ||
|
|
2f812377c3 | ||
|
|
ebce90eb74 | ||
|
|
c888b52ebb | ||
|
|
e422c7ce4b | ||
|
|
41ad8d5dcc | ||
|
|
526c3973d6 | ||
|
|
dc16c961af | ||
|
|
c54a603078 | ||
|
|
de3b2715c7 | ||
|
|
f3d8bd359d | ||
|
|
d7d6e46b2a | ||
|
|
501f682d68 | ||
|
|
7cbf1e10e5 | ||
|
|
fb00972bc1 | ||
|
|
387fb28c65 | ||
|
|
40fbfd10d1 | ||
|
|
e5e4695cd6 | ||
|
|
4a0c8ae80e | ||
|
|
384ca8eb2a | ||
|
|
59f360bde7 | ||
|
|
8830e0f5fb | ||
|
|
7a391a2825 | ||
|
|
a1941911ef | ||
|
|
a0667ff361 | ||
|
|
be59097a53 | ||
|
|
cbc41d535d | ||
|
|
f51780dcb5 | ||
|
|
c055488df7 | ||
|
|
b15c376e59 | ||
|
|
fdf03d2fdc | ||
|
|
437350af9f | ||
|
|
5114e2ad22 | ||
|
|
b3b4f61c42 | ||
|
|
fa16556af1 | ||
|
|
de766c2653 | ||
|
|
ccc4842839 | ||
|
|
d95625faa1 | ||
|
|
c5bfdf32a9 | ||
|
|
0598681a23 | ||
|
|
3b8bff0d47 | ||
|
|
55051072cb | ||
|
|
6cb409eeaf | ||
|
|
b38a350c48 | ||
|
|
0e5857015a | ||
|
|
9e9aa20692 | ||
|
|
ad49ca2cb4 | ||
|
|
ba84da71f3 | ||
|
|
5c34f59d09 | ||
|
|
7499867665 | ||
|
|
7b293b8082 | ||
|
|
46e8a13555 | ||
|
|
63a3150ec3 | ||
|
|
0a78987d30 | ||
|
|
929782c318 | ||
|
|
97a0f9c435 | ||
|
|
f7e65f4eec | ||
|
|
b3c56f3aab | ||
|
|
dcc27efe5c | ||
|
|
78ceb604f9 | ||
|
|
7bdcdcba3d | ||
|
|
e929d41d67 | ||
|
|
600aca083c | ||
|
|
b5dbfd3209 | ||
|
|
78675c9638 | ||
|
|
e8126e68ce | ||
|
|
c8075dbc19 | ||
|
|
2f1b5109d6 | ||
|
|
aa34ad52e6 | ||
|
|
175ed500a2 | ||
|
|
af3779ba2e | ||
|
|
953aee560f | ||
|
|
d8b798ff0f | ||
|
|
9cc863ae79 | ||
|
|
14ba852351 | ||
|
|
9516928529 | ||
|
|
62827b69cc | ||
|
|
2378227756 | ||
|
|
2194360c8a | ||
|
|
10bce1fb06 | ||
|
|
cfa3093dd0 | ||
|
|
02c20c01ee | ||
|
|
50cfdbbc0b | ||
|
|
38c556af0b | ||
|
|
0b9d777109 | ||
|
|
eb3c1f3c7a | ||
|
|
311c490377 | ||
|
|
39ed00ff6f | ||
|
|
28338d2dcf | ||
|
|
bba5d11bdc | ||
|
|
36ef191874 | ||
|
|
3e4c6c1b61 | ||
|
|
c9d0224814 | ||
|
|
8aed6045a0 | ||
|
|
8440705436 | ||
|
|
af667b10cf | ||
|
|
1b522fe9a0 | ||
|
|
cb3c46744e | ||
|
|
c471289fb5 | ||
|
|
18ca563865 | ||
|
|
2430526ee7 | ||
|
|
c9bb0235e2 | ||
|
|
2f8236e9db | ||
|
|
e4e757606d | ||
|
|
6fcb796177 | ||
|
|
4d6b035c29 | ||
|
|
68c206a4fa | ||
|
|
d258171131 | ||
|
|
adaf9ef5ce | ||
|
|
22f299f67c | ||
|
|
0fb0bc8e1d | ||
|
|
da666410fc | ||
|
|
ea007f3cfe | ||
|
|
1c868a644f | ||
|
|
5ff6a8c258 | ||
|
|
62d7b7a0c0 | ||
|
|
7fcbb57ab1 | ||
|
|
8e25dcd396 | ||
|
|
a9f1e5b761 | ||
|
|
f642149723 | ||
|
|
4b359d98cb | ||
|
|
764fccf0b5 | ||
|
|
81d1059489 | ||
|
|
a2574a5883 | ||
|
|
118a1411d8 | ||
|
|
44310cbd0e | ||
|
|
2dce7b6f71 | ||
|
|
98db82d137 | ||
|
|
fd55656261 | ||
|
|
978dec64ee | ||
|
|
8d7bf3a370 | ||
|
|
1cea7bcffc | ||
|
|
16e77d4b9b | ||
|
|
f22111e36f | ||
|
|
c5d4c9a9c1 | ||
|
|
4bc9b1d60c | ||
|
|
7a40a3cb0c | ||
|
|
ad555ece8e | ||
|
|
2d96d2e5c6 | ||
|
|
0a968b60aa | ||
|
|
3b4f82092d | ||
|
|
de16647d00 | ||
|
|
03403e53bc | ||
|
|
2939702842 | ||
|
|
b3d09a4cae | ||
|
|
73c0af9749 | ||
|
|
53c7e031a3 | ||
|
|
1e280f2c97 | ||
|
|
ce82593fc6 | ||
|
|
c2c3d778d1 | ||
|
|
7771181e15 | ||
|
|
1a39333b4c | ||
|
|
85472db00a | ||
|
|
d6a020994e | ||
|
|
518f75be32 | ||
|
|
28c7dbad69 | ||
|
|
31e9f3a025 | ||
|
|
a03d5af144 | ||
|
|
50a654d5af | ||
|
|
1954f31910 | ||
|
|
68bf441fdd | ||
|
|
a606bcb81e | ||
|
|
fe93e51ffa | ||
|
|
f2949aae63 | ||
|
|
80e101456b | ||
|
|
2f311c1dfe | ||
|
|
c5cfe4d1b3 | ||
|
|
a09b55c81b | ||
|
|
32f266c721 | ||
|
|
3adffad087 | ||
|
|
84e3cf0762 | ||
|
|
1292936904 | ||
|
|
7a913c89c9 | ||
|
|
4c4816bdab | ||
|
|
f9bfd5d1b1 | ||
|
|
00e1e3654e | ||
|
|
8aaee3710a | ||
|
|
1946fc6c0e | ||
|
|
8fb95b67d9 | ||
|
|
e6890e8fa5 | ||
|
|
7cdb2025dc | ||
|
|
414d81dc06 | ||
|
|
b450ab028d | ||
|
|
5a51e53603 | ||
|
|
d65beb632d | ||
|
|
2b03477d31 | ||
|
|
f25e23da82 | ||
|
|
82368e2632 | ||
|
|
9057090b56 | ||
|
|
357937e0bf | ||
|
|
f84a2396eb | ||
|
|
193dcad6e8 | ||
|
|
257f4427b8 | ||
|
|
4778ba0770 | ||
|
|
77b908199e | ||
|
|
d448c42f87 | ||
|
|
b9403222ba | ||
|
|
93d0fd41a6 | ||
|
|
067bd4752c | ||
|
|
fb4f9fc68f | ||
|
|
a20667a156 | ||
|
|
4d47544244 | ||
|
|
d59f149f7c | ||
|
|
028f6e7a02 | ||
|
|
71e4f98665 | ||
|
|
d60ced52fe | ||
|
|
45c7d2b071 | ||
|
|
7a789d2eed | ||
|
|
cc1e542146 | ||
|
|
e4e84d46b6 | ||
|
|
61fc53bf9c | ||
|
|
f8386a5a99 | ||
|
|
da7e89fc72 | ||
|
|
6b57a0ad9c | ||
|
|
f1eb997804 | ||
|
|
2fe5180721 | ||
|
|
958c22bed8 | ||
|
|
6ff640648f | ||
|
|
7b132adfe2 | ||
|
|
2fa88490bd | ||
|
|
5f5b5a5426 | ||
|
|
cb054bb4f5 | ||
|
|
3497144034 | ||
|
|
b4f9bc62ca | ||
|
|
34bface2d7 | ||
|
|
70e52b1a31 | ||
|
|
59a2398c4c | ||
|
|
edbeea5502 | ||
|
|
a88e08147d | ||
|
|
8ff06a3efd | ||
|
|
902c66cf1e | ||
|
|
d58307d1d6 | ||
|
|
3e6204a657 | ||
|
|
e677a0beaa | ||
|
|
e5003c1783 | ||
|
|
d379ada100 | ||
|
|
cb7d0ade47 | ||
|
|
d6fd012f71 | ||
|
|
d9d8c84230 | ||
|
|
4d9dc4312d | ||
|
|
1ad2cc3ee5 | ||
|
|
2eac09f6be | ||
|
|
4114ea6163 | ||
|
|
82da9f7a88 | ||
|
|
aa08046c4d | ||
|
|
b6d7f63470 | ||
|
|
624d18238c | ||
|
|
3bfe2b7a9e | ||
|
|
ba79cd0f8c | ||
|
|
be63ea1104 | ||
|
|
a3ad2a5677 | ||
|
|
26e5ceea01 | ||
|
|
83f1c4ea41 | ||
|
|
8d78e1432a | ||
|
|
d2ec5a5646 | ||
|
|
6bb0328799 | ||
|
|
8a8617b480 | ||
|
|
92fad41b96 | ||
|
|
7484744038 | ||
|
|
0bbc4ecae5 | ||
|
|
9fcb166047 | ||
|
|
1ac017e3ab | ||
|
|
1ba401b8e6 | ||
|
|
07303b58f7 | ||
|
|
32a463768c | ||
|
|
2307950e3b | ||
|
|
8108128c22 | ||
|
|
58fe6da7a8 | ||
|
|
92bf73297a | ||
|
|
0bf8fb39dd | ||
|
|
0dc9a3a834 | ||
|
|
91d6264b9f | ||
|
|
d2599ea525 | ||
|
|
7180af9bc7 | ||
|
|
dc993dc57c | ||
|
|
464c74ab6c | ||
|
|
e038348dca | ||
|
|
8c7dd8c74f | ||
|
|
69bcf6fac6 | ||
|
|
7c243dd434 | ||
|
|
357a0b9c31 | ||
|
|
d69d11b82f | ||
|
|
863e9bbcb3 | ||
|
|
1c47012033 | ||
|
|
cd99eaa323 | ||
|
|
dda2afda92 | ||
|
|
5519eefcfa | ||
|
|
a81352800d | ||
|
|
6fec16e498 | ||
|
|
42f31204a3 | ||
|
|
7ab64cfe46 | ||
|
|
1581a10c04 | ||
|
|
249becc25d | ||
|
|
da8e84f39d | ||
|
|
9ad2d223c3 | ||
|
|
3bf1e659ef | ||
|
|
e79257e5ea | ||
|
|
dac517a6fb | ||
|
|
1ab808f1b0 | ||
|
|
08f81b7df4 | ||
|
|
b16f32ca83 | ||
|
|
d6ed88b544 | ||
|
|
ae697d7b73 | ||
|
|
ff3306cc17 | ||
|
|
9ae2f4e0f6 | ||
|
|
4230afcbac | ||
|
|
7fc6ab05a4 | ||
|
|
060f6bfc97 | ||
|
|
919dac6caa | ||
|
|
98bcf63b2c | ||
|
|
0585262952 | ||
|
|
a33dc3980e | ||
|
|
caa5e77386 | ||
|
|
c037920e79 | ||
|
|
3cdd352d39 | ||
|
|
8ae80e2932 | ||
|
|
2ffa1ee236 | ||
|
|
2bf5b57823 | ||
|
|
853ea69180 | ||
|
|
43d0dd99ec | ||
|
|
b40e6db701 | ||
|
|
387eb420eb | ||
|
|
7785bb8820 | ||
|
|
de4fa8c7b0 | ||
|
|
17f519e01c | ||
|
|
6c50023074 | ||
|
|
0009940e1e | ||
|
|
f2a50b59b5 | ||
|
|
ed03606981 | ||
|
|
4a1eaf25c7 | ||
|
|
f5e445a610 | ||
|
|
6a3a256c0b | ||
|
|
1ed77ebcc0 | ||
|
|
db224e9e5c | ||
|
|
c719982ef3 | ||
|
|
f783f5d5ec | ||
|
|
dd4f87b54c | ||
|
|
b3789700e1 | ||
|
|
b39834f4eb | ||
|
|
8064d75102 | ||
|
|
4064b61cd7 | ||
|
|
ef3affece3 | ||
|
|
414465371b | ||
|
|
feed72a729 | ||
|
|
bca20a7a66 | ||
|
|
a38377baaa | ||
|
|
61a45fc738 | ||
|
|
3a65b5551f | ||
|
|
94ed5c18b8 | ||
|
|
a559483d86 | ||
|
|
3acdef1dd0 | ||
|
|
def6f8ab95 | ||
|
|
11ba1f3ddc | ||
|
|
3228f2cf5f | ||
|
|
9c4f7ad79d | ||
|
|
3bd57d162b | ||
|
|
5ea73a5a8d | ||
|
|
38670838c7 | ||
|
|
3e0b84dbce | ||
|
|
2302db6206 | ||
|
|
a94bf99660 | ||
|
|
0c5ce353b1 | ||
|
|
b3ce777a42 | ||
|
|
3085df3397 | ||
|
|
fb740b605f | ||
|
|
6a74d9f3b2 | ||
|
|
1c7dffb63f | ||
|
|
a213f073b1 | ||
|
|
1ab73be1f4 | ||
|
|
8412871090 | ||
|
|
fa63f3ca67 | ||
|
|
dbc0f52481 | ||
|
|
b8cd1caeac | ||
|
|
43c25b6d97 | ||
|
|
d85c6c8f41 | ||
|
|
17057e8f8d |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -3,7 +3,7 @@
|
|||||||
## Did you find a bug?
|
## Did you find a bug?
|
||||||
|
|
||||||
- Ensure you're running the latest version of Cryptomator.
|
- Ensure you're running the latest version of Cryptomator.
|
||||||
- Ensure the bug is related to the desktop version of Cryptomator. Bugs concerning the Cryptomator iOS and Android app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/cryptomator-ios/issues) and [Cryptomator for Android issues list](https://github.com/cryptomator/cryptomator-android/issues) respectively.
|
- Ensure the bug is related to the desktop version of Cryptomator. Bugs concerning the Cryptomator iOS and Android app can be reported on the [Cryptomator for iOS issues list](https://github.com/cryptomator/ios/issues) and [Cryptomator for Android issues list](https://github.com/cryptomator/android/issues) respectively.
|
||||||
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [FAQ](https://community.cryptomator.org/c/kb/faq).
|
- Ensure the bug was not [already reported](https://github.com/cryptomator/cryptomator/issues). You can also check out our [FAQ](https://community.cryptomator.org/c/kb/faq).
|
||||||
- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptomator/issues/new/choose).
|
- If you're unable to find an open issue addressing the problem, [submit a new one](https://github.com/cryptomator/cryptomator/issues/new/choose).
|
||||||
|
|
||||||
|
|||||||
7
.github/dependabot.yml
vendored
7
.github/dependabot.yml
vendored
@@ -7,6 +7,13 @@ updates:
|
|||||||
day: "monday"
|
day: "monday"
|
||||||
time: "06:00"
|
time: "06:00"
|
||||||
timezone: "Etc/UTC"
|
timezone: "Etc/UTC"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "org.cryptomator:integrations-api"
|
||||||
|
versions: ["2.0.0-alpha1"]
|
||||||
|
- dependency-name: "jakarta.inject:jakarta.inject-api"
|
||||||
|
versions: ["2.0.1.MR"]
|
||||||
|
- dependency-name: "org.openjfx:*"
|
||||||
|
update-types: ["version-update:semver-major"]
|
||||||
groups:
|
groups:
|
||||||
java-test-dependencies:
|
java-test-dependencies:
|
||||||
patterns:
|
patterns:
|
||||||
|
|||||||
24
.github/workflows/appimage.yml
vendored
24
.github/workflows/appimage.yml
vendored
@@ -11,7 +11,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: '21.0.2+13'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -29,12 +29,12 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
appimage-suffix: x86_64
|
appimage-suffix: x86_64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
|
||||||
openjfx-sha: '7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
openjfx-sha: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
||||||
- os: [self-hosted, Linux, ARM64]
|
- os: [self-hosted, Linux, ARM64]
|
||||||
appimage-suffix: aarch64
|
appimage-suffix: aarch64
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
openjfx-sha: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: mvn -B clean package -Plinux -DskipTests -Djavafx.platform=linux
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -80,17 +80,12 @@ jobs:
|
|||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
--strip-debug
|
--strip-debug
|
||||||
--compress zip-0
|
--compress zip-0
|
||||||
- name: Prepare additional launcher
|
|
||||||
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
|
|
||||||
env:
|
|
||||||
SEMVER_STR: ${{ needs.get-version.outputs.semVerStr }}
|
|
||||||
REVISION_NUM: ${{ needs.get-version.outputs.revNum }}
|
|
||||||
- name: Run jpackage
|
- name: Run jpackage
|
||||||
run: >
|
run: >
|
||||||
${JAVA_HOME}/bin/jpackage
|
${JAVA_HOME}/bin/jpackage
|
||||||
@@ -121,7 +116,6 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.showTrayIcon=true"
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
--java-options "-Dcryptomator.integrationsLinux.trayIconsDir=\"@{appdir}/usr/share/icons/hicolor/symbolic/apps\""
|
||||||
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
|
|
||||||
--resource-dir dist/linux/resources
|
--resource-dir dist/linux/resources
|
||||||
- name: Patch Cryptomator.AppDir
|
- name: Patch Cryptomator.AppDir
|
||||||
run: |
|
run: |
|
||||||
@@ -173,8 +167,8 @@ jobs:
|
|||||||
cryptomator-*.asc
|
cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish AppImage on GitHub Releases
|
- name: Publish AppImage on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published'
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
40
.github/workflows/av-whitelist.yml
vendored
Normal file
40
.github/workflows/av-whitelist.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: AntiVirus Whitelisting
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
url:
|
||||||
|
description: "Url to the file to upload"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
url:
|
||||||
|
description: "Url to the file to upload"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
allowlist:
|
||||||
|
name: Anti Virus Allowlisting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download file
|
||||||
|
run: |
|
||||||
|
curl --remote-name ${{ inputs.url }} -L
|
||||||
|
- name: Upload to Kaspersky
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
protocol: ftps
|
||||||
|
server: allowlist.kaspersky-labs.com
|
||||||
|
port: 990
|
||||||
|
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
|
||||||
|
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
|
||||||
|
- name: Upload to Avast
|
||||||
|
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||||
|
with:
|
||||||
|
protocol: ftp
|
||||||
|
server: whitelisting.avast.com
|
||||||
|
port: 21
|
||||||
|
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
||||||
|
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
||||||
27
.github/workflows/build.yml
vendored
27
.github/workflows/build.yml
vendored
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: 21
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
run: >
|
run: >
|
||||||
xvfb-run
|
xvfb-run
|
||||||
mvn -B verify
|
mvn -B verify -Djavafx.platform=linux
|
||||||
jacoco:report
|
jacoco:report
|
||||||
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
|
||||||
-Pcoverage
|
-Pcoverage
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
- name: Draft a release
|
- name: Draft a release
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
discussion_category_name: releases
|
discussion_category_name: releases
|
||||||
@@ -53,10 +53,25 @@ jobs:
|
|||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
body: |-
|
body: |-
|
||||||
:construction: Work in Progress
|
:construction: Work in Progress
|
||||||
|
### What's New 🎉
|
||||||
|
|
||||||
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
|
### Bugfixes 🐛
|
||||||
|
|
||||||
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
### Other Changes 📎
|
||||||
|
|
||||||
---
|
---
|
||||||
<!-- Don't forget to include the 💾 SHA-256 checksums of release artifacts: -->
|
|
||||||
|
TODO FULL CHANGELOG
|
||||||
|
|
||||||
|
📜 List of closed issues is available [here](TODO)
|
||||||
|
|
||||||
|
---
|
||||||
|
⏳ Please be patient, the builds are still [running](https://github.com/cryptomator/cryptomator/actions). New versions of Cryptomator can be found here in a few moments. ⏳
|
||||||
|
|
||||||
|
<!-- Don't forget to include the
|
||||||
|
💾 SHA-256 checksums of release artifacts:
|
||||||
|
```
|
||||||
|
```
|
||||||
|
-->
|
||||||
|
|
||||||
|
As usual, the GPG signatures can be checked using [our public key `5811 7AFA 1F85 B3EE C154 677D 615D 449F E6E6 A235`](https://gist.github.com/cryptobot/211111cf092037490275f39d408f461a).
|
||||||
|
|||||||
97
.github/workflows/check-jdk-updates.yml
vendored
97
.github/workflows/check-jdk-updates.yml
vendored
@@ -1,56 +1,75 @@
|
|||||||
name: Checks JDK version for minor updates
|
name: Check JDK for non-major updates
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 1 * *' # run once a month at the first day of month
|
- cron: '0 0 1 * *' # run once a month at the first day of month
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
JDK_VERSION: '21.0.1+12'
|
JDK_VERSION: '23.0.1+11'
|
||||||
JDK_VENDOR: zulu
|
JDK_VENDOR: zulu
|
||||||
|
RUNTIME_VERSION_HELPER: >
|
||||||
|
public class Test {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(Runtime.version());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
jdk-current:
|
check-version:
|
||||||
name: Check out current version
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: ${{ env.JDK_VERSION }}
|
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
|
||||||
check-latest: false
|
|
||||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
|
||||||
id: get-data
|
|
||||||
run: |
|
|
||||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
|
||||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
|
||||||
jdk-latest:
|
|
||||||
name: Checkout latest jdk version
|
name: Checkout latest jdk version
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
env:
|
||||||
jdk-date: ${{ steps.get-data.outputs.jdk-date}}
|
JDK_MAJOR_VERSION: 'toBeFilled'
|
||||||
jdk-version: ${{ steps.get-data.outputs.jdk-version}}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-java@v4
|
- name: Determine current major version
|
||||||
|
run: echo 'JDK_MAJOR_VERSION=${{ env.JDK_VERSION }}'.substring(0,20) >> "$env:GITHUB_ENV"
|
||||||
|
shell: pwsh
|
||||||
|
- name: Checkout latest JDK ${{ env.JDK_MAJOR_VERSION }}
|
||||||
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 21
|
java-version: ${{ env.JDK_MAJOR_VERSION}}
|
||||||
distribution: ${{ env.JDK_VENDOR }}
|
distribution: ${{ env.JDK_VENDOR }}
|
||||||
check-latest: true
|
check-latest: true
|
||||||
- name: Read JAVA_VERSION_DATE and store in env variable
|
- name: Determine if update is available
|
||||||
id: get-data
|
id: determine
|
||||||
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
date=$(cat ${JAVA_HOME}/release | grep "JAVA_VERSION_DATE=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
$latestVersion = 0,0,0,0 #INTERIM, UPDATE, PATCH and BUILD
|
||||||
echo "jdk-date=${date}" >> "$GITHUB_OUTPUT"
|
$currentVersion = 0,0,0,0
|
||||||
version=$(cat ${JAVA_HOME}/release | grep "JAVA_RUNTIME_VERSION=\"" | awk -F'=' '{print $2}' | tr -d '"')
|
|
||||||
echo "jdk-version=${version}" >> "$GITHUB_OUTPUT"
|
# Get the latest JDK runtime version
|
||||||
notify:
|
"${env:RUNTIME_VERSION_HELPER}" | Set-Content -Path "GetRuntimeVersion.java"
|
||||||
name: Notifies for jdk update
|
$latestVersionString = & java GetRuntimeVersion.java
|
||||||
runs-on: ubuntu-latest
|
$runtimeVersionAndBuild = $latestVersionString.Split('+')
|
||||||
needs: [jdk-current, jdk-latest]
|
if($runtimeVersionAndBuild.Length -eq 2) {
|
||||||
if: ${{ needs.jdk-latest.outputs.jdk-date }} > ${{ needs.jdk-current.outputs.jdk-date }}
|
$latestVersion[3]=$runtimeVersionAndBuild[1];
|
||||||
steps:
|
}
|
||||||
- name: Slack Notification
|
$tmp=$runtimeVersionAndBuild[0].Split('.')
|
||||||
|
for($i=0;$i -lt $latestVersion.Length; $i++) {
|
||||||
|
$latestVersion[$i]=$tmp[$i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the current JDK version
|
||||||
|
$runtimeVersionAndBuild = '${{ env.JDK_VERSION}}'.Split('+')
|
||||||
|
if($runtimeVersionAndBuild.Length -eq 2) {
|
||||||
|
$currentVersion[3]=$runtimeVersionAndBuild[1];
|
||||||
|
}
|
||||||
|
$tmp=$runtimeVersionAndBuild[0].Split('.')
|
||||||
|
for($i=0;$i -lt $currentVersion.Length; $i++) {
|
||||||
|
$currentVersion[$i]=$tmp[$i+1];
|
||||||
|
}
|
||||||
|
|
||||||
|
# compare
|
||||||
|
for($i=0; $i -lt $currentVersion.Length ; $i++) {
|
||||||
|
if($latestVersion[$i] -gt $currentVersion[$i]){
|
||||||
|
echo 'UPDATE_AVAILABLE=true' >> "$env:GITHUB_OUTPUT"
|
||||||
|
echo "LATEST_JDK_VERSION='${latestVersionString}'" >> "$env:GITHUB_OUTPUT"
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- name: Notify
|
||||||
|
if: steps.determine.outputs.UPDATE_AVAILABLE == 'true'
|
||||||
uses: rtCamp/action-slack-notify@v2
|
uses: rtCamp/action-slack-notify@v2
|
||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
@@ -59,6 +78,6 @@ jobs:
|
|||||||
SLACK_ICON_EMOJI: ':bot:'
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
SLACK_CHANNEL: 'cryptomator-desktop'
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
SLACK_TITLE: "JDK update available"
|
SLACK_TITLE: "JDK update available"
|
||||||
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ needs.jdk-latest.outputs.jdk-version }}. See https://github.com/cryptomator/cryptomator/wiki/How-to-update-the-build-JDK for instructions."
|
SLACK_MESSAGE: "Cryptomator-CI JDK can be upgraded to ${{ steps.determine.outputs.LATEST_JDK_VERSION }}. Check the Nextcloud collective for instructions."
|
||||||
SLACK_FOOTER: false
|
SLACK_FOOTER: false
|
||||||
MSG_MINIMAL: true
|
MSG_MINIMAL: true
|
||||||
19
.github/workflows/debian.yml
vendored
19
.github/workflows/debian.yml
vendored
@@ -17,13 +17,13 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: '21.0.2+13'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
COFFEELIBS_JDK: 21
|
COFFEELIBS_JDK: 23
|
||||||
COFFEELIBS_JDK_VERSION: '21.0.2+13-0ppa1'
|
COFFEELIBS_JDK_VERSION: '23.0.1+11-0ppa1'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: '7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
OPENJFX_JMODS_AMD64_HASH: 'd44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
||||||
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
OPENJFX_JMODS_AARCH64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_linux-aarch64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AARCH64_HASH: '871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
OPENJFX_JMODS_AARCH64_HASH: '3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo add-apt-repository ppa:coffeelibs/openjdk
|
sudo add-apt-repository ppa:coffeelibs/openjdk
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }} libgtk2.0-0
|
sudo apt-get install debhelper devscripts dput coffeelibs-jdk-${{ env.COFFEELIBS_JDK }}=${{ env.COFFEELIBS_JDK_VERSION }}
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
check-latest: true
|
check-latest: true
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Plinux -DskipTests
|
run: mvn -B clean package -Plinux -Djavafx.platform=linux -DskipTests
|
||||||
- name: Download OpenJFX jmods
|
- name: Download OpenJFX jmods
|
||||||
id: download-jmods
|
id: download-jmods
|
||||||
run: |
|
run: |
|
||||||
@@ -142,7 +142,6 @@ jobs:
|
|||||||
- name: Publish on PPA
|
- name: Publish on PPA
|
||||||
if: inputs.dput
|
if: inputs.dput
|
||||||
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
|
run: dput ppa:sebastian-stenzel/cryptomator-beta cryptomator_*_source.changes
|
||||||
|
|
||||||
# If ref is a tag, also upload to GitHub Releases:
|
# If ref is a tag, also upload to GitHub Releases:
|
||||||
- name: Publish Debian package on GitHub Releases
|
- name: Publish Debian package on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && inputs.dput
|
if: startsWith(github.ref, 'refs/tags/') && inputs.dput
|
||||||
|
|||||||
3
.github/workflows/dependency-check.yml
vendored
3
.github/workflows/dependency-check.yml
vendored
@@ -11,7 +11,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
runner-os: 'ubuntu-latest'
|
runner-os: 'ubuntu-latest'
|
||||||
java-distribution: 'temurin'
|
java-distribution: 'temurin'
|
||||||
java-version: 21
|
java-version: 23
|
||||||
|
check-command: 'mvn -B validate -Pdependency-check -Djavafx.platform=linux'
|
||||||
secrets:
|
secrets:
|
||||||
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
nvd-api-key: ${{ secrets.NVD_API_KEY }}
|
||||||
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
|
|||||||
88
.github/workflows/flathub.yml
vendored
Normal file
88
.github/workflows/flathub.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
name: Create PR for flathub
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: 'Release tag'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-version:
|
||||||
|
uses: ./.github/workflows/get-version.yml
|
||||||
|
with:
|
||||||
|
version: ${{ inputs.tag }}
|
||||||
|
tarball:
|
||||||
|
name: Determines tarball url and compute checksum
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [get-version]
|
||||||
|
if: github.event_name == 'workflow_dispatch' || needs.get-version.outputs.versionType == 'stable'
|
||||||
|
outputs:
|
||||||
|
url: ${{ steps.url.outputs.url}}
|
||||||
|
sha512: ${{ steps.sha512.outputs.sha512}}
|
||||||
|
steps:
|
||||||
|
- name: Determine tarball url
|
||||||
|
id: url
|
||||||
|
run: |
|
||||||
|
URL="";
|
||||||
|
if [[ -n "${{ inputs.tag }}" ]]; then
|
||||||
|
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ inputs.tag }}.tar.gz"
|
||||||
|
else
|
||||||
|
URL="https://github.com/cryptomator/cryptomator/archive/refs/tags/${{ github.event.release.tag_name }}.tar.gz"
|
||||||
|
fi
|
||||||
|
echo "url=${URL}" >> "$GITHUB_OUTPUT"
|
||||||
|
- name: Download source tarball and compute checksum
|
||||||
|
id: sha512
|
||||||
|
run: |
|
||||||
|
curl --silent --fail-with-body -L -H "Accept: application/vnd.github+json" ${{ steps.url.outputs.url }} --output cryptomator.tar.gz
|
||||||
|
TARBALL_SHA512=$(sha512sum cryptomator.tar.gz | cut -d ' ' -f1)
|
||||||
|
echo "sha512=${TARBALL_SHA512}" >> "$GITHUB_OUTPUT"
|
||||||
|
flathub:
|
||||||
|
name: Create PR for flathub
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [tarball, get-version]
|
||||||
|
env:
|
||||||
|
FLATHUB_PR_URL: tbd
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: 'flathub/org.cryptomator.Cryptomator'
|
||||||
|
token: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
||||||
|
- name: Checkout release branch
|
||||||
|
run: |
|
||||||
|
git checkout -b release/${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
- name: Update build file
|
||||||
|
run: |
|
||||||
|
sed -i -e 's/VERSION: [0-9]\+\.[0-9]\+\.[0-9]\+.*/VERSION: ${{ needs.get-version.outputs.semVerStr }}/g' org.cryptomator.Cryptomator.yaml
|
||||||
|
sed -i -e 's/sha512: [0-9A-Za-z_\+-]\{128\} #CRYPTOMATOR/sha512: ${{ needs.tarball.outputs.sha512 }} #CRYPTOMATOR/g' org.cryptomator.Cryptomator.yaml
|
||||||
|
sed -i -e 's;url: https://github.com/cryptomator/cryptomator/archive/refs/tags/[^[:blank:]]\+;url: ${{ needs.tarball.outputs.url }};g' org.cryptomator.Cryptomator.yaml
|
||||||
|
- name: Commit and push
|
||||||
|
run: |
|
||||||
|
git config user.name "${{ github.actor }}"
|
||||||
|
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
||||||
|
git config push.autoSetupRemote true
|
||||||
|
git stage .
|
||||||
|
git commit -m "Prepare release ${{needs.get-version.outputs.semVerStr}}"
|
||||||
|
git push
|
||||||
|
- name: Create pull request
|
||||||
|
run: |
|
||||||
|
printf "> [!IMPORTANT]\n> Todos:\n> - [ ] Update maven dependencies\n> - [ ] Check for JDK update\n> - [ ] Check for JFX update" > pr_body.md
|
||||||
|
PR_URL=$(gh pr create --title "Release ${{ needs.get-version.outputs.semVerStr }}" --body-file pr_body.md)
|
||||||
|
echo "FLATHUB_PR_URL=$PR_URL" >> "$GITHUB_ENV"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
||||||
|
- name: Slack Notification
|
||||||
|
uses: rtCamp/action-slack-notify@v2
|
||||||
|
if: github.event_name == 'release'
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||||
|
SLACK_USERNAME: 'Cryptobot'
|
||||||
|
SLACK_ICON: false
|
||||||
|
SLACK_ICON_EMOJI: ':bot:'
|
||||||
|
SLACK_CHANNEL: 'cryptomator-desktop'
|
||||||
|
SLACK_TITLE: "Flathub release PR created for ${{ github.event.repository.name }} ${{ github.event.release.tag_name }} created."
|
||||||
|
SLACK_MESSAGE: "See <${{ env.FLATHUB_PR_URL }}|PR> on how to proceed.>."
|
||||||
|
SLACK_FOOTER: false
|
||||||
|
MSG_MINIMAL: true
|
||||||
8
.github/workflows/get-version.yml
vendored
8
.github/workflows/get-version.yml
vendored
@@ -23,7 +23,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: 21
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
determine-version:
|
determine-version:
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
if [[ $GITHUB_REF =~ refs/tags/[0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||||
SEM_VER_STR=${GITHUB_REF##*/}
|
SEM_VER_STR=${GITHUB_REF##*/}
|
||||||
elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
elif [[ "${{ inputs.version }}" =~ [0-9]+\.[0-9]+\.[0-9]+.* ]]; then
|
||||||
SEM_VER_STR="${{ github.event.inputs.version }}"
|
SEM_VER_STR="${{ inputs.version }}"
|
||||||
else
|
else
|
||||||
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
SEM_VER_STR=`mvn help:evaluate -Dexpression=project.version -q -DforceStdout`
|
||||||
fi
|
fi
|
||||||
@@ -71,6 +71,6 @@ jobs:
|
|||||||
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
echo "revNum=${REVCOUNT}" >> $GITHUB_OUTPUT
|
||||||
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
echo "type=${TYPE}" >> $GITHUB_OUTPUT
|
||||||
- name: Validate Version
|
- name: Validate Version
|
||||||
uses: skymatic/semver-validation-action@v2
|
uses: skymatic/semver-validation-action@v3
|
||||||
with:
|
with:
|
||||||
version: ${{ steps.versions.outputs.semVerStr }}
|
version: ${{ steps.versions.outputs.semVerStr }}
|
||||||
264
.github/workflows/mac-dmg-x64.yml
vendored
Normal file
264
.github/workflows/mac-dmg-x64.yml
vendored
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
name: Build macOS .dmg for x64
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# STOP! DO NOT EDIT THIS FILE!
|
||||||
|
#
|
||||||
|
# It is a copy of mac-dmg.yml with tiny adjustements (mainly lines 42 to 47)
|
||||||
|
# It was made necessary, since Github does not offer free macos intel runners for macos 15 and above.
|
||||||
|
# This workflow can only be triggered by a release.
|
||||||
|
#
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
env:
|
||||||
|
JAVA_DIST: 'zulu'
|
||||||
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-version:
|
||||||
|
uses: ./.github/workflows/get-version.yml
|
||||||
|
with:
|
||||||
|
version: ${{ inputs.version }}
|
||||||
|
|
||||||
|
build-arm:
|
||||||
|
name: Build Cryptomator.app for ${{ matrix.output-suffix }}
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
needs: [get-version]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-15-large
|
||||||
|
architecture: x64
|
||||||
|
output-suffix: x64
|
||||||
|
fuse-lib: macFUSE
|
||||||
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-x64_bin-jmods.zip'
|
||||||
|
openjfx-sha: '115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: ${{ env.JAVA_DIST }}
|
||||||
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
|
architecture: ${{ matrix.architecture }}
|
||||||
|
check-latest: true
|
||||||
|
cache: 'maven'
|
||||||
|
- name: Download OpenJFX jmods
|
||||||
|
id: download-jmods
|
||||||
|
run: |
|
||||||
|
curl -L ${{ matrix.openjfx-url }} -o openjfx-jmods.zip
|
||||||
|
echo "${{ matrix.openjfx-sha }} *openjfx-jmods.zip" | shasum -a256 --check
|
||||||
|
mkdir -p openjfx-jmods/
|
||||||
|
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
|
- name: Ensure major jfx version in pom and in jmods is the same
|
||||||
|
run: |
|
||||||
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
|
JMOD_VERSION=${JMOD_VERSION#*@}
|
||||||
|
JMOD_VERSION=${JMOD_VERSION%%.*}
|
||||||
|
POM_JFX_VERSION=$(mvn help:evaluate "-Dexpression=javafx.version" -q -DforceStdout)
|
||||||
|
POM_JFX_VERSION=${POM_JFX_VERSION#*@}
|
||||||
|
POM_JFX_VERSION=${POM_JFX_VERSION%%.*}
|
||||||
|
|
||||||
|
if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
||||||
|
>&2 echo "Major JavaFX version in pom.xml (${POM_JFX_VERSION}) != jmod version (${JMOD_VERSION})"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Set version
|
||||||
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
|
- name: Run maven
|
||||||
|
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
|
||||||
|
- name: Patch target dir
|
||||||
|
run: |
|
||||||
|
cp LICENSE.txt target
|
||||||
|
cp target/cryptomator-*.jar target/mods
|
||||||
|
- name: Run jlink
|
||||||
|
#Remark: no compression is applied for improved build compression later (here dmg)
|
||||||
|
run: >
|
||||||
|
${JAVA_HOME}/bin/jlink
|
||||||
|
--verbose
|
||||||
|
--output runtime
|
||||||
|
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
||||||
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||||
|
--strip-native-commands
|
||||||
|
--no-header-files
|
||||||
|
--no-man-pages
|
||||||
|
--strip-debug
|
||||||
|
--compress zip-0
|
||||||
|
- name: Run jpackage
|
||||||
|
run: >
|
||||||
|
${JAVA_HOME}/bin/jpackage
|
||||||
|
--verbose
|
||||||
|
--type app-image
|
||||||
|
--runtime-image runtime
|
||||||
|
--input target/libs
|
||||||
|
--module-path target/mods
|
||||||
|
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
|
||||||
|
--dest appdir
|
||||||
|
--name Cryptomator
|
||||||
|
--vendor "Skymatic GmbH"
|
||||||
|
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||||
|
--app-version "${{ needs.get-version.outputs.semVerNum }}"
|
||||||
|
--java-options "--enable-preview"
|
||||||
|
--java-options "--enable-native-access=org.cryptomator.jfuse.mac"
|
||||||
|
--java-options "-Xss5m"
|
||||||
|
--java-options "-Xmx256m"
|
||||||
|
--java-options "-Dfile.encoding=\"utf-8\""
|
||||||
|
--java-options "-Djava.net.useSystemProxies=true"
|
||||||
|
--java-options "-Dapple.awt.enableTemplateImages=true"
|
||||||
|
--java-options "-Dsun.java2d.metal=true"
|
||||||
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
|
--java-options "-Dcryptomator.logDir=\"@{userhome}/Library/Logs/Cryptomator\""
|
||||||
|
--java-options "-Dcryptomator.pluginDir=\"@{userhome}/Library/Application Support/Cryptomator/Plugins\""
|
||||||
|
--java-options "-Dcryptomator.settingsPath=\"@{userhome}/Library/Application Support/Cryptomator/settings.json\""
|
||||||
|
--java-options "-Dcryptomator.p12Path=\"@{userhome}/Library/Application Support/Cryptomator/key.p12\""
|
||||||
|
--java-options "-Dcryptomator.ipcSocketPath=\"@{userhome}/Library/Application Support/Cryptomator/ipc.socket\""
|
||||||
|
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
|
||||||
|
--java-options "-Dcryptomator.mountPointsDir=\"@{userhome}/Library/Application Support/Cryptomator/mnt\""
|
||||||
|
--java-options "-Dcryptomator.showTrayIcon=true"
|
||||||
|
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ needs.get-version.outputs.revNum }}\""
|
||||||
|
--mac-package-identifier org.cryptomator
|
||||||
|
--resource-dir dist/mac/resources
|
||||||
|
- name: Patch Cryptomator.app
|
||||||
|
run: |
|
||||||
|
mv appdir/Cryptomator.app Cryptomator.app
|
||||||
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||||
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||||
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
|
env:
|
||||||
|
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
|
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||||
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
|
- name: Generate license for dmg
|
||||||
|
run: >
|
||||||
|
mvn -B -Djavafx.platform=mac license:add-third-party
|
||||||
|
-Dlicense.thirdPartyFilename=license.rtf
|
||||||
|
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||||
|
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||||
|
-Dlicense.includedScopes=compile
|
||||||
|
-Dlicense.excludedGroups=^org\.cryptomator
|
||||||
|
-Dlicense.failOnMissing=true
|
||||||
|
-Dlicense.licenseMergesUrl=file://${{ github.workspace }}/license/merges
|
||||||
|
- name: Install codesign certificate
|
||||||
|
run: |
|
||||||
|
# create variables
|
||||||
|
CERTIFICATE_PATH=$RUNNER_TEMP/codesign.p12
|
||||||
|
KEYCHAIN_PATH=$RUNNER_TEMP/codesign.keychain-db
|
||||||
|
|
||||||
|
# import certificate and provisioning profile from secrets
|
||||||
|
echo -n "$CODESIGN_P12_BASE64" | base64 --decode --output $CERTIFICATE_PATH
|
||||||
|
|
||||||
|
# create temporary keychain
|
||||||
|
security create-keychain -p "$CODESIGN_TMP_KEYCHAIN_PW" $KEYCHAIN_PATH
|
||||||
|
security set-keychain-settings -lut 900 $KEYCHAIN_PATH
|
||||||
|
security unlock-keychain -p "$CODESIGN_TMP_KEYCHAIN_PW" $KEYCHAIN_PATH
|
||||||
|
|
||||||
|
# import certificate to keychain
|
||||||
|
security import $CERTIFICATE_PATH -P "$CODESIGN_P12_PW" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||||
|
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||||
|
env:
|
||||||
|
CODESIGN_P12_BASE64: ${{ secrets.MACOS_CODESIGN_P12_BASE64 }}
|
||||||
|
CODESIGN_P12_PW: ${{ secrets.MACOS_CODESIGN_P12_PW }}
|
||||||
|
CODESIGN_TMP_KEYCHAIN_PW: ${{ secrets.MACOS_CODESIGN_TMP_KEYCHAIN_PW }}
|
||||||
|
- name: Codesign
|
||||||
|
run: |
|
||||||
|
echo "Codesigning jdk files..."
|
||||||
|
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ \( -name 'jspawnhelper' -o -name 'pauseengine' -o -name 'simengine' \) -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
echo "Codesigning jar contents..."
|
||||||
|
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
|
||||||
|
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
|
||||||
|
JAR_FILENAME=$(basename ${JAR_PATH})
|
||||||
|
OUTPUT_PATH=${JAR_PATH%.*}
|
||||||
|
echo "Codesigning libs in ${JAR_FILENAME}..."
|
||||||
|
unzip -q ${JAR_PATH} -d ${OUTPUT_PATH}
|
||||||
|
find ${OUTPUT_PATH} -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
find ${OUTPUT_PATH} -name '*.jnilib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
rm ${JAR_PATH}
|
||||||
|
pushd ${OUTPUT_PATH} > /dev/null
|
||||||
|
zip -qr ../${JAR_FILENAME} *
|
||||||
|
popd > /dev/null
|
||||||
|
rm -r ${OUTPUT_PATH}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Codesigning Cryptomator.app..."
|
||||||
|
sed -i '' "s|###APP_IDENTIFIER_PREFIX###|${TEAM_IDENTIFIER}.|g" dist/mac/Cryptomator.entitlements
|
||||||
|
sed -i '' "s|###TEAM_IDENTIFIER###|${TEAM_IDENTIFIER}|g" dist/mac/Cryptomator.entitlements
|
||||||
|
codesign --force --deep --entitlements dist/mac/Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
|
||||||
|
env:
|
||||||
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
|
TEAM_IDENTIFIER: ${{ secrets.MACOS_TEAM_IDENTIFIER }}
|
||||||
|
- name: Prepare .dmg contents
|
||||||
|
run: |
|
||||||
|
mkdir dmg
|
||||||
|
mv Cryptomator.app dmg
|
||||||
|
cp dist/mac/dmg/resources/${{ matrix.fuse-lib }}.webloc dmg
|
||||||
|
ls -l dmg
|
||||||
|
- name: Install create-dmg
|
||||||
|
run: |
|
||||||
|
brew install create-dmg
|
||||||
|
create-dmg --help
|
||||||
|
- name: Create .dmg
|
||||||
|
run: >
|
||||||
|
create-dmg
|
||||||
|
--volname Cryptomator
|
||||||
|
--volicon "dist/mac/dmg/resources/Cryptomator-Volume.icns"
|
||||||
|
--background "dist/mac/dmg/resources/Cryptomator-${{ matrix.fuse-lib }}-background.tiff"
|
||||||
|
--window-pos 400 100
|
||||||
|
--window-size 640 694
|
||||||
|
--icon-size 128
|
||||||
|
--icon "Cryptomator.app" 128 245
|
||||||
|
--hide-extension "Cryptomator.app"
|
||||||
|
--icon "${{ matrix.fuse-lib }}.webloc" 320 501
|
||||||
|
--hide-extension "${{ matrix.fuse-lib }}.webloc"
|
||||||
|
--app-drop-link 512 245
|
||||||
|
--eula "dist/mac/dmg/resources/license.rtf"
|
||||||
|
--icon ".background" 128 758
|
||||||
|
--icon ".VolumeIcon.icns" 512 758
|
||||||
|
Cryptomator-${VERSION_NO}-${{ matrix.output-suffix }}.dmg dmg
|
||||||
|
env:
|
||||||
|
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
|
- name: Notarize .dmg
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') || inputs.notarize
|
||||||
|
uses: cocoalibs/xcode-notarization-action@v1
|
||||||
|
with:
|
||||||
|
app-path: 'Cryptomator-*.dmg'
|
||||||
|
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||||
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
|
xcode-path: '/Applications/Xcode_16.app'
|
||||||
|
- name: Add possible alpha/beta tags to installer name
|
||||||
|
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
||||||
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
|
run: |
|
||||||
|
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-*.dmg
|
||||||
|
env:
|
||||||
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
|
- name: Clean up codesign certificate
|
||||||
|
if: ${{ always() }}
|
||||||
|
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
|
||||||
|
continue-on-error: true
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
|
path: |
|
||||||
|
Cryptomator-*.dmg
|
||||||
|
Cryptomator-*.asc
|
||||||
|
if-no-files-found: error
|
||||||
|
- name: Publish dmg on GitHub Releases
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
files: |
|
||||||
|
Cryptomator-*.dmg
|
||||||
|
Cryptomator-*.asc
|
||||||
41
.github/workflows/mac-dmg.yml
vendored
41
.github/workflows/mac-dmg.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Build macOS .dmg
|
name: Build macOS .dmg for arm64
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
@@ -16,7 +16,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: '21.0.2+13'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
get-version:
|
get-version:
|
||||||
@@ -32,20 +32,12 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: macos-11
|
- os: macos-15
|
||||||
architecture: x64
|
|
||||||
output-suffix: x64
|
|
||||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
|
||||||
fuse-lib: macFUSE
|
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-x64_bin-jmods.zip'
|
|
||||||
openjfx-sha: 'bd6abab20da73d5a968dcf2fd915d81b5fb919340e3bb84979ee9a888a829939'
|
|
||||||
- os: [self-hosted, macOS, ARM64]
|
|
||||||
architecture: aarch64
|
architecture: aarch64
|
||||||
output-suffix: arm64
|
output-suffix: arm64
|
||||||
xcode-path: '/Applications/Xcode_13.2.1.app'
|
|
||||||
fuse-lib: FUSE-T
|
fuse-lib: FUSE-T
|
||||||
openjfx-url: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-aarch64_bin-jmods.zip'
|
openjfx-url: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_osx-aarch64_bin-jmods.zip'
|
||||||
openjfx-sha: '7afaa1c57a6cc3c384d636e597b9a5364693e2db4aaec0a6e63d2fa964400b58'
|
openjfx-sha: '813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
@@ -79,7 +71,7 @@ jobs:
|
|||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pmac -DskipTests
|
run: mvn -B -Djavafx.platform=mac clean package -Pmac -DskipTests
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -91,7 +83,7 @@ jobs:
|
|||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
--module-path "${JAVA_HOME}/jmods:openjfx-jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
@@ -137,12 +129,14 @@ jobs:
|
|||||||
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
mv dist/mac/resources/Cryptomator-Vault.icns Cryptomator.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" Cryptomator.app/Contents/Info.plist
|
||||||
|
echo -n "$PROVISIONING_PROFILE_BASE64" | base64 --decode --output Cryptomator.app/Contents/embedded.provisionprofile
|
||||||
env:
|
env:
|
||||||
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
VERSION_NO: ${{ needs.get-version.outputs.semVerNum }}
|
||||||
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
REVISION_NO: ${{ needs.get-version.outputs.revNum }}
|
||||||
|
PROVISIONING_PROFILE_BASE64: ${{ secrets.MACOS_PROVISIONING_PROFILE_BASE64 }}
|
||||||
- name: Generate license for dmg
|
- name: Generate license for dmg
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B -Djavafx.platform=mac license:add-third-party
|
||||||
-Dlicense.thirdPartyFilename=license.rtf
|
-Dlicense.thirdPartyFilename=license.rtf
|
||||||
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
-Dlicense.outputDirectory=dist/mac/dmg/resources
|
||||||
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
-Dlicense.fileTemplate=dist/mac/dmg/resources/licenseTemplate.ftl
|
||||||
@@ -175,7 +169,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "Codesigning jdk files..."
|
echo "Codesigning jdk files..."
|
||||||
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ -name 'jspawnhelper' -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
|
find Cryptomator.app/Contents/runtime/Contents/Home/lib/ \( -name 'jspawnhelper' -o -name 'pauseengine' -o -name 'simengine' \) -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
|
||||||
echo "Codesigning jar contents..."
|
echo "Codesigning jar contents..."
|
||||||
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
find Cryptomator.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
|
for JAR_PATH in `find Cryptomator.app -name "*.jar"`; do
|
||||||
@@ -194,9 +188,12 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo "Codesigning Cryptomator.app..."
|
echo "Codesigning Cryptomator.app..."
|
||||||
|
sed -i '' "s|###APP_IDENTIFIER_PREFIX###|${TEAM_IDENTIFIER}.|g" dist/mac/Cryptomator.entitlements
|
||||||
|
sed -i '' "s|###TEAM_IDENTIFIER###|${TEAM_IDENTIFIER}|g" dist/mac/Cryptomator.entitlements
|
||||||
codesign --force --deep --entitlements dist/mac/Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
|
codesign --force --deep --entitlements dist/mac/Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
|
||||||
env:
|
env:
|
||||||
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
|
||||||
|
TEAM_IDENTIFIER: ${{ secrets.MACOS_TEAM_IDENTIFIER }}
|
||||||
- name: Prepare .dmg contents
|
- name: Prepare .dmg contents
|
||||||
run: |
|
run: |
|
||||||
mkdir dmg
|
mkdir dmg
|
||||||
@@ -235,7 +232,7 @@ jobs:
|
|||||||
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
apple-id: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
|
||||||
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
password: ${{ secrets.MACOS_NOTARIZATION_PW }}
|
||||||
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
team-id: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
|
||||||
xcode-path: ${{ matrix.xcode-path }}
|
xcode-path: '/Applications/Xcode_16.app'
|
||||||
- name: Add possible alpha/beta tags to installer name
|
- name: Add possible alpha/beta tags to installer name
|
||||||
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
run: mv Cryptomator-*.dmg Cryptomator-${{ needs.get-version.outputs.semVerStr }}-${{ matrix.output-suffix }}.dmg
|
||||||
- name: Create detached GPG signature with key 615D449FE6E6A235
|
- name: Create detached GPG signature with key 615D449FE6E6A235
|
||||||
@@ -253,11 +250,13 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dmg-${{ matrix.output-suffix }}
|
name: dmg-${{ matrix.output-suffix }}
|
||||||
path: Cryptomator-*.dmg
|
path: |
|
||||||
|
Cryptomator-*.dmg
|
||||||
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish dmg on GitHub Releases
|
- name: Publish dmg on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published'
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
2
.github/workflows/post-publish.yml
vendored
2
.github/workflows/post-publish.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
GPG_PRIVATE_KEY: ${{ secrets.RELEASES_GPG_PRIVATE_KEY }}
|
||||||
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
GPG_PASSPHRASE: ${{ secrets.RELEASES_GPG_PASSPHRASE }}
|
||||||
- name: Publish asc on GitHub Releases
|
- name: Publish asc on GitHub Releases
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
|||||||
4
.github/workflows/pullrequest.yml
vendored
4
.github/workflows/pullrequest.yml
vendored
@@ -5,7 +5,7 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: 21
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@@ -24,4 +24,4 @@ jobs:
|
|||||||
java-version: ${{ env.JAVA_VERSION }}
|
java-version: ${{ env.JAVA_VERSION }}
|
||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Build and Test
|
- name: Build and Test
|
||||||
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage
|
run: xvfb-run mvn -B clean install jacoco:report -Pcoverage -Djavafx.platform=linux
|
||||||
6
.github/workflows/release-check.yml
vendored
6
.github/workflows/release-check.yml
vendored
@@ -12,7 +12,7 @@ defaults:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: 21
|
JAVA_VERSION: 23
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-preconditions:
|
check-preconditions:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
|
- name: Validate release in org.cryptomator.Cryptomator.metainfo.xml file
|
||||||
run: |
|
run: |
|
||||||
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\"/>" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
if ! grep -q "<release date=\".*\" version=\"${{ steps.validate-pom-version.outputs.semVerStr }}\">" dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml; then
|
||||||
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
echo "Release not set in dist/linux/common/org.cryptomator.Cryptomator.metainfo.xml"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -60,6 +60,6 @@ jobs:
|
|||||||
- name: Run org.owasp:dependency-check plugin
|
- name: Run org.owasp:dependency-check plugin
|
||||||
id: dependency-check
|
id: dependency-check
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: mvn -B verify -Pdependency-check -DskipTests
|
run: mvn -B verify -Pdependency-check -DskipTests -Djavafx.platform=linux
|
||||||
env:
|
env:
|
||||||
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
|
||||||
110
.github/workflows/win-exe.yml
vendored
110
.github/workflows/win-exe.yml
vendored
@@ -16,9 +16,9 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
JAVA_DIST: 'zulu'
|
JAVA_DIST: 'zulu'
|
||||||
JAVA_VERSION: '21.0.2+13'
|
JAVA_VERSION: '23.0.1+11'
|
||||||
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_windows-x64_bin-jmods.zip'
|
OPENJFX_JMODS_AMD64: 'https://download2.gluonhq.com/openjfx/22.0.2/openjfx-22.0.2_windows-x64_bin-jmods.zip'
|
||||||
OPENJFX_JMODS_AMD64_HASH: 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
OPENJFX_JMODS_AMD64_HASH: 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
|
||||||
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
WINFSP_MSI: 'https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi'
|
||||||
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
WINFSP_UNINSTALLER: 'https://github.com/cryptomator/winfsp-uninstaller/releases/latest/download/winfsp-uninstaller.exe'
|
||||||
|
|
||||||
@@ -40,6 +40,9 @@ jobs:
|
|||||||
LOOPBACK_ALIAS: 'cryptomator-vault'
|
LOOPBACK_ALIAS: 'cryptomator-vault'
|
||||||
WIN_CONSOLE_FLAG: ''
|
WIN_CONSOLE_FLAG: ''
|
||||||
steps:
|
steps:
|
||||||
|
- name: Upgrade WIX to latest version
|
||||||
|
run: choco install wixtoolset --version 3.14.1
|
||||||
|
shell: pwsh
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
@@ -74,7 +77,7 @@ jobs:
|
|||||||
- name: Set version
|
- name: Set version
|
||||||
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
run : mvn versions:set -DnewVersion=${{ needs.get-version.outputs.semVerStr }}
|
||||||
- name: Run maven
|
- name: Run maven
|
||||||
run: mvn -B clean package -Pwin -DskipTests
|
run: mvn -B clean package -Pwin -DskipTests -Djavafx.platform=win
|
||||||
- name: Patch target dir
|
- name: Patch target dir
|
||||||
run: |
|
run: |
|
||||||
cp LICENSE.txt target
|
cp LICENSE.txt target
|
||||||
@@ -86,7 +89,7 @@ jobs:
|
|||||||
--verbose
|
--verbose
|
||||||
--output runtime
|
--output runtime
|
||||||
--module-path "jfxjmods;${JAVA_HOME}/jmods"
|
--module-path "jfxjmods;${JAVA_HOME}/jmods"
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler
|
||||||
--strip-native-commands
|
--strip-native-commands
|
||||||
--no-header-files
|
--no-header-files
|
||||||
--no-man-pages
|
--no-man-pages
|
||||||
@@ -110,7 +113,7 @@ jobs:
|
|||||||
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
--copyright "(C) 2016 - 2024 Skymatic GmbH"
|
||||||
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
--app-version "${{ needs.get-version.outputs.semVerNum }}.${{ needs.get-version.outputs.revNum }}"
|
||||||
--java-options "--enable-preview"
|
--java-options "--enable-preview"
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.win"
|
--java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win"
|
||||||
--java-options "-Xss5m"
|
--java-options "-Xss5m"
|
||||||
--java-options "-Xmx256m"
|
--java-options "-Xmx256m"
|
||||||
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
--java-options "-Dcryptomator.appVersion=\"${{ needs.get-version.outputs.semVerStr }}\""
|
||||||
@@ -127,6 +130,7 @@ jobs:
|
|||||||
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
--java-options "-Dcryptomator.buildNumber=\"msi-${{ needs.get-version.outputs.revNum }}\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
|
||||||
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
--java-options "-Dcryptomator.integrationsWin.keychainPaths=\"@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json\""
|
||||||
|
--java-options "-Dcryptomator.integrationsWin.windowsHelloKeychainPaths=\"@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json\""
|
||||||
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
--java-options "-Djavafx.verbose=${{ inputs.isDebug }}"
|
||||||
--resource-dir dist/win/resources
|
--resource-dir dist/win/resources
|
||||||
--icon dist/win/resources/Cryptomator.ico
|
--icon dist/win/resources/Cryptomator.ico
|
||||||
@@ -196,7 +200,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
- name: Generate license for MSI
|
- name: Generate license for MSI
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party "-Djavafx.platform=win"
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||||
"-Dlicense.outputDirectory=dist/win/resources"
|
"-Dlicense.outputDirectory=dist/win/resources"
|
||||||
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
|
||||||
@@ -254,15 +258,6 @@ jobs:
|
|||||||
Cryptomator-*.msi
|
Cryptomator-*.msi
|
||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
- name: Publish .msi on GitHub Releases
|
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published'
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
with:
|
|
||||||
fail_on_unmatched_files: true
|
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
|
||||||
files: |
|
|
||||||
*.msi
|
|
||||||
*.asc
|
|
||||||
|
|
||||||
build-exe:
|
build-exe:
|
||||||
name: Build .exe installer
|
name: Build .exe installer
|
||||||
@@ -285,7 +280,7 @@ jobs:
|
|||||||
cache: 'maven'
|
cache: 'maven'
|
||||||
- name: Generate license for exe
|
- name: Generate license for exe
|
||||||
run: >
|
run: >
|
||||||
mvn -B license:add-third-party
|
mvn -B license:add-third-party "-Djavafx.platform=win"
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf"
|
"-Dlicense.thirdPartyFilename=license.rtf"
|
||||||
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
|
||||||
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
"-Dlicense.outputDirectory=dist/win/bundle/resources"
|
||||||
@@ -365,59 +360,50 @@ jobs:
|
|||||||
Cryptomator-*.exe
|
Cryptomator-*.exe
|
||||||
Cryptomator-*.asc
|
Cryptomator-*.asc
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish installers to the github release
|
||||||
|
if: startsWith(github.ref, 'refs/tags/') && github.event.action == 'published'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build-msi, build-exe]
|
||||||
|
outputs:
|
||||||
|
download-url-msi: ${{ fromJSON(steps.publish.outputs.assets)[0].browser_download_url }}
|
||||||
|
download-url-exe: ${{ fromJSON(steps.publish.outputs.assets)[1].browser_download_url }}
|
||||||
|
steps:
|
||||||
|
- name: Download installers
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
merge-multiple: true
|
||||||
- name: Publish .msi on GitHub Releases
|
- name: Publish .msi on GitHub Releases
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published'
|
id: publish
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
fail_on_unmatched_files: true
|
fail_on_unmatched_files: true
|
||||||
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
|
||||||
|
# do not change ordering of filelist, required for correct job output
|
||||||
files: |
|
files: |
|
||||||
Cryptomator-*.exe
|
*.msi
|
||||||
Cryptomator-*.asc
|
*.exe
|
||||||
|
*.asc
|
||||||
|
|
||||||
|
allowlist-msi:
|
||||||
|
uses: ./.github/workflows/av-whitelist.yml
|
||||||
|
needs: [publish]
|
||||||
|
with:
|
||||||
|
url: ${{ needs.publish.outputs.download-url-msi }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
allowlist-exe:
|
||||||
|
uses: ./.github/workflows/av-whitelist.yml
|
||||||
|
needs: [publish]
|
||||||
|
with:
|
||||||
|
url: ${{ needs.publish.outputs.download-url-exe }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
allowlist:
|
|
||||||
name: Anti Virus Allowlisting
|
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build-msi, build-exe]
|
|
||||||
steps:
|
|
||||||
- name: Download .msi
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: msi
|
|
||||||
path: msi
|
|
||||||
- name: Download .exe
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
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@v4.3.4
|
|
||||||
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@v4.3.4
|
|
||||||
with:
|
|
||||||
protocol: ftp
|
|
||||||
server: whitelisting.avast.com
|
|
||||||
port: 21
|
|
||||||
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
|
|
||||||
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
|
|
||||||
local-dir: files/
|
|
||||||
notify-winget:
|
notify-winget:
|
||||||
name: Notify for winget-release
|
name: Notify for winget-release
|
||||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'published' && needs.get-version.outputs.versionType == 'stable'
|
if: needs.get-version.outputs.versionType == 'stable'
|
||||||
needs: [build-msi, get-version]
|
needs: [publish, get-version]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Slack Notification
|
- name: Slack Notification
|
||||||
|
|||||||
2
.github/workflows/winget.yml
vendored
2
.github/workflows/winget.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
GH_TOKEN: ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
|
||||||
- name: Submit package
|
- name: Submit package
|
||||||
uses: vedantmgoyal2009/winget-releaser@v2
|
uses: vedantmgoyal2009/winget-releaser@main
|
||||||
with:
|
with:
|
||||||
identifier: Cryptomator.Cryptomator
|
identifier: Cryptomator.Cryptomator
|
||||||
version: ${{ inputs.tag }}
|
version: ${{ inputs.tag }}
|
||||||
|
|||||||
31
.idea/compiler.xml
generated
31
.idea/compiler.xml
generated
@@ -14,31 +14,30 @@
|
|||||||
<option name="dagger.fastInit" value="enabled" />
|
<option name="dagger.fastInit" value="enabled" />
|
||||||
<option name="dagger.formatGeneratedSource" value="enabled" />
|
<option name="dagger.formatGeneratedSource" value="enabled" />
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.48.1/dagger-compiler-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.52/dagger-compiler-2.52.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.48.1/dagger-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.52/dagger-2.52.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/jakarta/inject/jakarta.inject-api/2.0.1/jakarta.inject-api-2.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.48.1/dagger-producers-2.48.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.52/dagger-spi-2.52.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.24-1.0.20/symbol-processing-api-1.9.24-1.0.20.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.24/kotlin-stdlib-1.9.24.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.48.1/dagger-spi-2.48.1.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.9.0-1.0.12/symbol-processing-api-1.9.0-1.0.12.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/33.0.0-jre/guava-33.0.0-jre.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/3.41.0/checker-qual-3.41.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.23.0/error_prone_annotations-2.23.0.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/2.8/j2objc-annotations-2.8.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.13.0/javapoet-1.13.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/kotlinpoet/1.11.0/kotlinpoet-1.11.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar" />
|
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
|
||||||
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
</profile>
|
</profile>
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -8,7 +8,7 @@
|
|||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21_PREVIEW" project-jdk-name="21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" project-jdk-name="23" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
2
.idea/runConfigurations/Cryptomator_Windows.xml
generated
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator/settings.json;@{userhome}/AppData/Roaming/Cryptomator/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator" -Dcryptomator.pluginDir="@{appdata}/Cryptomator/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator/keychain.json;@{userhome}/AppData/Roaming/Cryptomator/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator/key.p12;@{userhome}/AppData/Roaming/Cryptomator/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
|
||||||
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
|
||||||
<module name="cryptomator" />
|
<module name="cryptomator" />
|
||||||
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win" />
|
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath="@{appdata}/Cryptomator-Dev/settings.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/settings.json" -Dcryptomator.ipcSocketPath="@{localappdata}/Cryptomator-Dev/ipc.socket" -Dcryptomator.logDir="@{localappdata}/Cryptomator-Dev" -Dcryptomator.pluginDir="@{appdata}/Cryptomator-Dev/Plugins" -Dcryptomator.integrationsWin.keychainPaths="@{appdata}/Cryptomator-Dev/keychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/keychain.json" -Dcryptomator.integrationsWin.windowsHelloKeychainPaths="@{appdata}/Cryptomator-Dev/windowsHelloKeychain.json;@{userhome}/AppData/Roaming/Cryptomator-Dev/windowsHelloKeychain.json" -Dcryptomator.p12Path="@{appdata}/Cryptomator-Dev/key.p12;@{userhome}/AppData/Roaming/Cryptomator-Dev/key.p12" -Dcryptomator.mountPointsDir="@{userhome}/Cryptomator-Dev" -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m --enable-preview --enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" />
|
||||||
<method v="2">
|
<method v="2">
|
||||||
<option name="Make" enabled="true" />
|
<option name="Make" enabled="true" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -17,23 +17,15 @@ Cryptomator is provided free of charge as an open-source project despite the hig
|
|||||||
|
|
||||||
### Gold Sponsors
|
### Gold Sponsors
|
||||||
|
|
||||||
<table>
|
Become our Gold Sponsor and showcase your brand to a targeted audience! Please contact us if you are interested.
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="80"></a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### Silver Sponsors
|
### Silver Sponsors
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="https://mowcapital.com/"><img src="https://cryptomator.org/img/sponsors/mowcapital.svg" alt="Mow Capital" height="28"></a></td>
|
<td><a href="https://www.gee-whiz.de/"><img src="https://cryptomator.org/img/sponsors/geewhiz.svg" alt="gee-whiz" height="56"></a></td>
|
||||||
<td><a href="https://www.easeus.com/"><img src="https://cryptomator.org/img/sponsors/easeus.png" alt="EaseUS" height="40"></a></td>
|
<td><a href="https://www.route4me.com/"><img src="https://cryptomator.org/img/sponsors/route4me.svg" alt="Route4Me" height="56"></a></td>
|
||||||
<td><a href="https://www.hassmann-it-forensik.de/"><img src="https://cryptomator.org/img/sponsors/hassmannitforensik.png" alt="Hassmann IT-Forensik" height="40"></a></td>
|
|
||||||
<td><a href="https://ente.io/"><img src="https://cryptomator.org/img/sponsors/ente.svg" alt="Ente" height="58"></a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -86,7 +78,7 @@ For more information on the security details visit [cryptomator.org](https://doc
|
|||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* JDK 21 (e.g. temurin, zulu)
|
* JDK 22 (e.g. temurin, zulu)
|
||||||
* Maven 3
|
* Maven 3
|
||||||
|
|
||||||
### Run Maven
|
### Run Maven
|
||||||
|
|||||||
4
dist/linux/appimage/.gitignore
vendored
4
dist/linux/appimage/.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
# created during build
|
# downloaded/created during build
|
||||||
|
openjfx-jmods.zip
|
||||||
|
*.jmod
|
||||||
Cryptomator.AppDir
|
Cryptomator.AppDir
|
||||||
*.AppImage
|
*.AppImage
|
||||||
*.AppImage.zsync
|
*.AppImage.zsync
|
||||||
40
dist/linux/appimage/build.sh
vendored
40
dist/linux/appimage/build.sh
vendored
@@ -12,31 +12,31 @@ command -v unzip >/dev/null 2>&1 || { echo >&2 "unzip not found."; exit 1; }
|
|||||||
|
|
||||||
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
VERSION=$(mvn -f ../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
|
||||||
SEMVER_STR=${VERSION}
|
SEMVER_STR=${VERSION}
|
||||||
MACHINE_TYPE=$(uname -m)
|
CPU_ARCH=$(uname -p)
|
||||||
|
|
||||||
if [[ ! "${MACHINE_TYPE}" =~ x86_64|aarch64 ]]; then echo "Platform ${MACHINE_TYPE} not supported"; exit 1; fi
|
if [[ ! "${CPU_ARCH}" =~ x86_64|aarch64 ]]; then echo "Platform ${CPU_ARCH} not supported"; exit 1; fi
|
||||||
|
|
||||||
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
mvn -f ../../../pom.xml versions:set -DnewVersion=${SEMVER_STR}
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests
|
mvn -B -f ../../../pom.xml clean package -Plinux -DskipTests -Djavafx.platform=linux
|
||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
cp ../../../target/cryptomator-*.jar ../../../target/mods
|
||||||
|
|
||||||
|
JAVAFX_VERSION=22.0.2
|
||||||
# download javaFX jmods
|
JAVAFX_ARCH="x64"
|
||||||
OPENJFX_URL='https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-x64_bin-jmods.zip'
|
JAVAFX_JMODS_SHA256='d44bff3b94d5668fdee18a938d7b1269026d663d44765f02d29a9bdfd3fa1eb0'
|
||||||
OPENJFX_SHA='7baed11ca56d5fee85995fa6612d4299f1e8b7337287228f7f12fd50407c56f8'
|
if [ "${CPU_ARCH}" = "aarch64" ]; then
|
||||||
OPENJFX_URL_aarch64='https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_linux-aarch64_bin-jmods.zip'
|
JAVAFX_ARCH="aarch64"
|
||||||
OPENJFX_SHA_aarch64='871e7b9d7af16aef2e55c1b7830d0e0b2503b13dd8641374ba7e55ecb81d2ef9'
|
JAVAFX_JMODS_SHA256='3d5457136690c4f5bb9522d38b45218e045bdac13c24aa4c808c7c8d17d039c7'
|
||||||
|
|
||||||
if [[ "${MACHINE_TYPE}" = "aarch64" ]]; then
|
|
||||||
OPENJFX_URL="${OPENJFX_URL_aarch64}";
|
|
||||||
OPENJFX_SHA="${OPENJFX_SHA_aarch64}";
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl -L ${OPENJFX_URL} -o openjfx-jmods.zip
|
# download javaFX jmods
|
||||||
echo "${OPENJFX_SHA} openjfx-jmods.zip" | shasum -a256 --check
|
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_linux-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||||
|
|
||||||
|
|
||||||
|
curl -L ${JAVAFX_JMODS_URL} -o openjfx-jmods.zip
|
||||||
|
echo "${JAVAFX_JMODS_SHA256} openjfx-jmods.zip" | shasum -a256 --check
|
||||||
mkdir -p openjfx-jmods
|
mkdir -p openjfx-jmods
|
||||||
unzip -o -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
unzip -o -j openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe ./openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
@@ -56,7 +56,7 @@ ${JAVA_HOME}/bin/jlink \
|
|||||||
--verbose \
|
--verbose \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
@@ -114,17 +114,17 @@ ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.App
|
|||||||
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
|
||||||
|
|
||||||
# load AppImageTool
|
# load AppImageTool
|
||||||
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${MACHINE_TYPE}.AppImage -o /tmp/appimagetool.AppImage
|
curl -L https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${CPU_ARCH}.AppImage -o /tmp/appimagetool.AppImage
|
||||||
chmod +x /tmp/appimagetool.AppImage
|
chmod +x /tmp/appimagetool.AppImage
|
||||||
|
|
||||||
# create AppImage
|
# create AppImage
|
||||||
/tmp/appimagetool.AppImage \
|
/tmp/appimagetool.AppImage \
|
||||||
Cryptomator.AppDir \
|
Cryptomator.AppDir \
|
||||||
cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage \
|
cryptomator-${SEMVER_STR}-${CPU_ARCH}.AppImage \
|
||||||
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${MACHINE_TYPE}.AppImage.zsync'
|
-u 'gh-releases-zsync|cryptomator|cryptomator|latest|cryptomator-*-${CPU_ARCH}.AppImage.zsync'
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${MACHINE_TYPE}.AppImage"
|
echo "Done. AppImage successfully created: cryptomator-${SEMVER_STR}-${CPU_ARCH}.AppImage"
|
||||||
echo ""
|
echo ""
|
||||||
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir runtime squashfs-root openjfx-jmods; rm /tmp/appimagetool.AppImage openjfx-jmods.zip"
|
echo >&2 "To clean up, run: rm -rf Cryptomator.AppDir appdir runtime squashfs-root openjfx-jmods; rm /tmp/appimagetool.AppImage openjfx-jmods.zip"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -5,26 +5,27 @@
|
|||||||
<metadata_license>FSFAP</metadata_license>
|
<metadata_license>FSFAP</metadata_license>
|
||||||
<project_license>GPL-3.0-or-later</project_license>
|
<project_license>GPL-3.0-or-later</project_license>
|
||||||
<name>Cryptomator</name>
|
<name>Cryptomator</name>
|
||||||
<summary>Multi-platform client-side encryption tool optimized for cloud storages</summary>
|
<summary>Encryption made easy and optimized for the cloud</summary>
|
||||||
|
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
Cryptomator provides transparent, client-side encryption for your cloud. Protect your documents from unauthorized
|
Cryptomator provides easy-to-use, transparent, client-side encryption for your cloud.
|
||||||
access. Cryptomator is free and open source software, so you can rest assured there are no backdoors.
|
It protects your documents from unauthorized access and prying eyes, while you will still be able to view and edit your documents locally.
|
||||||
|
By not requiring any registration or account and performing all encryption locally, it gives you back control over your data and ensures your privacy.
|
||||||
|
Cryptomator is offered for all major platforms (including Android and iOS).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Cryptomator encrypts file contents and names using AES. Your passphrase is protected against bruteforcing attempts
|
Cryptomator encrypts file contents and names using the widespread industry standard AES.
|
||||||
using scrypt. Directory structures get obfuscated. The only thing which cannot be encrypted without breaking your
|
Your passphrase is protected against brute forcing attempts using scrypt.
|
||||||
cloud synchronization is the modification date of your files.
|
Additionally, directory structures get obfuscated.
|
||||||
|
For more info about the Cryptomator encryption scheme, check out the online documentation.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Cryptomator is a free and open source software licensed under the GPLv3. This allows anyone to check our code. It
|
Cryptomator is a free and open-source software licensed under the GPLv3.
|
||||||
is impossible to introduce backdoors for third parties. Also we cannot hide vulnerabilities. And the best thing
|
This allows anyone to check our code.
|
||||||
is: There is no need to trust us, as you can control us!
|
Thus, it is impossible to introduce backdoors for third parties or to hide vulnerabilities, so you do not need to trust Cryptomator.
|
||||||
</p>
|
Also, vendor lock-ins are impossible.
|
||||||
<p>
|
Even if we decided to stop development: The source code is already cloned by hundreds of other developers and development can be picked up by others.
|
||||||
Vendor lock-ins are impossible. Even if we decided to stop development: The source code is already cloned by
|
|
||||||
hundreds of other developers. As you don't need an account, you will never stand in front of locked doors.
|
|
||||||
</p>
|
</p>
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@
|
|||||||
</provides>
|
</provides>
|
||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot>
|
<screenshot type="default">
|
||||||
<caption>Light theme</caption>
|
<caption>Light theme</caption>
|
||||||
<image>https://user-images.githubusercontent.com/11858409/156986109-6e58f59c-8b8c-4501-b33b-bb1e33007cea.png</image>
|
<image>https://user-images.githubusercontent.com/11858409/156986109-6e58f59c-8b8c-4501-b33b-bb1e33007cea.png</image>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
@@ -52,20 +53,42 @@
|
|||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
|
<branding>
|
||||||
|
<color type="primary" scheme_preference="light">#EBF5EB</color>
|
||||||
|
<color type="primary" scheme_preference="dark">#2F4858</color>
|
||||||
|
</branding>
|
||||||
|
|
||||||
<url type="homepage">https://cryptomator.org/</url>
|
<url type="homepage">https://cryptomator.org/</url>
|
||||||
<url type="bugtracker">https://github.com/cryptomator/cryptomator/issues/</url>
|
<url type="bugtracker">https://github.com/cryptomator/cryptomator/issues/</url>
|
||||||
<url type="donation">https://cryptomator.org/donate</url>
|
<url type="donation">https://cryptomator.org/donate</url>
|
||||||
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
|
<url type="faq">https://community.cryptomator.org/c/kb/faq</url>
|
||||||
<url type="help">https://community.cryptomator.org/</url>
|
<url type="help">https://docs.cryptomator.org/</url>
|
||||||
<url type="translate">https://translate.cryptomator.org</url>
|
<url type="translate">https://translate.cryptomator.org</url>
|
||||||
|
|
||||||
<developer_name>Skymatic GmbH</developer_name>
|
<developer id="de.skymatic">
|
||||||
|
<name>Skymatic GmbH</name>
|
||||||
|
</developer>
|
||||||
|
|
||||||
<content_rating type="oars-1.1">
|
<content_rating type="oars-1.1">
|
||||||
<content_attribute id="social-info">mild</content_attribute> <!-- update checker connects to https://api.cryptomator.org/updates/latestVersion.json -->
|
<content_attribute id="social-info">mild</content_attribute> <!-- update checker connects to https://api.cryptomator.org/updates/latestVersion.json -->
|
||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
|
<release date="2024-11-19" version="1.14.2">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.2</url>
|
||||||
|
</release>
|
||||||
|
<release date="2024-09-17" version="1.14.0">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.14.0</url>
|
||||||
|
</release>
|
||||||
|
<release date="2024-06-26" version="1.13.0">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.13.0</url>
|
||||||
|
</release>
|
||||||
|
<release date="2024-03-27" version="1.12.4">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.4</url>
|
||||||
|
</release>
|
||||||
|
<release date="2024-02-27" version="1.12.3">
|
||||||
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.3</url>
|
||||||
|
</release>
|
||||||
<release date="2024-02-09" version="1.12.2">
|
<release date="2024-02-09" version="1.12.2">
|
||||||
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.2</url>
|
<url type="details">https://github.com/cryptomator/cryptomator/releases/1.12.2</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
2
dist/linux/debian/control
vendored
2
dist/linux/debian/control
vendored
@@ -2,7 +2,7 @@ Source: cryptomator
|
|||||||
Maintainer: Cryptobot <releases@cryptomator.org>
|
Maintainer: Cryptobot <releases@cryptomator.org>
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: debhelper (>=10), coffeelibs-jdk-21 (>= 21.0.2+12-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
|
Build-Depends: debhelper (>=10), coffeelibs-jdk-22 (>= 22.0.1+8-0ppa1), libgtk-3-0, libxxf86vm1, libgl1
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
Homepage: https://cryptomator.org
|
Homepage: https://cryptomator.org
|
||||||
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
Vcs-Git: https://github.com/cryptomator/cryptomator.git
|
||||||
|
|||||||
5
dist/linux/debian/rules
vendored
5
dist/linux/debian/rules
vendored
@@ -4,7 +4,7 @@
|
|||||||
# Uncomment this to turn on verbose mode.
|
# Uncomment this to turn on verbose mode.
|
||||||
#export DH_VERBOSE=1
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
JAVA_HOME = /usr/lib/jvm/java-21-coffeelibs
|
JAVA_HOME = /usr/lib/jvm/java-23-coffeelibs
|
||||||
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH)
|
||||||
ifeq ($(DEB_BUILD_ARCH),amd64)
|
ifeq ($(DEB_BUILD_ARCH),amd64)
|
||||||
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
JMODS_PATH = jmods/amd64:${JAVA_HOME}/jmods
|
||||||
@@ -28,7 +28,7 @@ override_dh_auto_build:
|
|||||||
$(JAVA_HOME)/bin/jlink \
|
$(JAVA_HOME)/bin/jlink \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JMODS_PATH}" \
|
--module-path "${JMODS_PATH}" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,jdk.net,java.compiler \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
@@ -61,6 +61,7 @@ override_dh_auto_build:
|
|||||||
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
--java-options "-Dcryptomator.buildNumber=\"deb-${REVISION_NUM}\"" \
|
||||||
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
--java-options "-Dcryptomator.appVersion=\"${SEMVER_STR}\"" \
|
||||||
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
--java-options "-Dcryptomator.disableUpdateCheck=\"${DISABLE_UPDATE_CHECK}\"" \
|
||||||
|
--java-options "-Dcryptomator.integrationsLinux.autoStartCmd=\"cryptomator\"" \
|
||||||
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
--app-version "${VERSION_NUM}.${REVISION_NUM}" \
|
||||||
--resource-dir resources \
|
--resource-dir resources \
|
||||||
--verbose
|
--verbose
|
||||||
|
|||||||
1
dist/mac/.gitignore
vendored
Normal file
1
dist/mac/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
embedded.provisionprofile
|
||||||
8
dist/mac/Cryptomator.entitlements
vendored
8
dist/mac/Cryptomator.entitlements
vendored
@@ -2,6 +2,10 @@
|
|||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>com.apple.application-identifier</key>
|
||||||
|
<string>###APP_IDENTIFIER_PREFIX###org.cryptomator</string>
|
||||||
|
<key>com.apple.developer.team-identifier</key>
|
||||||
|
<string>###TEAM_IDENTIFIER###</string>
|
||||||
<key>com.apple.security.cs.allow-jit</key>
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
@@ -10,5 +14,9 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.cs.disable-library-validation</key>
|
<key>com.apple.security.cs.disable-library-validation</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>###APP_IDENTIFIER_PREFIX###org.cryptomator</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
7
dist/mac/dmg/.gitignore
vendored
7
dist/mac/dmg/.gitignore
vendored
@@ -1,6 +1,9 @@
|
|||||||
# created during build
|
# downloaded/created during build
|
||||||
Cryptomator.app/
|
Cryptomator.app/
|
||||||
runtime/
|
runtime/
|
||||||
dmg/
|
dmg/
|
||||||
*.dmg
|
*.dmg
|
||||||
license.rtf
|
license.rtf
|
||||||
|
openjfx-jmods.zip
|
||||||
|
*.jmod
|
||||||
|
Cryptomator.entitlements
|
||||||
|
|||||||
40
dist/mac/dmg/build.sh
vendored
40
dist/mac/dmg/build.sh
vendored
@@ -1,12 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# parse options
|
# parse options
|
||||||
usage() { echo "Usage: $0 [-s <codesign-identity>]" 1>&2; exit 1; }
|
usage() { echo "Usage: $0 [-s <codesign-identity>] [-t <team-identifier>]" 1>&2; exit 1; }
|
||||||
while getopts ":s:" o; do
|
while getopts ":s:t:" o; do
|
||||||
case "${o}" in
|
case "${o}" in
|
||||||
s)
|
s)
|
||||||
CODESIGN_IDENTITY=${OPTARG}
|
CODESIGN_IDENTITY=${OPTARG}
|
||||||
;;
|
;;
|
||||||
|
t)
|
||||||
|
TEAM_IDENTIFIER=${OPTARG}
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
@@ -29,13 +32,17 @@ REVISION_NO=`git rev-list --count HEAD`
|
|||||||
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
VERSION_NO=`mvn -f../../../pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout | sed -rn 's/.*([0-9]+\.[0-9]+\.[0-9]+).*/\1/p'`
|
||||||
FUSE_LIB="FUSE-T"
|
FUSE_LIB="FUSE-T"
|
||||||
|
|
||||||
ARCH="undefined"
|
JAVAFX_VERSION=22.0.2
|
||||||
|
JAVAFX_ARCH="undefined"
|
||||||
|
JAVAFX_JMODS_SHA256="undefined"
|
||||||
if [ "$(machine)" = "arm64e" ]; then
|
if [ "$(machine)" = "arm64e" ]; then
|
||||||
ARCH="aarch64"
|
JAVAFX_ARCH="aarch64"
|
||||||
|
JAVAFX_JMODS_SHA256="813c6748f7c99cb7a579d48b48a087b4682b1fad1fc1a4fe5f9b21cf872b15a7"
|
||||||
else
|
else
|
||||||
ARCH="x64"
|
JAVAFX_ARCH="x64"
|
||||||
|
JAVAFX_JMODS_SHA256="115cb08bb59d880cfff6e51e0bf0dcc45785ed9d456b8b8425597b04da6ab3d4"
|
||||||
fi
|
fi
|
||||||
OPENJFX_JMODS="https://download2.gluonhq.com/openjfx/21.0.1/openjfx-21.0.1_osx-${ARCH}_bin-jmods.zip"
|
JAVAFX_JMODS_URL="https://download2.gluonhq.com/openjfx/${JAVAFX_VERSION}/openjfx-${JAVAFX_VERSION}_osx-${JAVAFX_ARCH}_bin-jmods.zip"
|
||||||
|
|
||||||
# check preconditions
|
# check preconditions
|
||||||
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
if [ -z "${JAVA_HOME}" ]; then echo "JAVA_HOME not set. Run using JAVA_HOME=/path/to/jdk ./build.sh"; exit 1; fi
|
||||||
@@ -47,7 +54,8 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# download and check jmods
|
# download and check jmods
|
||||||
curl -L ${OPENJFX_JMODS} -o openjfx-jmods.zip
|
curl -L ${JAVAFX_JMODS_URL} -o openjfx-jmods.zip
|
||||||
|
echo "${JAVAFX_JMODS_SHA256} openjfx-jmods.zip" | shasum -a256 --check
|
||||||
mkdir -p openjfx-jmods/
|
mkdir -p openjfx-jmods/
|
||||||
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
unzip -jo openjfx-jmods.zip \*/javafx.base.jmod \*/javafx.controls.jmod \*/javafx.fxml.jmod \*/javafx.graphics.jmod -d openjfx-jmods
|
||||||
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
JMOD_VERSION=$(jmod describe openjfx-jmods/javafx.base.jmod | head -1)
|
||||||
@@ -63,7 +71,7 @@ if [ "${POM_JFX_VERSION}" -ne "${JMOD_VERSION}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
|
mvn -B -Djavafx.platform=mac -f../../../pom.xml clean package -DskipTests -Pmac
|
||||||
cp ../../../LICENSE.txt ../../../target
|
cp ../../../LICENSE.txt ../../../target
|
||||||
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
||||||
|
|
||||||
@@ -71,7 +79,7 @@ cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
|
|||||||
${JAVA_HOME}/bin/jlink \
|
${JAVA_HOME}/bin/jlink \
|
||||||
--output runtime \
|
--output runtime \
|
||||||
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
--module-path "${JAVA_HOME}/jmods:openjfx-jmods" \
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.crypto.ec,jdk.security.auth,jdk.accessibility,jdk.management.jfr \
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,javafx.base,javafx.graphics,javafx.controls,javafx.fxml,jdk.unsupported,jdk.security.auth,jdk.accessibility,jdk.management.jfr,java.compiler \
|
||||||
--strip-native-commands \
|
--strip-native-commands \
|
||||||
--no-header-files \
|
--no-header-files \
|
||||||
--no-man-pages \
|
--no-man-pages \
|
||||||
@@ -116,9 +124,10 @@ ${JAVA_HOME}/bin/jpackage \
|
|||||||
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
cp ../resources/${APP_NAME}-Vault.icns ${APP_NAME}.app/Contents/Resources/
|
||||||
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_SHORT_VERSION_STRING###|${VERSION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||||
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
sed -i '' "s|###BUNDLE_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
|
||||||
|
cp ../embedded.provisionprofile ${APP_NAME}.app/Contents/
|
||||||
|
|
||||||
# generate license
|
# generate license
|
||||||
mvn -B -f../../../pom.xml license:add-third-party \
|
mvn -B -Djavafx.platform=mac -f../../../pom.xml license:add-third-party \
|
||||||
-Dlicense.thirdPartyFilename=license.rtf \
|
-Dlicense.thirdPartyFilename=license.rtf \
|
||||||
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
-Dlicense.outputDirectory=dist/mac/dmg/resources \
|
||||||
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
|
||||||
@@ -128,7 +137,11 @@ mvn -B -f../../../pom.xml license:add-third-party \
|
|||||||
-Dlicense.licenseMergesUrl=file://$(pwd)/../../../license/merges
|
-Dlicense.licenseMergesUrl=file://$(pwd)/../../../license/merges
|
||||||
|
|
||||||
# codesign
|
# codesign
|
||||||
if [ -n "${CODESIGN_IDENTITY}" ]; then
|
if [ -n "${CODESIGN_IDENTITY}" ] && [ -n "${TEAM_IDENTIFIER}" ]; then
|
||||||
|
echo "Codesigning jdk files..."
|
||||||
|
find ${APP_NAME}.app/Contents/runtime/Contents/Home/lib/ -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
find ${APP_NAME}.app/Contents/runtime/Contents/Home/lib/ -name 'jspawnhelper' -exec codesign --force -o runtime -s ${CODESIGN_IDENTITY} {} \;
|
||||||
|
echo "Codesigning jar contents..."
|
||||||
find ${APP_NAME}.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
find ${APP_NAME}.app/Contents/runtime/Contents/MacOS -name '*.dylib' -exec codesign --force -s ${CODESIGN_IDENTITY} {} \;
|
||||||
for JAR_PATH in `find ${APP_NAME}.app -name "*.jar"`; do
|
for JAR_PATH in `find ${APP_NAME}.app -name "*.jar"`; do
|
||||||
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
|
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
|
||||||
@@ -146,7 +159,10 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo "Codesigning ${APP_NAME}.app..."
|
echo "Codesigning ${APP_NAME}.app..."
|
||||||
codesign --force --deep --entitlements ../${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
|
cp ../${APP_NAME}.entitlements .
|
||||||
|
sed -i '' "s|###APP_IDENTIFIER_PREFIX###|${TEAM_IDENTIFIER}.|g" ${APP_NAME}.entitlements
|
||||||
|
sed -i '' "s|###TEAM_IDENTIFIER###|${TEAM_IDENTIFIER}|g" ${APP_NAME}.entitlements
|
||||||
|
codesign --force --deep --entitlements ${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# prepare dmg contents
|
# prepare dmg contents
|
||||||
|
|||||||
1
dist/win/.gitignore
vendored
1
dist/win/.gitignore
vendored
@@ -6,4 +6,5 @@ installer
|
|||||||
*.msi
|
*.msi
|
||||||
*.exe
|
*.exe
|
||||||
*.jmod
|
*.jmod
|
||||||
|
resources/jfxJmods.zip
|
||||||
license.rtf
|
license.rtf
|
||||||
33
dist/win/build.ps1
vendored
33
dist/win/build.ps1
vendored
@@ -41,7 +41,7 @@ Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
|
|||||||
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
|
||||||
|
|
||||||
# compile
|
# compile
|
||||||
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
|
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin "-Djavafx.platform=win"
|
||||||
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
|
||||||
|
|
||||||
# add runtime
|
# add runtime
|
||||||
@@ -51,21 +51,22 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
## download jfx jmods
|
## download jfx jmods
|
||||||
$jmodsVersion='21.0.1'
|
$javaFxVersion='22.0.2'
|
||||||
$jmodsUrl = "https://download2.gluonhq.com/openjfx/${jmodsVersion}/openjfx-${jmodsVersion}_windows-x64_bin-jmods.zip"
|
$javaFxJmodsUrl = "https://download2.gluonhq.com/openjfx/${javaFxVersion}/openjfx-${javaFxVersion}_windows-x64_bin-jmods.zip"
|
||||||
$jfxJmodsChecksum = 'daf8acae631c016c24cfe23f88469400274d3441dd890615a42dfb501f3eb94a'
|
$javaFxJmodsSHA256 = 'f9376d200f5c5b85327d575c1ec1482e6455f19916577f7e2fc9be2f48bb29b6'
|
||||||
$jfxJmodsZip = '.\resources\jfxJmods.zip'
|
$javaFxJmods = '.\resources\jfxJmods.zip'
|
||||||
if( !(Test-Path -Path $jfxJmodsZip) ) {
|
if( !(Test-Path -Path $javaFxJmods) ) {
|
||||||
Write-Output "Downloading ${jmodsUrl}..."
|
Write-Output "Downloading ${javaFxJmodsUrl}..."
|
||||||
Invoke-WebRequest $jmodsUrl -OutFile $jfxJmodsZip # redirects are followed by default
|
Invoke-WebRequest $javaFxJmodsUrl -OutFile $javaFxJmods # redirects are followed by default
|
||||||
}
|
}
|
||||||
|
|
||||||
$jmodsChecksumActual = $(Get-FileHash -Path $jfxJmodsZip -Algorithm SHA256).Hash
|
$jmodsChecksumActual = $(Get-FileHash -Path $javaFxJmods -Algorithm SHA256).Hash
|
||||||
if( $jmodsChecksumActual -ne $jfxJmodsChecksum ) {
|
if( $jmodsChecksumActual -ne $javaFxJmodsSHA256 ) {
|
||||||
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $jfxJmodsChecksum, actual: $jmodsChecksumActual"
|
Write-Error "Checksum mismatch for jfxJmods.zip. Expected: $javaFxJmodsSHA256
|
||||||
|
, actual: $jmodsChecksumActual"
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
Expand-Archive -Path $jfxJmodsZip -Force -DestinationPath ".\resources\"
|
Expand-Archive -Path $javaFxJmods -Force -DestinationPath ".\resources\"
|
||||||
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
|
Remove-Item -Recurse -Force -Path ".\resources\javafx-jmods"
|
||||||
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\javafx-jmods" -ErrorAction Stop
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ Move-Item -Force -Path ".\resources\javafx-jmods-*" -Destination ".\resources\ja
|
|||||||
--verbose `
|
--verbose `
|
||||||
--output runtime `
|
--output runtime `
|
||||||
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
|
--module-path "$Env:JAVA_HOME/jmods;$buildDir/resources/javafx-jmods" `
|
||||||
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
--add-modules java.base,java.desktop,java.instrument,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.accessibility,jdk.management.jfr,java.compiler,javafx.base,javafx.graphics,javafx.controls,javafx.fxml `
|
||||||
--strip-native-commands `
|
--strip-native-commands `
|
||||||
--no-header-files `
|
--no-header-files `
|
||||||
--no-man-pages `
|
--no-man-pages `
|
||||||
@@ -99,7 +100,7 @@ if ($clean -and (Test-Path -Path $appPath)) {
|
|||||||
--vendor $Vendor `
|
--vendor $Vendor `
|
||||||
--copyright $copyright `
|
--copyright $copyright `
|
||||||
--java-options "--enable-preview" `
|
--java-options "--enable-preview" `
|
||||||
--java-options "--enable-native-access=org.cryptomator.jfuse.win" `
|
--java-options "--enable-native-access=org.cryptomator.jfuse.win,org.cryptomator.integrations.win" `
|
||||||
--java-options "-Xss5m" `
|
--java-options "-Xss5m" `
|
||||||
--java-options "-Xmx256m" `
|
--java-options "-Xmx256m" `
|
||||||
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
|
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
|
||||||
@@ -121,7 +122,7 @@ if ($clean -and (Test-Path -Path $appPath)) {
|
|||||||
--icon resources/$AppName.ico
|
--icon resources/$AppName.ico
|
||||||
|
|
||||||
#Create RTF license for msi
|
#Create RTF license for msi
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||||
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
|
||||||
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
"-Dlicense.outputDirectory=$buildDir\resources\" `
|
||||||
@@ -166,7 +167,7 @@ $Env:JP_WIXHELPER_DIR = "."
|
|||||||
--file-associations resources/FAvaultFile.properties
|
--file-associations resources/FAvaultFile.properties
|
||||||
|
|
||||||
#Create RTF license for bundle
|
#Create RTF license for bundle
|
||||||
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
|
&mvn -B -f $buildDir/../../pom.xml license:add-third-party "-Djavafx.platform=win" `
|
||||||
"-Dlicense.thirdPartyFilename=license.rtf" `
|
"-Dlicense.thirdPartyFilename=license.rtf" `
|
||||||
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
|
||||||
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
@echo off
|
|
||||||
:: see comments in file ./version170-migrate-settings.ps1
|
|
||||||
|
|
||||||
cd %~dp0
|
|
||||||
powershell -NoLogo -NoProfile -NonInteractive -ExecutionPolicy RemoteSigned -Command .\version170-migrate-settings.ps1
|
|
||||||
35
dist/win/contrib/version170-migrate-settings.ps1
vendored
35
dist/win/contrib/version170-migrate-settings.ps1
vendored
@@ -1,35 +0,0 @@
|
|||||||
# This script migrates Cryptomator settings for all local users on Windows in case the users uses custom directories as mountpoint
|
|
||||||
# See also https://github.com/cryptomator/cryptomator/pull/2654.
|
|
||||||
#
|
|
||||||
# TODO: This script should be evaluated in a yearly interval if it is still needed and if not, should be removed
|
|
||||||
#
|
|
||||||
#Requires -RunAsAdministrator
|
|
||||||
|
|
||||||
#Get all active, local user profiles
|
|
||||||
$profileList = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'
|
|
||||||
Get-ChildItem $profileList | ForEach-Object {
|
|
||||||
$profilePath = $_.GetValue("ProfileImagePath")
|
|
||||||
$settingsPath = "$profilePath\AppData\Roaming\Cryptomator\settings.json"
|
|
||||||
if(!(Test-Path -Path $settingsPath -PathType Leaf)) {
|
|
||||||
#No settings file, nothing to do.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$settings = Get-Content -Path $settingsPath | ConvertFrom-Json
|
|
||||||
if($settings.preferredVolumeImpl -ne "FUSE") {
|
|
||||||
#Fuse not used, nothing to do
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#check if customMountPoints are used
|
|
||||||
$atLeastOneCustomPath = $false;
|
|
||||||
foreach ($vault in $settings.directories){
|
|
||||||
$atLeastOneCustomPath = $atLeastOneCustomPath -or ($vault.useCustomMountPath -eq "True")
|
|
||||||
}
|
|
||||||
|
|
||||||
#if so, use WinFsp Local Drive
|
|
||||||
if( $atLeastOneCustomPath ) {
|
|
||||||
Add-Member -Force -InputObject $settings -Name "mountService" -Value "org.cryptomator.frontend.fuse.mount.WinFspMountProvider" -MemberType NoteProperty
|
|
||||||
$newSettings = $settings | Select-Object * -ExcludeProperty "preferredVolumeImpl"
|
|
||||||
ConvertTo-Json $newSettings | Set-Content -Path $settingsPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6
dist/win/resources/main.wxs
vendored
6
dist/win/resources/main.wxs
vendored
@@ -139,11 +139,6 @@
|
|||||||
Sequence="execute" Before="PatchWebDAV" />
|
Sequence="execute" Before="PatchWebDAV" />
|
||||||
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
<CustomAction Id="PatchWebDAV" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
||||||
|
|
||||||
<!-- Special Settings migration for 1.7.0,. Should be removed eventually, for more info, see ../contrib/version170-migrate-settings.ps1-->
|
|
||||||
<SetProperty Id="V170MigrateSettings" Value=""[INSTALLDIR]version170-migrate-settings.bat""
|
|
||||||
Sequence="execute" Before="V170MigrateSettings" />
|
|
||||||
<CustomAction Id="V170MigrateSettings" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Return="ignore" Impersonate="no"/>
|
|
||||||
|
|
||||||
<!-- Running App detection and exit -->
|
<!-- Running App detection and exit -->
|
||||||
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
|
||||||
<util:CloseApplication
|
<util:CloseApplication
|
||||||
@@ -195,7 +190,6 @@
|
|||||||
<RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to WixCloseApplications -->
|
<RemoveExistingProducts After="InstallValidate"/> <!-- Moved from CostInitialize, due to WixCloseApplications -->
|
||||||
|
|
||||||
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
<Custom Action="PatchWebDAV" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
||||||
<Custom Action="V170MigrateSettings" After="InstallFiles">NOT Installed OR REINSTALL</Custom>
|
|
||||||
</InstallExecuteSequence>
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
<InstallUISequence>
|
<InstallUISequence>
|
||||||
|
|||||||
114
pom.xml
114
pom.xml
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptomator</artifactId>
|
<artifactId>cryptomator</artifactId>
|
||||||
<version>1.12.3</version>
|
<version>1.15.0-SNAPSHOT</version>
|
||||||
<name>Cryptomator Desktop App</name>
|
<name>Cryptomator Desktop App</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
@@ -26,56 +26,59 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.jdk.version>21</project.jdk.version>
|
<project.jdk.version>23</project.jdk.version>
|
||||||
|
|
||||||
<!-- Group IDs of jars that need to stay on the class path for now -->
|
<!-- Group IDs of jars that need to stay on the class path for now -->
|
||||||
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
<!-- remove them, as soon they got modularized or support is dropped (i.e., WebDAV) -->
|
||||||
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
<nonModularGroupIds>org.ow2.asm,org.apache.jackrabbit,org.apache.httpcomponents</nonModularGroupIds>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>2.6.9</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>2.7.1</cryptomator.cryptofs.version>
|
||||||
<cryptomator.integrations.version>1.3.0</cryptomator.integrations.version>
|
<cryptomator.integrations.version>1.4.0</cryptomator.integrations.version>
|
||||||
<cryptomator.integrations.win.version>1.2.5</cryptomator.integrations.win.version>
|
<cryptomator.integrations.win.version>1.4.1</cryptomator.integrations.win.version>
|
||||||
<cryptomator.integrations.mac.version>1.2.3</cryptomator.integrations.mac.version>
|
<cryptomator.integrations.mac.version>1.3.0</cryptomator.integrations.mac.version>
|
||||||
<cryptomator.integrations.linux.version>1.4.4</cryptomator.integrations.linux.version>
|
<cryptomator.integrations.linux.version>1.5.1</cryptomator.integrations.linux.version>
|
||||||
<cryptomator.fuse.version>4.0.0</cryptomator.fuse.version>
|
<cryptomator.fuse.version>5.0.2</cryptomator.fuse.version>
|
||||||
<cryptomator.dokany.version>2.0.0</cryptomator.dokany.version>
|
<cryptomator.webdav.version>2.0.7</cryptomator.webdav.version>
|
||||||
<cryptomator.webdav.version>2.0.6</cryptomator.webdav.version>
|
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
<commons-lang3.version>3.17.0</commons-lang3.version>
|
||||||
<dagger.version>2.50</dagger.version>
|
<dagger.version>2.52</dagger.version>
|
||||||
<easybind.version>2.2</easybind.version>
|
<easybind.version>2.2</easybind.version>
|
||||||
<guava.version>33.0.0-jre</guava.version>
|
<jackson.version>2.18.1</jackson.version>
|
||||||
<jackson.version>2.16.1</jackson.version>
|
<javafx.version>22.0.2</javafx.version>
|
||||||
<javafx.version>21.0.1</javafx.version>
|
|
||||||
<jwt.version>4.4.0</jwt.version>
|
<jwt.version>4.4.0</jwt.version>
|
||||||
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
<nimbus-jose.version>9.37.3</nimbus-jose.version>
|
||||||
<logback.version>1.5.0</logback.version>
|
<logback.version>1.5.12</logback.version>
|
||||||
<slf4j.version>2.0.12</slf4j.version>
|
<slf4j.version>2.0.16</slf4j.version>
|
||||||
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
<tinyoauth2.version>0.8.0</tinyoauth2.version>
|
||||||
<zxcvbn.version>1.8.2</zxcvbn.version>
|
<zxcvbn.version>1.9.0</zxcvbn.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.10.2</junit.jupiter.version>
|
<junit.jupiter.version>5.11.3</junit.jupiter.version>
|
||||||
<mockito.version>5.10.0</mockito.version>
|
<mockito.version>5.14.2</mockito.version>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>3.0</hamcrest.version>
|
||||||
|
|
||||||
<!-- build-time dependencies -->
|
<!-- build-time dependencies -->
|
||||||
<jetbrains.annotations.version>24.1.0</jetbrains.annotations.version>
|
<jetbrains.annotations.version>26.0.1</jetbrains.annotations.version>
|
||||||
<dependency-check.version>9.0.9</dependency-check.version>
|
<dependency-check.version>11.1.0</dependency-check.version>
|
||||||
<jacoco.version>0.8.11</jacoco.version>
|
<jacoco.version>0.8.12</jacoco.version>
|
||||||
<license-generator.version>2.4.0</license-generator.version>
|
<license-generator.version>2.4.0</license-generator.version>
|
||||||
<junit-tree-reporter.version>1.2.1</junit-tree-reporter.version>
|
<junit-tree-reporter.version>1.3.0</junit-tree-reporter.version>
|
||||||
<mvn-compiler.version>3.12.1</mvn-compiler.version>
|
<mvn-compiler.version>3.13.0</mvn-compiler.version>
|
||||||
<mvn-resources.version>3.3.1</mvn-resources.version>
|
<mvn-resources.version>3.3.1</mvn-resources.version>
|
||||||
<mvn-dependency.version>3.6.1</mvn-dependency.version>
|
<mvn-dependency.version>3.8.1</mvn-dependency.version>
|
||||||
<mvn-surefire.version>3.2.5</mvn-surefire.version>
|
<mvn-surefire.version>3.5.2</mvn-surefire.version>
|
||||||
<mvn-jar.version>3.3.0</mvn-jar.version>
|
<mvn-jar.version>3.4.2</mvn-jar.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Cryptomator Libs -->
|
<!-- Cryptomator Libs -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.cryptomator</groupId>
|
||||||
|
<artifactId>cryptolib</artifactId>
|
||||||
|
<version>2.2.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>cryptofs</artifactId>
|
<artifactId>cryptofs</artifactId>
|
||||||
@@ -86,11 +89,6 @@
|
|||||||
<artifactId>fuse-nio-adapter</artifactId>
|
<artifactId>fuse-nio-adapter</artifactId>
|
||||||
<version>${cryptomator.fuse.version}</version>
|
<version>${cryptomator.fuse.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.cryptomator</groupId>
|
|
||||||
<artifactId>dokany-nio-adapter</artifactId>
|
|
||||||
<version>${cryptomator.dokany.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>webdav-nio-adapter</artifactId>
|
<artifactId>webdav-nio-adapter</artifactId>
|
||||||
@@ -164,11 +162,18 @@
|
|||||||
<artifactId>nimbus-jose-jwt</artifactId>
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
<version>${nimbus-jose.version}</version>
|
<version>${nimbus-jose.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Jackson -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>${jackson.version}</version>
|
<version>${jackson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>${jackson.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- EasyBind -->
|
<!-- EasyBind -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -185,40 +190,23 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Google -->
|
<!-- Google -->
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>${guava.version}</version>
|
|
||||||
<exclusions>
|
|
||||||
<!-- see https://github.com/google/guava/wiki/UseGuavaInYourBuild#what-about-guavas-own-dependencies -->
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>listenablefuture</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.google.code.findbugs</groupId>
|
|
||||||
<artifactId>jsr305</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.checkerframework</groupId>
|
|
||||||
<artifactId>checker-qual</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.google.errorprone</groupId>
|
|
||||||
<artifactId>error_prone_annotations</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.google.j2objc</groupId>
|
|
||||||
<artifactId>j2objc-annotations</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.dagger</groupId>
|
<groupId>com.google.dagger</groupId>
|
||||||
<artifactId>dagger</artifactId>
|
<artifactId>dagger</artifactId>
|
||||||
<version>${dagger.version}</version>
|
<version>${dagger.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.inject</groupId>
|
||||||
|
<artifactId>jakarta.inject-api</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Caffeine -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>3.1.8</version>
|
||||||
|
</dependency>
|
||||||
<!-- JUnit / Mockito / Hamcrest -->
|
<!-- JUnit / Mockito / Hamcrest -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
@@ -465,7 +453,7 @@
|
|||||||
<skipTestScope>true</skipTestScope>
|
<skipTestScope>true</skipTestScope>
|
||||||
<detail>true</detail>
|
<detail>true</detail>
|
||||||
<suppressionFile>suppression.xml</suppressionFile>
|
<suppressionFile>suppression.xml</suppressionFile>
|
||||||
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
|
<nvdApiKeyEnvironmentVariable>NVD_API_KEY</nvdApiKeyEnvironmentVariable>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import ch.qos.logback.classic.spi.Configurator;
|
|||||||
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxLinuxLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.DropboxWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.GoogleDriveLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.GoogleDriveMacLocationPresetsProvider;
|
||||||
|
import org.cryptomator.common.locationpresets.GoogleDriveWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.ICloudMacLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.ICloudWindowsLocationPresetsProvider;
|
||||||
import org.cryptomator.common.locationpresets.LeitzcloudLocationPresetsProvider;
|
import org.cryptomator.common.locationpresets.LeitzcloudLocationPresetsProvider;
|
||||||
@@ -21,7 +22,6 @@ open module org.cryptomator.desktop {
|
|||||||
|
|
||||||
requires org.cryptomator.cryptolib;
|
requires org.cryptomator.cryptolib;
|
||||||
requires org.cryptomator.cryptofs;
|
requires org.cryptomator.cryptofs;
|
||||||
requires org.cryptomator.frontend.dokany;
|
|
||||||
requires org.cryptomator.frontend.fuse;
|
requires org.cryptomator.frontend.fuse;
|
||||||
requires org.cryptomator.frontend.webdav;
|
requires org.cryptomator.frontend.webdav;
|
||||||
requires org.cryptomator.integrations.api;
|
requires org.cryptomator.integrations.api;
|
||||||
@@ -32,13 +32,13 @@ open module org.cryptomator.desktop {
|
|||||||
requires javafx.graphics;
|
requires javafx.graphics;
|
||||||
requires javafx.controls;
|
requires javafx.controls;
|
||||||
requires javafx.fxml;
|
requires javafx.fxml;
|
||||||
requires jdk.crypto.ec;
|
|
||||||
// 3rd party:
|
// 3rd party:
|
||||||
requires ch.qos.logback.classic;
|
requires ch.qos.logback.classic;
|
||||||
requires ch.qos.logback.core;
|
requires ch.qos.logback.core;
|
||||||
requires com.auth0.jwt;
|
requires com.auth0.jwt;
|
||||||
requires com.google.common;
|
requires com.google.common;
|
||||||
requires com.fasterxml.jackson.databind;
|
requires com.fasterxml.jackson.databind;
|
||||||
|
requires com.fasterxml.jackson.datatype.jsr310;
|
||||||
requires com.nimbusds.jose.jwt;
|
requires com.nimbusds.jose.jwt;
|
||||||
requires com.nulabinc.zxcvbn;
|
requires com.nulabinc.zxcvbn;
|
||||||
requires com.tobiasdiez.easybind;
|
requires com.tobiasdiez.easybind;
|
||||||
@@ -47,8 +47,11 @@ open module org.cryptomator.desktop {
|
|||||||
requires org.slf4j;
|
requires org.slf4j;
|
||||||
requires org.apache.commons.lang3;
|
requires org.apache.commons.lang3;
|
||||||
|
|
||||||
/* TODO: filename-based modules: */
|
/* dagger bs */
|
||||||
requires static javax.inject; /* ugly dagger/guava crap */
|
requires jakarta.inject;
|
||||||
|
requires static javax.inject;
|
||||||
|
requires java.compiler;
|
||||||
|
requires com.github.benmanes.caffeine;
|
||||||
|
|
||||||
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
uses org.cryptomator.common.locationpresets.LocationPresetsProvider;
|
||||||
|
|
||||||
@@ -56,7 +59,7 @@ open module org.cryptomator.desktop {
|
|||||||
provides Configurator with LogbackConfiguratorFactory;
|
provides Configurator with LogbackConfiguratorFactory;
|
||||||
provides LocationPresetsProvider with //
|
provides LocationPresetsProvider with //
|
||||||
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
DropboxWindowsLocationPresetsProvider, DropboxMacLocationPresetsProvider, DropboxLinuxLocationPresetsProvider, //
|
||||||
GoogleDriveLocationPresetsProvider, //
|
GoogleDriveMacLocationPresetsProvider, GoogleDriveWindowsLocationPresetsProvider, //
|
||||||
ICloudWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
|
ICloudWindowsLocationPresetsProvider, ICloudMacLocationPresetsProvider, //
|
||||||
LeitzcloudLocationPresetsProvider, //
|
LeitzcloudLocationPresetsProvider, //
|
||||||
MegaLocationPresetsProvider, //
|
MegaLocationPresetsProvider, //
|
||||||
|
|||||||
20
src/main/java/org/cryptomator/JavaFXUtil.java
Normal file
20
src/main/java/org/cryptomator/JavaFXUtil.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package org.cryptomator;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class JavaFXUtil {
|
||||||
|
|
||||||
|
public static boolean startPlatform() throws InterruptedException {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
try {
|
||||||
|
Platform.startup(latch::countDown);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
//already initialized
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
return latch.await(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ public class Environment {
|
|||||||
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
private static final String SETTINGS_PATH_PROP_NAME = "cryptomator.settingsPath";
|
||||||
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
private static final String IPC_SOCKET_PATH_PROP_NAME = "cryptomator.ipcSocketPath";
|
||||||
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
private static final String KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.keychainPaths";
|
||||||
|
private static final String WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME = "cryptomator.integrationsWin.windowsHelloKeychainPaths";
|
||||||
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
private static final String P12_PATH_PROP_NAME = "cryptomator.p12Path";
|
||||||
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
private static final String LOG_DIR_PROP_NAME = "cryptomator.logDir";
|
||||||
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
private static final String LOOPBACK_ALIAS_PROP_NAME = "cryptomator.loopbackAlias";
|
||||||
@@ -45,6 +46,7 @@ public class Environment {
|
|||||||
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(SETTINGS_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(IPC_SOCKET_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
logCryptomatorSystemProperty(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
|
logCryptomatorSystemProperty(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
logCryptomatorSystemProperty(P12_PATH_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
logCryptomatorSystemProperty(LOG_DIR_PROP_NAME);
|
||||||
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
logCryptomatorSystemProperty(LOOPBACK_ALIAS_PROP_NAME);
|
||||||
@@ -85,6 +87,10 @@ public class Environment {
|
|||||||
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
return getPaths(KEYCHAIN_PATHS_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stream<Path> getWindowsHelloKeychainPath() {
|
||||||
|
return getPaths(WINDOWS_HELLO_KEYCHAIN_PATHS_PROP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
public Stream<Path> getP12Path() {
|
public Stream<Path> getP12Path() {
|
||||||
return getPaths(P12_PATH_PROP_NAME);
|
return getPaths(P12_PATH_PROP_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class SubstitutingProperties extends PropertiesDecorator {
|
public class SubstitutingProperties extends PropertiesDecorator {
|
||||||
@@ -58,7 +59,7 @@ public class SubstitutingProperties extends PropertiesDecorator {
|
|||||||
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
LoggerFactory.getLogger(SubstitutingProperties.class).warn("Variable {} used for substitution not found in {}. Replaced with empty string.", key, src);
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
return val.replace("\\", "\\\\");
|
return Matcher.quoteReplacement(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package org.cryptomator.common.keychain;
|
package org.cryptomator.common.keychain;
|
||||||
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import com.google.common.cache.CacheLoader;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.google.common.cache.LoadingCache;
|
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
|
||||||
@@ -24,9 +23,9 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
@Inject
|
@Inject
|
||||||
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
KeychainManager(ObjectExpression<KeychainAccessProvider> selectedKeychain) {
|
||||||
this.keychain = selectedKeychain;
|
this.keychain = selectedKeychain;
|
||||||
this.passphraseStoredProperties = CacheBuilder.newBuilder() //
|
this.passphraseStoredProperties = Caffeine.newBuilder() //
|
||||||
.weakValues() //
|
.softValues() //
|
||||||
.build(CacheLoader.from(this::createStoredPassphraseProperty));
|
.build(this::createStoredPassphraseProperty);
|
||||||
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
keychain.addListener(ignored -> passphraseStoredProperties.invalidateAll());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +44,12 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
|
||||||
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
|
storePassphrase(key, displayName, passphrase, true); //TODO: currently only TouchID is using this parameter, so this is okayish
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storePassphrase(String key, String displayName, CharSequence passphrase, boolean requireOsAuthentication) throws KeychainAccessException {
|
||||||
|
getKeychainOrFail().storePassphrase(key, displayName, passphrase, requireOsAuthentication);
|
||||||
setPassphraseStored(key, true);
|
setPassphraseStored(key, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,13 +106,11 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setPassphraseStored(String key, boolean value) {
|
private void setPassphraseStored(String key, boolean value) {
|
||||||
BooleanProperty property = passphraseStoredProperties.getIfPresent(key);
|
BooleanProperty property = passphraseStoredProperties.get(key, _ -> new SimpleBooleanProperty(value));
|
||||||
if (property != null) {
|
if (Platform.isFxApplicationThread()) {
|
||||||
if (Platform.isFxApplicationThread()) {
|
property.set(value);
|
||||||
property.set(value);
|
} else {
|
||||||
} else {
|
Platform.runLater(() -> property.set(value));
|
||||||
Platform.runLater(() -> property.set(value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
* @see #isPassphraseStored(String)
|
* @see #isPassphraseStored(String)
|
||||||
*/
|
*/
|
||||||
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
public ReadOnlyBooleanProperty getPassphraseStoredProperty(String key) {
|
||||||
return passphraseStoredProperties.getUnchecked(key);
|
return passphraseStoredProperties.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BooleanProperty createStoredPassphraseProperty(String key) {
|
private BooleanProperty createStoredPassphraseProperty(String key) {
|
||||||
@@ -135,4 +137,8 @@ public class KeychainManager implements KeychainAccessProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectExpression<KeychainAccessProvider> getKeychainImplementation() {
|
||||||
|
return this.keychain;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package org.cryptomator.common.locationpresets;
|
||||||
|
|
||||||
|
import org.cryptomator.integrations.common.CheckAvailability;
|
||||||
|
import org.cryptomator.integrations.common.OperatingSystem;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
||||||
|
|
||||||
|
@OperatingSystem(MAC)
|
||||||
|
@CheckAvailability
|
||||||
|
public final class GoogleDriveMacLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
|
private static final Path ROOT_LOCATION = LocationPresetsProvider.resolveLocation("~/Library/CloudStorage/").toAbsolutePath();
|
||||||
|
private static final Predicate<String> PATTERN = Pattern.compile("^GoogleDrive-[^/]+$").asMatchPredicate();
|
||||||
|
|
||||||
|
private static final List<Path> FALLBACK_LOCATIONS = List.of( //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/Google Drive/My Drive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/GoogleDrive"), //
|
||||||
|
LocationPresetsProvider.resolveLocation("~/Google Drive") //
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<LocationPreset> getLocations() {
|
||||||
|
List<LocationPreset> cloudStorageDirLocations = getCloudStorageDirLocations();
|
||||||
|
return cloudStorageDirLocations.isEmpty() ? getFallbackLocation().stream() : cloudStorageDirLocations.stream();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@CheckAvailability
|
||||||
|
public static boolean isPresent() {
|
||||||
|
return isRootLocationPresent() || FALLBACK_LOCATIONS.stream().anyMatch(Files::isDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a root location directory is present that matches the specified pattern.
|
||||||
|
* <p>
|
||||||
|
* This method scans the {@code ROOT_LOCATION} directory for subdirectories and tests each one against a pre-defined pattern ({@code PATTERN}).
|
||||||
|
*
|
||||||
|
* @return {@code true} if a matching root location is present, otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
public static boolean isRootLocationPresent() {
|
||||||
|
try (var dirStream = Files.list(ROOT_LOCATION)) {
|
||||||
|
return dirStream.anyMatch(path -> Files.isDirectory(path) && PATTERN.test(path.getFileName().toString()));
|
||||||
|
} catch (IOException | UncheckedIOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns Google Drive preset String.
|
||||||
|
*
|
||||||
|
* @param accountPath The path to the Google Drive account directory (e.g. {@code ~/Library/CloudStorage/GoogleDrive-username})
|
||||||
|
* @return {@code String}. For example: "Google Drive - username"
|
||||||
|
*/
|
||||||
|
private String getDriveLocationString(Path accountPath) {
|
||||||
|
String accountName = accountPath.getFileName().toString().replace("GoogleDrive-", "");
|
||||||
|
return "Google Drive - " + accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of cloud storage directory locations based on the {@code ROOT_LOCATION}.
|
||||||
|
* <p>
|
||||||
|
* This method lists all directories in the {@code ROOT_LOCATION}, filters them based on whether their names match
|
||||||
|
* a predefined pattern ({@code PATTERN}), and then extracts presets using {@code getPresetsFromAccountPath(Path)}.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @return a list of {@code LocationPreset} objects representing valid cloud storage directory locations.
|
||||||
|
*/
|
||||||
|
private List<LocationPreset> getCloudStorageDirLocations() {
|
||||||
|
try (var dirStream = Files.list(ROOT_LOCATION)) {
|
||||||
|
return dirStream.filter(path -> Files.isDirectory(path) && PATTERN.test(path.getFileName().toString()))
|
||||||
|
.flatMap(this::getPresetsFromAccountPath)
|
||||||
|
.toList();
|
||||||
|
} catch (IOException | UncheckedIOException e) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a stream of {@code LocationPreset} objects from a given Google Drive account path.
|
||||||
|
* <p>
|
||||||
|
* This method lists all directories within the provided {@code accountPath} and filters them
|
||||||
|
* to identify folders whose names match any of the translations defined in {@code MY_DRIVE_TRANSLATIONS}.
|
||||||
|
*
|
||||||
|
* @param accountPath the root path of the Google Drive account to scan.
|
||||||
|
* @return a stream of {@code LocationPreset} objects representing matching directories.
|
||||||
|
*/
|
||||||
|
private Stream<LocationPreset> getPresetsFromAccountPath(Path accountPath) {
|
||||||
|
try (var driveStream = Files.list(accountPath)) {
|
||||||
|
return driveStream
|
||||||
|
.filter(preset -> MY_DRIVE_TRANSLATIONS
|
||||||
|
.contains(preset.getFileName().toString()))
|
||||||
|
.map(drivePath -> new LocationPreset(
|
||||||
|
getDriveLocationString(accountPath),
|
||||||
|
drivePath
|
||||||
|
)).toList().stream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list containing a fallback location preset for Google Drive.
|
||||||
|
* <p>
|
||||||
|
* This method iterates through the predefined fallback locations, checks if any of them is a directory,
|
||||||
|
* and creates a {@code LocationPreset} object for the first matching directory found.
|
||||||
|
*
|
||||||
|
* @return a list containing a single fallback location preset if a valid directory is found, otherwise an empty list.
|
||||||
|
* @deprecated This method is intended for legacy support and may be removed in future releases.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
private List<LocationPreset> getFallbackLocation() {
|
||||||
|
return FALLBACK_LOCATIONS.stream()
|
||||||
|
.filter(Files::isDirectory)
|
||||||
|
.map(location -> new LocationPreset("Google Drive", location))
|
||||||
|
.findFirst()
|
||||||
|
.stream()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of translations for "My Drive" in various languages.
|
||||||
|
* <p>
|
||||||
|
* This constant is used to identify different language-specific labels for "My Drive" in Google Drive.
|
||||||
|
* <p>
|
||||||
|
* The translations were originally extracted from the Chromium project’s Chrome OS translation files.
|
||||||
|
* <p>
|
||||||
|
* Source: `ui/chromeos/translations` directory in the Chromium repository.
|
||||||
|
*/
|
||||||
|
private static final Set<String> MY_DRIVE_TRANSLATIONS = Set.of("My Drive", "የእኔ Drive", "ملفاتي", "মোৰ ড্ৰাইভ", "Diskim", "Мой Дыск", "Моят диск", "আমার ড্রাইভ", "Moj disk", "La meva unitat", "Můj disk", "Mit drev", "Meine Ablage", "Το Drive μου", "Mi unidad", "Minu ketas", "Nire unitatea", "Aking Drive", "Oma Drive", "Mon disque", "Mon Drive", "A miña unidade", "મારી ડ્રાઇવ", "मेरी ड्राइव", "Saját meghajtó", "Իմ դրայվը", "Drive Saya", "Drifið mitt", "I miei file", "האחסון שלי", "マイドライブ", "ჩემი Drive", "Менің Drive дискім", "ដ្រាយរបស់ខ្ញុំ", "ನನ್ನ ಡ್ರೈವ್", "내 드라이브", "Менин Drive'ым", "Mano Diskas", "Mans disks", "Мојот Drive", "എന്റെ ഡ്രൈവ്", "Миний Драйв", "माझा ड्राइव्ह", "मेरो ड्राइभ", "Mijn Drive", "Min disk", "ମୋ ଡ୍ରାଇଭ୍", "Mój dysk", "Meu Drive", "O meu disco", "Contul meu Drive", "Мой диск", "මගේ Drive", "Môj disk", "Disku im", "Мој диск", "Min enhet", "Hifadhi Yangu", "எனது இயக்ககம்", "నా డ్రైవ్", "ไดรฟ์ของฉัน", "Drive'ım", "Мій диск", "میری ڈرائیو", "Drive của tôi", "我的云端硬盘", "我的雲端硬碟", "IDrayivu yami");
|
||||||
|
}
|
||||||
@@ -9,13 +9,11 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.MAC;
|
|
||||||
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
import static org.cryptomator.integrations.common.OperatingSystem.Value.WINDOWS;
|
||||||
|
|
||||||
@OperatingSystem(WINDOWS)
|
@OperatingSystem(WINDOWS)
|
||||||
@OperatingSystem(MAC)
|
|
||||||
@CheckAvailability
|
@CheckAvailability
|
||||||
public final class GoogleDriveLocationPresetsProvider implements LocationPresetsProvider {
|
public final class GoogleDriveWindowsLocationPresetsProvider implements LocationPresetsProvider {
|
||||||
|
|
||||||
private static final List<Path> LOCATIONS = Arrays.asList( //
|
private static final List<Path> LOCATIONS = Arrays.asList( //
|
||||||
LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive"), //
|
LocationPresetsProvider.resolveLocation("~/GoogleDrive/My Drive"), //
|
||||||
@@ -37,5 +35,4 @@ public final class GoogleDriveLocationPresetsProvider implements LocationPresets
|
|||||||
.findFirst() //
|
.findFirst() //
|
||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -119,14 +119,8 @@ public class Mounter {
|
|||||||
Files.createDirectories(defaultMountPointBase);
|
Files.createDirectories(defaultMountPointBase);
|
||||||
builder.setMountpoint(defaultMountPointBase);
|
builder.setMountpoint(defaultMountPointBase);
|
||||||
} else if (canMountToDir) {
|
} else if (canMountToDir) {
|
||||||
var dirName = vaultSettings.mountName.get();
|
var mountPoint = defaultMountPointBase.resolve(vaultSettings.mountName.get());
|
||||||
//required for https://github.com/cryptomator/cryptomator/issues/3272
|
|
||||||
if(service.getClass().getCanonicalName().equals("org.cryptomator.frontend.fuse.mount.FuseTMountProvider")) {
|
|
||||||
dirName = vaultSettings.id;
|
|
||||||
}
|
|
||||||
var mountPoint = defaultMountPointBase.resolve(dirName);
|
|
||||||
Files.createDirectories(mountPoint);
|
Files.createDirectories(mountPoint);
|
||||||
cleanup = () -> removeCreatedDirectory(mountPoint);
|
|
||||||
builder.setMountpoint(mountPoint);
|
builder.setMountpoint(mountPoint);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -162,19 +156,11 @@ public class Mounter {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeCreatedDirectory(Path toDelete) {
|
|
||||||
try {
|
|
||||||
Files.delete(toDelete);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.warn("Unable to remove {} after unmount: {}.", toDelete, e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
public MountHandle mount(VaultSettings vaultSettings, Path cryptoFsRoot) throws IOException, MountFailedException {
|
||||||
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
|
var mountService = mountProviders.stream().filter(s -> s.getClass().getName().equals(vaultSettings.mountService.getValue())).findFirst().orElse(defaultMountService.getValue());
|
||||||
|
|
||||||
if (isConflictingMountService(mountService)) {
|
if (isConflictingMountService(mountService)) {
|
||||||
var msg = STR."\{mountService.getClass()} unavailable due to conflict with either of \{CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName())}";
|
var msg = mountService.getClass() + " unavailable due to conflict with either of " + CONFLICTING_MOUNT_SERVICES.get(mountService.getClass().getName());
|
||||||
throw new ConflictingMountServiceException(msg);
|
throw new ConflictingMountServiceException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,30 +25,33 @@ import javafx.beans.property.StringProperty;
|
|||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.geometry.NodeOrientation;
|
import javafx.geometry.NodeOrientation;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class Settings {
|
public class Settings {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Settings.class);
|
||||||
|
|
||||||
static final boolean DEFAULT_ASKED_FOR_UPDATE_CHECK = false;
|
|
||||||
static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
|
static final boolean DEFAULT_CHECK_FOR_UPDATES = false;
|
||||||
static final boolean DEFAULT_START_HIDDEN = false;
|
static final boolean DEFAULT_START_HIDDEN = false;
|
||||||
static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
|
static final boolean DEFAULT_AUTO_CLOSE_VAULTS = false;
|
||||||
static final boolean DEFAULT_USE_KEYCHAIN = true;
|
static final boolean DEFAULT_USE_KEYCHAIN = true;
|
||||||
|
static final boolean DEFAULT_USE_QUICKACCESS = true;
|
||||||
static final int DEFAULT_PORT = 42427;
|
static final int DEFAULT_PORT = 42427;
|
||||||
static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
|
||||||
static final boolean DEFAULT_DEBUG_MODE = false;
|
static final boolean DEFAULT_DEBUG_MODE = false;
|
||||||
static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
|
||||||
@Deprecated // to be changed to "whatever is available" eventually
|
@Deprecated // to be changed to "whatever is available" eventually
|
||||||
static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : "org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
|
static final String DEFAULT_KEYCHAIN_PROVIDER = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess" : //
|
||||||
|
SystemUtils.IS_OS_MAC ? "org.cryptomator.macos.keychain.MacSystemKeychainAccess" : //
|
||||||
|
"org.cryptomator.linux.keychain.SecretServiceKeychainAccess";
|
||||||
|
static final String DEFAULT_QUICKACCESS_SERVICE = SystemUtils.IS_OS_WINDOWS ? "org.cryptomator.windows.quickaccess.ExplorerQuickAccessService" : //
|
||||||
|
SystemUtils.IS_OS_LINUX ? "org.cryptomator.linux.quickaccess.NautilusBookmarks" : null;
|
||||||
|
|
||||||
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
|
static final String DEFAULT_USER_INTERFACE_ORIENTATION = NodeOrientation.LEFT_TO_RIGHT.name();
|
||||||
static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
|
public static final Instant DEFAULT_TIMESTAMP = Instant.parse("2000-01-01T00:00:00Z");
|
||||||
static final String DEFAULT_LAST_UPDATE_CHECK = "2000-01-01";
|
|
||||||
|
|
||||||
public final ObservableList<VaultSettings> directories;
|
public final ObservableList<VaultSettings> directories;
|
||||||
public final BooleanProperty askedForUpdateCheck;
|
|
||||||
public final BooleanProperty checkForUpdates;
|
|
||||||
public final BooleanProperty startHidden;
|
public final BooleanProperty startHidden;
|
||||||
public final BooleanProperty autoCloseVaults;
|
public final BooleanProperty autoCloseVaults;
|
||||||
public final BooleanProperty useKeychain;
|
public final BooleanProperty useKeychain;
|
||||||
@@ -57,17 +60,21 @@ public class Settings {
|
|||||||
public final BooleanProperty debugMode;
|
public final BooleanProperty debugMode;
|
||||||
public final ObjectProperty<UiTheme> theme;
|
public final ObjectProperty<UiTheme> theme;
|
||||||
public final StringProperty keychainProvider;
|
public final StringProperty keychainProvider;
|
||||||
|
public final BooleanProperty useQuickAccess;
|
||||||
|
public final StringProperty quickAccessService;
|
||||||
public final ObjectProperty<NodeOrientation> userInterfaceOrientation;
|
public final ObjectProperty<NodeOrientation> userInterfaceOrientation;
|
||||||
public final StringProperty licenseKey;
|
public final StringProperty licenseKey;
|
||||||
public final BooleanProperty showMinimizeButton;
|
|
||||||
public final BooleanProperty showTrayIcon;
|
public final BooleanProperty showTrayIcon;
|
||||||
|
public final BooleanProperty compactMode;
|
||||||
public final IntegerProperty windowXPosition;
|
public final IntegerProperty windowXPosition;
|
||||||
public final IntegerProperty windowYPosition;
|
public final IntegerProperty windowYPosition;
|
||||||
public final IntegerProperty windowWidth;
|
public final IntegerProperty windowWidth;
|
||||||
public final IntegerProperty windowHeight;
|
public final IntegerProperty windowHeight;
|
||||||
public final StringProperty language;
|
public final StringProperty language;
|
||||||
public final StringProperty mountService;
|
public final StringProperty mountService;
|
||||||
public final StringProperty lastUpdateCheck;
|
public final BooleanProperty checkForUpdates;
|
||||||
|
public final ObjectProperty<Instant> lastUpdateCheckReminder;
|
||||||
|
public final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||||
|
|
||||||
private Consumer<Settings> saveCmd;
|
private Consumer<Settings> saveCmd;
|
||||||
|
|
||||||
@@ -84,11 +91,10 @@ public class Settings {
|
|||||||
*/
|
*/
|
||||||
Settings(SettingsJson json) {
|
Settings(SettingsJson json) {
|
||||||
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
|
this.directories = FXCollections.observableArrayList(VaultSettings::observables);
|
||||||
this.askedForUpdateCheck = new SimpleBooleanProperty(this, "askedForUpdateCheck", json.askedForUpdateCheck);
|
|
||||||
this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
|
|
||||||
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
|
this.startHidden = new SimpleBooleanProperty(this, "startHidden", json.startHidden);
|
||||||
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
|
this.autoCloseVaults = new SimpleBooleanProperty(this, "autoCloseVaults", json.autoCloseVaults);
|
||||||
this.useKeychain = new SimpleBooleanProperty(this, "useKeychain", json.useKeychain);
|
this.useKeychain = new SimpleBooleanProperty(this, "useKeychain", json.useKeychain);
|
||||||
|
this.useQuickAccess = new SimpleBooleanProperty(this, "addToQuickAccess", json.useQuickAccess);
|
||||||
this.port = new SimpleIntegerProperty(this, "webDavPort", json.port);
|
this.port = new SimpleIntegerProperty(this, "webDavPort", json.port);
|
||||||
this.numTrayNotifications = new SimpleIntegerProperty(this, "numTrayNotifications", json.numTrayNotifications);
|
this.numTrayNotifications = new SimpleIntegerProperty(this, "numTrayNotifications", json.numTrayNotifications);
|
||||||
this.debugMode = new SimpleBooleanProperty(this, "debugMode", json.debugMode);
|
this.debugMode = new SimpleBooleanProperty(this, "debugMode", json.debugMode);
|
||||||
@@ -96,26 +102,28 @@ public class Settings {
|
|||||||
this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider);
|
this.keychainProvider = new SimpleStringProperty(this, "keychainProvider", json.keychainProvider);
|
||||||
this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT));
|
this.userInterfaceOrientation = new SimpleObjectProperty<>(this, "userInterfaceOrientation", parseEnum(json.uiOrientation, NodeOrientation.class, NodeOrientation.LEFT_TO_RIGHT));
|
||||||
this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey);
|
this.licenseKey = new SimpleStringProperty(this, "licenseKey", json.licenseKey);
|
||||||
this.showMinimizeButton = new SimpleBooleanProperty(this, "showMinimizeButton", json.showMinimizeButton);
|
|
||||||
this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon);
|
this.showTrayIcon = new SimpleBooleanProperty(this, "showTrayIcon", json.showTrayIcon);
|
||||||
|
this.compactMode = new SimpleBooleanProperty(this, "compactMode", json.compactMode);
|
||||||
this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition);
|
this.windowXPosition = new SimpleIntegerProperty(this, "windowXPosition", json.windowXPosition);
|
||||||
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
this.windowYPosition = new SimpleIntegerProperty(this, "windowYPosition", json.windowYPosition);
|
||||||
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
this.windowWidth = new SimpleIntegerProperty(this, "windowWidth", json.windowWidth);
|
||||||
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
this.windowHeight = new SimpleIntegerProperty(this, "windowHeight", json.windowHeight);
|
||||||
this.language = new SimpleStringProperty(this, "language", json.language);
|
this.language = new SimpleStringProperty(this, "language", json.language);
|
||||||
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
this.mountService = new SimpleStringProperty(this, "mountService", json.mountService);
|
||||||
this.lastUpdateCheck = new SimpleStringProperty(this, "lastUpdateCheck", json.lastUpdateCheck);
|
this.quickAccessService = new SimpleStringProperty(this, "quickAccessService", json.quickAccessService);
|
||||||
|
this.checkForUpdates = new SimpleBooleanProperty(this, "checkForUpdates", json.checkForUpdatesEnabled);
|
||||||
|
this.lastUpdateCheckReminder = new SimpleObjectProperty<>(this, "lastUpdateCheckReminder", json.lastReminderForUpdateCheck);
|
||||||
|
this.lastSuccessfulUpdateCheck = new SimpleObjectProperty<>(this, "lastSuccessfulUpdateCheck", json.lastSuccessfulUpdateCheck);
|
||||||
|
|
||||||
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
this.directories.addAll(json.directories.stream().map(VaultSettings::new).toList());
|
||||||
|
|
||||||
migrateLegacySettings(json);
|
migrateLegacySettings(json);
|
||||||
|
|
||||||
directories.addListener(this::somethingChanged);
|
directories.addListener(this::somethingChanged);
|
||||||
askedForUpdateCheck.addListener(this::somethingChanged);
|
|
||||||
checkForUpdates.addListener(this::somethingChanged);
|
|
||||||
startHidden.addListener(this::somethingChanged);
|
startHidden.addListener(this::somethingChanged);
|
||||||
autoCloseVaults.addListener(this::somethingChanged);
|
autoCloseVaults.addListener(this::somethingChanged);
|
||||||
useKeychain.addListener(this::somethingChanged);
|
useKeychain.addListener(this::somethingChanged);
|
||||||
|
useQuickAccess.addListener(this::somethingChanged);
|
||||||
port.addListener(this::somethingChanged);
|
port.addListener(this::somethingChanged);
|
||||||
numTrayNotifications.addListener(this::somethingChanged);
|
numTrayNotifications.addListener(this::somethingChanged);
|
||||||
debugMode.addListener(this::somethingChanged);
|
debugMode.addListener(this::somethingChanged);
|
||||||
@@ -123,15 +131,18 @@ public class Settings {
|
|||||||
keychainProvider.addListener(this::somethingChanged);
|
keychainProvider.addListener(this::somethingChanged);
|
||||||
userInterfaceOrientation.addListener(this::somethingChanged);
|
userInterfaceOrientation.addListener(this::somethingChanged);
|
||||||
licenseKey.addListener(this::somethingChanged);
|
licenseKey.addListener(this::somethingChanged);
|
||||||
showMinimizeButton.addListener(this::somethingChanged);
|
|
||||||
showTrayIcon.addListener(this::somethingChanged);
|
showTrayIcon.addListener(this::somethingChanged);
|
||||||
|
compactMode.addListener(this::somethingChanged);
|
||||||
windowXPosition.addListener(this::somethingChanged);
|
windowXPosition.addListener(this::somethingChanged);
|
||||||
windowYPosition.addListener(this::somethingChanged);
|
windowYPosition.addListener(this::somethingChanged);
|
||||||
windowWidth.addListener(this::somethingChanged);
|
windowWidth.addListener(this::somethingChanged);
|
||||||
windowHeight.addListener(this::somethingChanged);
|
windowHeight.addListener(this::somethingChanged);
|
||||||
language.addListener(this::somethingChanged);
|
language.addListener(this::somethingChanged);
|
||||||
mountService.addListener(this::somethingChanged);
|
mountService.addListener(this::somethingChanged);
|
||||||
lastUpdateCheck.addListener(this::somethingChanged);
|
quickAccessService.addListener(this::somethingChanged);
|
||||||
|
checkForUpdates.addListener(this::somethingChanged);
|
||||||
|
lastUpdateCheckReminder.addListener(this::somethingChanged);
|
||||||
|
lastSuccessfulUpdateCheck.addListener(this::somethingChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@@ -165,11 +176,10 @@ public class Settings {
|
|||||||
SettingsJson serialized() {
|
SettingsJson serialized() {
|
||||||
var json = new SettingsJson();
|
var json = new SettingsJson();
|
||||||
json.directories = directories.stream().map(VaultSettings::serialized).toList();
|
json.directories = directories.stream().map(VaultSettings::serialized).toList();
|
||||||
json.askedForUpdateCheck = askedForUpdateCheck.get();
|
|
||||||
json.checkForUpdatesEnabled = checkForUpdates.get();
|
|
||||||
json.startHidden = startHidden.get();
|
json.startHidden = startHidden.get();
|
||||||
json.autoCloseVaults = autoCloseVaults.get();
|
json.autoCloseVaults = autoCloseVaults.get();
|
||||||
json.useKeychain = useKeychain.get();
|
json.useKeychain = useKeychain.get();
|
||||||
|
json.useQuickAccess = useQuickAccess.get();
|
||||||
json.port = port.get();
|
json.port = port.get();
|
||||||
json.numTrayNotifications = numTrayNotifications.get();
|
json.numTrayNotifications = numTrayNotifications.get();
|
||||||
json.debugMode = debugMode.get();
|
json.debugMode = debugMode.get();
|
||||||
@@ -177,15 +187,18 @@ public class Settings {
|
|||||||
json.keychainProvider = keychainProvider.get();
|
json.keychainProvider = keychainProvider.get();
|
||||||
json.uiOrientation = userInterfaceOrientation.get().name();
|
json.uiOrientation = userInterfaceOrientation.get().name();
|
||||||
json.licenseKey = licenseKey.get();
|
json.licenseKey = licenseKey.get();
|
||||||
json.showMinimizeButton = showMinimizeButton.get();
|
|
||||||
json.showTrayIcon = showTrayIcon.get();
|
json.showTrayIcon = showTrayIcon.get();
|
||||||
|
json.compactMode = compactMode.get();
|
||||||
json.windowXPosition = windowXPosition.get();
|
json.windowXPosition = windowXPosition.get();
|
||||||
json.windowYPosition = windowYPosition.get();
|
json.windowYPosition = windowYPosition.get();
|
||||||
json.windowWidth = windowWidth.get();
|
json.windowWidth = windowWidth.get();
|
||||||
json.windowHeight = windowHeight.get();
|
json.windowHeight = windowHeight.get();
|
||||||
json.language = language.get();
|
json.language = language.get();
|
||||||
json.mountService = mountService.get();
|
json.mountService = mountService.get();
|
||||||
json.lastUpdateCheck = lastUpdateCheck.get();
|
json.quickAccessService = quickAccessService.get();
|
||||||
|
json.checkForUpdatesEnabled = checkForUpdates.get();
|
||||||
|
json.lastReminderForUpdateCheck = lastUpdateCheckReminder.get();
|
||||||
|
json.lastSuccessfulUpdateCheck = lastSuccessfulUpdateCheck.get();
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.cryptomator.common.settings;
|
package org.cryptomator.common.settings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
@@ -16,15 +18,9 @@ class SettingsJson {
|
|||||||
@JsonProperty("writtenByVersion")
|
@JsonProperty("writtenByVersion")
|
||||||
String writtenByVersion;
|
String writtenByVersion;
|
||||||
|
|
||||||
@JsonProperty("askedForUpdateCheck")
|
|
||||||
boolean askedForUpdateCheck = Settings.DEFAULT_ASKED_FOR_UPDATE_CHECK;
|
|
||||||
|
|
||||||
@JsonProperty("autoCloseVaults")
|
@JsonProperty("autoCloseVaults")
|
||||||
boolean autoCloseVaults = Settings.DEFAULT_AUTO_CLOSE_VAULTS;
|
boolean autoCloseVaults = Settings.DEFAULT_AUTO_CLOSE_VAULTS;
|
||||||
|
|
||||||
@JsonProperty("checkForUpdatesEnabled")
|
|
||||||
boolean checkForUpdatesEnabled = Settings.DEFAULT_CHECK_FOR_UPDATES;
|
|
||||||
|
|
||||||
@JsonProperty("debugMode")
|
@JsonProperty("debugMode")
|
||||||
boolean debugMode = Settings.DEFAULT_DEBUG_MODE;
|
boolean debugMode = Settings.DEFAULT_DEBUG_MODE;
|
||||||
|
|
||||||
@@ -49,12 +45,12 @@ class SettingsJson {
|
|||||||
@JsonProperty("port")
|
@JsonProperty("port")
|
||||||
int port = Settings.DEFAULT_PORT;
|
int port = Settings.DEFAULT_PORT;
|
||||||
|
|
||||||
@JsonProperty("showMinimizeButton")
|
|
||||||
boolean showMinimizeButton = Settings.DEFAULT_SHOW_MINIMIZE_BUTTON;
|
|
||||||
|
|
||||||
@JsonProperty("showTrayIcon")
|
@JsonProperty("showTrayIcon")
|
||||||
boolean showTrayIcon;
|
boolean showTrayIcon;
|
||||||
|
|
||||||
|
@JsonProperty("compactMode")
|
||||||
|
boolean compactMode;
|
||||||
|
|
||||||
@JsonProperty("startHidden")
|
@JsonProperty("startHidden")
|
||||||
boolean startHidden = Settings.DEFAULT_START_HIDDEN;
|
boolean startHidden = Settings.DEFAULT_START_HIDDEN;
|
||||||
|
|
||||||
@@ -80,7 +76,20 @@ class SettingsJson {
|
|||||||
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
@JsonProperty(value = "preferredVolumeImpl", access = JsonProperty.Access.WRITE_ONLY) // WRITE_ONLY means value is "written" into the java object during deserialization. Upvote this: https://github.com/FasterXML/jackson-annotations/issues/233
|
||||||
String preferredVolumeImpl;
|
String preferredVolumeImpl;
|
||||||
|
|
||||||
@JsonProperty("lastUpdateCheck")
|
@JsonProperty("checkForUpdatesEnabled")
|
||||||
String lastUpdateCheck = Settings.DEFAULT_LAST_UPDATE_CHECK;
|
boolean checkForUpdatesEnabled = Settings.DEFAULT_CHECK_FOR_UPDATES;
|
||||||
|
|
||||||
|
@JsonProperty("lastReminderForUpdateCheck")
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
|
||||||
|
Instant lastReminderForUpdateCheck = Settings.DEFAULT_TIMESTAMP;
|
||||||
|
|
||||||
|
@JsonProperty("lastSuccessfulUpdateCheck")
|
||||||
|
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC")
|
||||||
|
Instant lastSuccessfulUpdateCheck = Settings.DEFAULT_TIMESTAMP;
|
||||||
|
|
||||||
|
@JsonProperty("useQuickAccess")
|
||||||
|
boolean useQuickAccess = Settings.DEFAULT_USE_QUICKACCESS;
|
||||||
|
|
||||||
|
@JsonProperty("quickAccessService")
|
||||||
|
String quickAccessService = Settings.DEFAULT_QUICKACCESS_SERVICE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ package org.cryptomator.common.settings;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JacksonException;
|
import com.fasterxml.jackson.core.JacksonException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -36,7 +37,7 @@ import java.util.stream.Stream;
|
|||||||
@Singleton
|
@Singleton
|
||||||
public class SettingsProvider implements Supplier<Settings> {
|
public class SettingsProvider implements Supplier<Settings> {
|
||||||
|
|
||||||
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true);
|
private static final ObjectMapper JSON = new ObjectMapper().setDefaultLeniency(true).registerModule(new JavaTimeModule());
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
||||||
private static final long SAVE_DELAY_MS = 1000;
|
private static final long SAVE_DELAY_MS = 1000;
|
||||||
|
|
||||||
@@ -57,7 +58,10 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Settings load() {
|
private Settings load() {
|
||||||
Settings settings = env.getSettingsPath().flatMap(this::tryLoad).findFirst().orElseGet(() -> Settings.create(env));
|
Settings settings = env.getSettingsPath() //
|
||||||
|
.flatMap(this::tryLoad) //
|
||||||
|
.findFirst() //
|
||||||
|
.orElseGet(() -> Settings.create(env));
|
||||||
settings.setSaveCmd(this::scheduleSave);
|
settings.setSaveCmd(this::scheduleSave);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ package org.cryptomator.common.vaults;
|
|||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Constants;
|
import org.cryptomator.common.Constants;
|
||||||
import org.cryptomator.common.mount.Mounter;
|
import org.cryptomator.common.mount.Mounter;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
import org.cryptomator.cryptofs.CryptoFileSystemProperties;
|
||||||
@@ -23,6 +24,9 @@ import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
|
|||||||
import org.cryptomator.integrations.mount.MountFailedException;
|
import org.cryptomator.integrations.mount.MountFailedException;
|
||||||
import org.cryptomator.integrations.mount.Mountpoint;
|
import org.cryptomator.integrations.mount.Mountpoint;
|
||||||
import org.cryptomator.integrations.mount.UnmountFailedException;
|
import org.cryptomator.integrations.mount.UnmountFailedException;
|
||||||
|
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
||||||
|
import org.cryptomator.integrations.quickaccess.QuickAccessServiceException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -54,6 +58,7 @@ public class Vault {
|
|||||||
|
|
||||||
private final VaultSettings vaultSettings;
|
private final VaultSettings vaultSettings;
|
||||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
private final AtomicReference<CryptoFileSystem> cryptoFileSystem;
|
||||||
|
private final AtomicReference<QuickAccessService.QuickAccessEntry> quickAccessEntry;
|
||||||
private final VaultState state;
|
private final VaultState state;
|
||||||
private final ObjectProperty<Exception> lastKnownException;
|
private final ObjectProperty<Exception> lastKnownException;
|
||||||
private final VaultConfigCache configCache;
|
private final VaultConfigCache configCache;
|
||||||
@@ -67,6 +72,7 @@ public class Vault {
|
|||||||
private final BooleanBinding unknownError;
|
private final BooleanBinding unknownError;
|
||||||
private final ObjectBinding<Mountpoint> mountPoint;
|
private final ObjectBinding<Mountpoint> mountPoint;
|
||||||
private final Mounter mounter;
|
private final Mounter mounter;
|
||||||
|
private final Settings settings;
|
||||||
private final BooleanProperty showingStats;
|
private final BooleanProperty showingStats;
|
||||||
|
|
||||||
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
private final AtomicReference<Mounter.MountHandle> mountHandle = new AtomicReference<>(null);
|
||||||
@@ -78,7 +84,7 @@ public class Vault {
|
|||||||
VaultState state, //
|
VaultState state, //
|
||||||
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
@Named("lastKnownException") ObjectProperty<Exception> lastKnownException, //
|
||||||
VaultStats stats, //
|
VaultStats stats, //
|
||||||
Mounter mounter) {
|
Mounter mounter, Settings settings) {
|
||||||
this.vaultSettings = vaultSettings;
|
this.vaultSettings = vaultSettings;
|
||||||
this.configCache = configCache;
|
this.configCache = configCache;
|
||||||
this.cryptoFileSystem = cryptoFileSystem;
|
this.cryptoFileSystem = cryptoFileSystem;
|
||||||
@@ -94,7 +100,9 @@ public class Vault {
|
|||||||
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||||
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
||||||
this.mounter = mounter;
|
this.mounter = mounter;
|
||||||
|
this.settings = settings;
|
||||||
this.showingStats = new SimpleBooleanProperty(false);
|
this.showingStats = new SimpleBooleanProperty(false);
|
||||||
|
this.quickAccessEntry = new AtomicReference<>(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
@@ -154,6 +162,9 @@ public class Vault {
|
|||||||
var rootPath = fs.getRootDirectories().iterator().next();
|
var rootPath = fs.getRootDirectories().iterator().next();
|
||||||
var mountHandle = mounter.mount(vaultSettings, rootPath);
|
var mountHandle = mounter.mount(vaultSettings, rootPath);
|
||||||
success = this.mountHandle.compareAndSet(null, mountHandle);
|
success = this.mountHandle.compareAndSet(null, mountHandle);
|
||||||
|
if (settings.useQuickAccess.getValue()) {
|
||||||
|
addToQuickAccess();
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
destroyCryptoFileSystem();
|
destroyCryptoFileSystem();
|
||||||
@@ -178,6 +189,7 @@ public class Vault {
|
|||||||
mountHandle.mountObj().close();
|
mountHandle.mountObj().close();
|
||||||
mountHandle.specialCleanup().run();
|
mountHandle.specialCleanup().run();
|
||||||
} finally {
|
} finally {
|
||||||
|
removeFromQuickAccess();
|
||||||
destroyCryptoFileSystem();
|
destroyCryptoFileSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +197,52 @@ public class Vault {
|
|||||||
LOG.info("Locked vault '{}'", getDisplayName());
|
LOG.info("Locked vault '{}'", getDisplayName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private synchronized void addToQuickAccess() {
|
||||||
|
if (quickAccessEntry.get() != null) {
|
||||||
|
//we don't throw an exception since we don't wanna block unlocking
|
||||||
|
LOG.warn("Vault already added to quick access area. Will be removed on next lock operation.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QuickAccessService.get() //
|
||||||
|
.filter(s -> s.getClass().getName().equals(settings.quickAccessService.getValue())) //
|
||||||
|
.findFirst() //
|
||||||
|
.ifPresentOrElse( //
|
||||||
|
this::addToQuickAccessInternal, //
|
||||||
|
() -> LOG.warn("Unable to add Vault to quick access area: Desired implementation not available.") //
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToQuickAccessInternal(@NotNull QuickAccessService s) {
|
||||||
|
if (getMountPoint() instanceof Mountpoint.WithPath mp) {
|
||||||
|
try {
|
||||||
|
var entry = s.add(mp.path(), getDisplayName());
|
||||||
|
quickAccessEntry.set(entry);
|
||||||
|
} catch (QuickAccessServiceException e) {
|
||||||
|
LOG.error("Adding vault to quick access area failed", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG.warn("Unable to add vault to quick access area: Vault is not mounted to local system path.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void removeFromQuickAccess() {
|
||||||
|
if (quickAccessEntry.get() == null) {
|
||||||
|
LOG.debug("Removing vault from quick access area: Entry not found, nothing to do.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
removeFromQuickAccessInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFromQuickAccessInternal() {
|
||||||
|
try {
|
||||||
|
quickAccessEntry.get().remove();
|
||||||
|
quickAccessEntry.set(null);
|
||||||
|
} catch (QuickAccessServiceException e) {
|
||||||
|
LOG.error("Removing vault from quick access area failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ******************************************************************************
|
// ******************************************************************************
|
||||||
// Observable Properties
|
// Observable Properties
|
||||||
// *******************************************************************************
|
// *******************************************************************************
|
||||||
|
|||||||
@@ -8,11 +8,13 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common.vaults;
|
package org.cryptomator.common.vaults;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.VaultSettings;
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
import org.cryptomator.cryptofs.DirStructure;
|
import org.cryptomator.cryptofs.DirStructure;
|
||||||
import org.cryptomator.cryptofs.migration.Migrators;
|
import org.cryptomator.cryptofs.migration.Migrators;
|
||||||
|
import org.cryptomator.integrations.mount.MountService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -24,6 +26,7 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.NoSuchFileException;
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@@ -38,14 +41,21 @@ public class VaultListManager {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
|
||||||
|
|
||||||
private final AutoLocker autoLocker;
|
private final AutoLocker autoLocker;
|
||||||
|
private final List<MountService> mountServices;
|
||||||
private final VaultComponent.Factory vaultComponentFactory;
|
private final VaultComponent.Factory vaultComponentFactory;
|
||||||
private final ObservableList<Vault> vaultList;
|
private final ObservableList<Vault> vaultList;
|
||||||
private final String defaultVaultName;
|
private final String defaultVaultName;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Factory vaultComponentFactory, ResourceBundle resourceBundle, Settings settings) {
|
public VaultListManager(ObservableList<Vault> vaultList, //
|
||||||
|
AutoLocker autoLocker, //
|
||||||
|
List<MountService> mountServices,
|
||||||
|
VaultComponent.Factory vaultComponentFactory,
|
||||||
|
ResourceBundle resourceBundle,
|
||||||
|
Settings settings) {
|
||||||
this.vaultList = vaultList;
|
this.vaultList = vaultList;
|
||||||
this.autoLocker = autoLocker;
|
this.autoLocker = autoLocker;
|
||||||
|
this.mountServices = mountServices;
|
||||||
this.vaultComponentFactory = vaultComponentFactory;
|
this.vaultComponentFactory = vaultComponentFactory;
|
||||||
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
|
||||||
|
|
||||||
@@ -76,6 +86,15 @@ public class VaultListManager {
|
|||||||
} else {
|
} else {
|
||||||
vaultSettings.displayName.set(defaultVaultName);
|
vaultSettings.displayName.set(defaultVaultName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//due to https://github.com/cryptomator/cryptomator/issues/2880#issuecomment-1680313498
|
||||||
|
var nameOfWinfspLocalMounter = "org.cryptomator.frontend.fuse.mount.WinFspMountProvider";
|
||||||
|
if (SystemUtils.IS_OS_WINDOWS //
|
||||||
|
&& vaultSettings.path.get().toString().contains("Dropbox") //
|
||||||
|
&& mountServices.stream().anyMatch(s -> s.getClass().getName().equals(nameOfWinfspLocalMounter))) {
|
||||||
|
vaultSettings.mountService.setValue(nameOfWinfspLocalMounter);
|
||||||
|
}
|
||||||
|
|
||||||
return vaultSettings;
|
return vaultSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class SupportedLanguages {
|
|||||||
// "en" is not part of this list - it is always inserted at the top.
|
// "en" is not part of this list - it is always inserted at the top.
|
||||||
public static final List<String> LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", //
|
public static final List<String> LANGUAGE_TAGS = List.of("ar", "be", "bn", "bs", "ca", "cs", "da", "de", "el", "es", "fr", "gl", "he", //
|
||||||
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", //
|
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "nb", "nl", "nn", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", "sr-Latn", "sv", "sw", //
|
||||||
"ta", "th", "tr", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
"ta", "th", "tr", "ug", "uk", "vi", "zh", "zh-HK", "zh-TW");
|
||||||
public static final String ENGLISH = "en";
|
public static final String ENGLISH = "en";
|
||||||
|
|
||||||
private final List<String> sortedLanguageTags;
|
private final List<String> sortedLanguageTags;
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ public enum FxmlFile {
|
|||||||
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
|
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
|
||||||
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
RECOVERYKEY_RESET_PASSWORD_SUCCESS("/fxml/recoverykey_reset_password_success.fxml"), //
|
||||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
|
||||||
SHARE_VAULT("/fxml/share_vault.fxml"), //
|
SHARE_VAULT("/fxml/share_vault.fxml"), //
|
||||||
|
SIMPLE_DIALOG("/fxml/simple_dialog.fxml"), //
|
||||||
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
UPDATE_REMINDER("/fxml/update_reminder.fxml"), //
|
||||||
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
UNLOCK_ENTER_PASSWORD("/fxml/unlock_enter_password.fxml"),
|
||||||
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
|
UNLOCK_REQUIRES_RESTART("/fxml/unlock_requires_restart.fxml"), //
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public enum FontAwesome5Icon {
|
|||||||
COGS("\uF085"), //
|
COGS("\uF085"), //
|
||||||
COPY("\uF0C5"), //
|
COPY("\uF0C5"), //
|
||||||
CROWN("\uF521"), //
|
CROWN("\uF521"), //
|
||||||
|
DONATE("\uF4B9"), //
|
||||||
EDIT("\uF044"), //
|
EDIT("\uF044"), //
|
||||||
EXCHANGE_ALT("\uF362"), //
|
EXCHANGE_ALT("\uF362"), //
|
||||||
EXCLAMATION("\uF12A"), //
|
EXCLAMATION("\uF12A"), //
|
||||||
@@ -49,6 +50,7 @@ public enum FontAwesome5Icon {
|
|||||||
SEARCH("\uF002"), //
|
SEARCH("\uF002"), //
|
||||||
SHARE("\uF064"), //
|
SHARE("\uF064"), //
|
||||||
SPINNER("\uF110"), //
|
SPINNER("\uF110"), //
|
||||||
|
SPONSORS("\uF2B5"), //
|
||||||
STETHOSCOPE("\uF0f1"), //
|
STETHOSCOPE("\uF0f1"), //
|
||||||
SYNC("\uF021"), //
|
SYNC("\uF021"), //
|
||||||
TIMES("\uF00D"), //
|
TIMES("\uF00D"), //
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package org.cryptomator.ui.controls;
|
||||||
|
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
public class NotificationBar extends HBox {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label notificationLabel;
|
||||||
|
|
||||||
|
private final BooleanProperty dismissable = new SimpleBooleanProperty();
|
||||||
|
private final BooleanProperty notify = new SimpleBooleanProperty();
|
||||||
|
|
||||||
|
|
||||||
|
public NotificationBar() {
|
||||||
|
setAlignment(Pos.CENTER);
|
||||||
|
setStyle("-fx-alignment: center;");
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
spacer.setMinWidth(40);
|
||||||
|
|
||||||
|
Region leftRegion = new Region();
|
||||||
|
HBox.setHgrow(leftRegion, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
|
||||||
|
Region rightRegion = new Region();
|
||||||
|
HBox.setHgrow(rightRegion, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
|
||||||
|
VBox vbox = new VBox();
|
||||||
|
vbox.setAlignment(Pos.CENTER);
|
||||||
|
HBox.setHgrow(vbox, javafx.scene.layout.Priority.ALWAYS);
|
||||||
|
|
||||||
|
notificationLabel = new Label();
|
||||||
|
notificationLabel.getStyleClass().add("notification-label");
|
||||||
|
notificationLabel.setStyle("-fx-alignment: center;");
|
||||||
|
vbox.getChildren().add(notificationLabel);
|
||||||
|
|
||||||
|
Button closeButton = new Button("X");
|
||||||
|
closeButton.setMinWidth(40);
|
||||||
|
closeButton.setStyle("-fx-background-color: transparent; -fx-text-fill: white; -fx-font-weight: bold;");
|
||||||
|
closeButton.visibleProperty().bind(dismissable);
|
||||||
|
|
||||||
|
closeButton.setOnAction(_ -> {
|
||||||
|
visibleProperty().unbind();
|
||||||
|
managedProperty().unbind();
|
||||||
|
visibleProperty().set(false);
|
||||||
|
managedProperty().set(false);
|
||||||
|
});
|
||||||
|
closeButton.visibleProperty().bind(dismissable);
|
||||||
|
|
||||||
|
getChildren().addAll(spacer, leftRegion, vbox, rightRegion, closeButton);
|
||||||
|
|
||||||
|
visibleProperty().bind(notifyProperty());
|
||||||
|
managedProperty().bind(notifyProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return notificationLabel.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
notificationLabel.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStyleClass(String styleClass) {
|
||||||
|
getStyleClass().clear();
|
||||||
|
getStyleClass().add(styleClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDismissable() {
|
||||||
|
return dismissable.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDismissable(boolean value) {
|
||||||
|
dismissable.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getNotify() {
|
||||||
|
return notify.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotify(boolean value) {
|
||||||
|
notify.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty notifyProperty() {
|
||||||
|
return notify;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
75
src/main/java/org/cryptomator/ui/dialogs/Dialogs.java
Normal file
75
src/main/java/org/cryptomator/ui/dialogs/Dialogs.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package org.cryptomator.ui.dialogs;
|
||||||
|
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class Dialogs {
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public Dialogs(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Dialogs.class);
|
||||||
|
|
||||||
|
private SimpleDialog.Builder createDialogBuilder() {
|
||||||
|
return new SimpleDialog.Builder(resourceBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareRemoveVaultDialog(Stage window, Vault vault, ObservableList<Vault> vaults) {
|
||||||
|
return createDialogBuilder().setOwner(window) //
|
||||||
|
.setTitleKey("removeVault.title", vault.getDisplayName()) //
|
||||||
|
.setMessageKey("removeVault.message") //
|
||||||
|
.setDescriptionKey("removeVault.description") //
|
||||||
|
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||||
|
.setOkButtonKey("removeVault.confirmBtn") //
|
||||||
|
.setCancelButtonKey("generic.button.cancel") //
|
||||||
|
.setOkAction(stage -> {
|
||||||
|
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
||||||
|
vaults.remove(vault);
|
||||||
|
stage.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareRemoveCertDialog(Stage window, Settings settings) {
|
||||||
|
return createDialogBuilder() //
|
||||||
|
.setOwner(window) //
|
||||||
|
.setTitleKey("removeCert.title") //
|
||||||
|
.setMessageKey("removeCert.message") //
|
||||||
|
.setDescriptionKey("removeCert.description") //
|
||||||
|
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||||
|
.setOkButtonKey("removeCert.confirmBtn") //
|
||||||
|
.setCancelButtonKey("generic.button.cancel") //
|
||||||
|
.setOkAction(stage -> {
|
||||||
|
settings.licenseKey.set(null);
|
||||||
|
stage.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleDialog.Builder prepareDokanySupportEndDialog(Stage window, Consumer<Stage> cancelAction) {
|
||||||
|
return createDialogBuilder() //
|
||||||
|
.setOwner(window) //
|
||||||
|
.setTitleKey("dokanySupportEnd.title") //
|
||||||
|
.setMessageKey("dokanySupportEnd.message") //
|
||||||
|
.setDescriptionKey("dokanySupportEnd.description") //
|
||||||
|
.setIcon(FontAwesome5Icon.QUESTION) //
|
||||||
|
.setOkButtonKey("generic.button.close") //
|
||||||
|
.setCancelButtonKey("dokanySupportEnd.preferencesBtn") //
|
||||||
|
.setOkAction(Stage::close) //
|
||||||
|
.setCancelAction(cancelAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
138
src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java
Normal file
138
src/main/java/org/cryptomator/ui/dialogs/SimpleDialog.java
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package org.cryptomator.ui.dialogs;
|
||||||
|
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
||||||
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.IllegalFormatException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SimpleDialog {
|
||||||
|
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
private final Stage dialogStage;
|
||||||
|
|
||||||
|
SimpleDialog(Builder builder) throws IOException {
|
||||||
|
this.resourceBundle = builder.resourceBundle;
|
||||||
|
dialogStage = new Stage();
|
||||||
|
dialogStage.initOwner(builder.owner);
|
||||||
|
dialogStage.initModality(Modality.WINDOW_MODAL);
|
||||||
|
dialogStage.setTitle(resolveText(builder.titleKey, builder.titleArgs));
|
||||||
|
dialogStage.setResizable(false);
|
||||||
|
|
||||||
|
FxmlLoaderFactory loaderFactory = FxmlLoaderFactory.forController(
|
||||||
|
new SimpleDialogController(resolveText(builder.messageKey, null),
|
||||||
|
resolveText(builder.descriptionKey, null),
|
||||||
|
builder.icon,resolveText(builder.okButtonKey, null),
|
||||||
|
resolveText(builder.cancelButtonKey, null),
|
||||||
|
() -> builder.okAction.accept(dialogStage),
|
||||||
|
() -> builder.cancelAction.accept(dialogStage)),
|
||||||
|
Scene::new, builder.resourceBundle);
|
||||||
|
|
||||||
|
dialogStage.setScene(new Scene(loaderFactory.load(FxmlFile.SIMPLE_DIALOG.getRessourcePathString()).getRoot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAndWait() {
|
||||||
|
dialogStage.showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveText(String key, String[] args) {
|
||||||
|
if (key == null || key.isEmpty() || !resourceBundle.containsKey(key)) {
|
||||||
|
throw new IllegalArgumentException(String.format("Invalid key: '%s'. Key not found in ResourceBundle.", key));
|
||||||
|
}
|
||||||
|
String text = resourceBundle.getString(key);
|
||||||
|
try {
|
||||||
|
return args != null && args.length > 0 ? String.format(text, (Object[]) args) : text;
|
||||||
|
} catch (IllegalFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Formatting error: Check if arguments match placeholders in the text.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private Stage owner;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
|
private String titleKey;
|
||||||
|
private String[] titleArgs;
|
||||||
|
private String messageKey;
|
||||||
|
private String descriptionKey;
|
||||||
|
private String okButtonKey;
|
||||||
|
private String cancelButtonKey;
|
||||||
|
|
||||||
|
private FontAwesome5Icon icon;
|
||||||
|
private Consumer<Stage> okAction = Stage::close;
|
||||||
|
private Consumer<Stage> cancelAction = Stage::close;
|
||||||
|
|
||||||
|
public Builder(ResourceBundle resourceBundle) {
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setOwner(Stage owner) {
|
||||||
|
this.owner = owner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setTitleKey(String titleKey, String... args) {
|
||||||
|
this.titleKey = titleKey;
|
||||||
|
this.titleArgs = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMessageKey(String messageKey) {
|
||||||
|
this.messageKey = messageKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setDescriptionKey(String descriptionKey) {
|
||||||
|
this.descriptionKey = descriptionKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setIcon(FontAwesome5Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setOkButtonKey(String okButtonKey) {
|
||||||
|
this.okButtonKey = okButtonKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCancelButtonKey(String cancelButtonKey) {
|
||||||
|
this.cancelButtonKey = cancelButtonKey;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setOkAction(Consumer<Stage> okAction) {
|
||||||
|
this.okAction = okAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setCancelAction(Consumer<Stage> cancelAction) {
|
||||||
|
this.cancelAction = cancelAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleDialog build() {
|
||||||
|
Objects.requireNonNull(titleKey,"SimpleDialog titleKey must be set.");
|
||||||
|
Objects.requireNonNull(messageKey,"SimpleDialog messageKey must be set.");
|
||||||
|
Objects.requireNonNull(descriptionKey,"SimpleDialog descriptionKey must be set.");
|
||||||
|
Objects.requireNonNull(okButtonKey,"SimpleDialog okButtonKey must be set.");
|
||||||
|
Objects.requireNonNull(cancelButtonKey,"SimpleDialog cancelButtonKey must be set.");
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new SimpleDialog(this);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new UncheckedIOException("Failed to create SimpleDialog.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package org.cryptomator.ui.dialogs;
|
||||||
|
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.controls.FontAwesome5Icon;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
|
||||||
|
public class SimpleDialogController implements FxController {
|
||||||
|
|
||||||
|
private final String message;
|
||||||
|
private final String description;
|
||||||
|
private final FontAwesome5Icon icon;
|
||||||
|
private final String okButtonText;
|
||||||
|
private final String cancelButtonText;
|
||||||
|
private final Runnable okAction;
|
||||||
|
private final Runnable cancelAction;
|
||||||
|
|
||||||
|
public SimpleDialogController(String message, String description, FontAwesome5Icon icon, String okButtonText, String cancelButtonText, Runnable okAction, Runnable cancelAction) {
|
||||||
|
this.message = message;
|
||||||
|
this.description = description;
|
||||||
|
this.icon = icon;
|
||||||
|
this.okButtonText = okButtonText;
|
||||||
|
this.cancelButtonText = cancelButtonText;
|
||||||
|
this.okAction = okAction;
|
||||||
|
this.cancelAction = cancelAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FontAwesome5Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOkButtonText() {
|
||||||
|
return okButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCancelButtonText() {
|
||||||
|
return cancelButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleOk() {
|
||||||
|
if (okAction != null) {
|
||||||
|
okAction.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void handleCancel() {
|
||||||
|
if (cancelAction != null) {
|
||||||
|
cancelAction.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,8 @@ public interface ErrorComponent {
|
|||||||
default Stage show() {
|
default Stage show() {
|
||||||
Stage stage = window();
|
Stage stage = window();
|
||||||
stage.setScene(scene());
|
stage.setScene(scene());
|
||||||
|
stage.setMinWidth(420);
|
||||||
|
stage.setMinHeight(300);
|
||||||
stage.show();
|
stage.show();
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ public class ErrorController implements FxController {
|
|||||||
private final BooleanExpression errorSolutionFound = matchingErrorDiscussion.isNotNull();
|
private final BooleanExpression errorSolutionFound = matchingErrorDiscussion.isNotNull();
|
||||||
private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty();
|
private final BooleanProperty isLoadingHttpResponse = new SimpleBooleanProperty();
|
||||||
private final BooleanProperty askedForLookupDatabasePermission = new SimpleBooleanProperty();
|
private final BooleanProperty askedForLookupDatabasePermission = new SimpleBooleanProperty();
|
||||||
|
private final boolean formerSceneWasResizable;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) {
|
ErrorController(Application application, @Named("stackTrace") String stackTrace, ErrorCode errorCode, @Nullable Scene previousScene, Stage window, Environment environment, ExecutorService executorService) {
|
||||||
@@ -85,12 +86,14 @@ public class ErrorController implements FxController {
|
|||||||
this.window = window;
|
this.window = window;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
|
this.formerSceneWasResizable = window.isResizable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void back() {
|
public void back() {
|
||||||
if (previousScene != null) {
|
if (previousScene != null) {
|
||||||
window.setScene(previousScene);
|
window.setScene(previousScene);
|
||||||
|
window.setResizable(formerSceneWasResizable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.cryptomator.ui.fxapp;
|
|||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.settings.VaultSettings;
|
||||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -10,6 +11,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
@@ -71,11 +74,35 @@ public class FxApplication {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!environment.disableUpdateCheck()) {
|
var time14DaysAgo = Instant.now().minus(Duration.ofDays(14));
|
||||||
appWindows.checkAndShowUpdateReminderWindow();
|
if (!environment.disableUpdateCheck() //
|
||||||
|
&& !settings.checkForUpdates.getValue() //
|
||||||
|
&& settings.lastSuccessfulUpdateCheck.get().isBefore(time14DaysAgo) //
|
||||||
|
&& settings.lastUpdateCheckReminder.get().isBefore(time14DaysAgo)) {
|
||||||
|
appWindows.showUpdateReminderWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
migrateAndInformDokanyRemoval();
|
||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents();
|
launchEventHandler.startHandlingLaunchEvents();
|
||||||
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
autoUnlocker.tryUnlockForTimespan(2, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void migrateAndInformDokanyRemoval() {
|
||||||
|
var dokanyProviderId = "org.cryptomator.frontend.dokany.mount.DokanyMountProvider";
|
||||||
|
boolean dokanyFound = false;
|
||||||
|
if (settings.mountService.getValueSafe().equals(dokanyProviderId)) {
|
||||||
|
dokanyFound = true;
|
||||||
|
settings.mountService.set(null);
|
||||||
|
}
|
||||||
|
for (VaultSettings vaultSettings : settings.directories) {
|
||||||
|
if (vaultSettings.mountService.getValueSafe().equals(dokanyProviderId)) {
|
||||||
|
dokanyFound = true;
|
||||||
|
vaultSettings.mountService.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dokanyFound) {
|
||||||
|
appWindows.showDokanySupportEndWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import dagger.Lazy;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
import org.cryptomator.ui.lock.LockComponent;
|
import org.cryptomator.ui.lock.LockComponent;
|
||||||
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
import org.cryptomator.ui.mainwindow.MainWindowComponent;
|
||||||
@@ -47,40 +48,43 @@ public class FxApplicationWindows {
|
|||||||
private final Lazy<PreferencesComponent> preferencesWindow;
|
private final Lazy<PreferencesComponent> preferencesWindow;
|
||||||
private final QuitComponent.Builder quitWindowBuilder;
|
private final QuitComponent.Builder quitWindowBuilder;
|
||||||
private final UnlockComponent.Factory unlockWorkflowFactory;
|
private final UnlockComponent.Factory unlockWorkflowFactory;
|
||||||
private final UpdateReminderComponent.Factory updateReminderWindowBuilder;
|
private final UpdateReminderComponent.Factory updateReminderWindowFactory;
|
||||||
private final LockComponent.Factory lockWorkflowFactory;
|
private final LockComponent.Factory lockWorkflowFactory;
|
||||||
private final ErrorComponent.Factory errorWindowFactory;
|
private final ErrorComponent.Factory errorWindowFactory;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final ShareVaultComponent.Factory shareVaultWindow;
|
private final ShareVaultComponent.Factory shareVaultWindow;
|
||||||
private final FilteredList<Window> visibleWindows;
|
private final FilteredList<Window> visibleWindows;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FxApplicationWindows(@PrimaryStage Stage primaryStage,
|
public FxApplicationWindows(@PrimaryStage Stage primaryStage, //
|
||||||
Optional<TrayIntegrationProvider> trayIntegration, //
|
Optional<TrayIntegrationProvider> trayIntegration, //
|
||||||
Lazy<MainWindowComponent> mainWindow, //
|
Lazy<MainWindowComponent> mainWindow, //
|
||||||
Lazy<PreferencesComponent> preferencesWindow, //
|
Lazy<PreferencesComponent> preferencesWindow, //
|
||||||
QuitComponent.Builder quitWindowBuilder, //
|
QuitComponent.Builder quitWindowBuilder, //
|
||||||
UnlockComponent.Factory unlockWorkflowFactory, //
|
UnlockComponent.Factory unlockWorkflowFactory, //
|
||||||
UpdateReminderComponent.Factory updateReminderWindowBuilder, //
|
UpdateReminderComponent.Factory updateReminderWindowFactory, //
|
||||||
LockComponent.Factory lockWorkflowFactory, //
|
LockComponent.Factory lockWorkflowFactory, //
|
||||||
ErrorComponent.Factory errorWindowFactory, //
|
ErrorComponent.Factory errorWindowFactory, //
|
||||||
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
ShareVaultComponent.Factory shareVaultWindow, //
|
ShareVaultComponent.Factory shareVaultWindow, //
|
||||||
ExecutorService executor) {
|
ExecutorService executor, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.primaryStage = primaryStage;
|
this.primaryStage = primaryStage;
|
||||||
this.trayIntegration = trayIntegration;
|
this.trayIntegration = trayIntegration;
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.preferencesWindow = preferencesWindow;
|
this.preferencesWindow = preferencesWindow;
|
||||||
this.quitWindowBuilder = quitWindowBuilder;
|
this.quitWindowBuilder = quitWindowBuilder;
|
||||||
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
this.unlockWorkflowFactory = unlockWorkflowFactory;
|
||||||
this.updateReminderWindowBuilder = updateReminderWindowBuilder;
|
this.updateReminderWindowFactory = updateReminderWindowFactory;
|
||||||
this.lockWorkflowFactory = lockWorkflowFactory;
|
this.lockWorkflowFactory = lockWorkflowFactory;
|
||||||
this.errorWindowFactory = errorWindowFactory;
|
this.errorWindowFactory = errorWindowFactory;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.shareVaultWindow = shareVaultWindow;
|
this.shareVaultWindow = shareVaultWindow;
|
||||||
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
|
||||||
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -138,8 +142,18 @@ public class FxApplicationWindows {
|
|||||||
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
|
CompletableFuture.runAsync(() -> quitWindowBuilder.build().showQuitWindow(response,forced), Platform::runLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkAndShowUpdateReminderWindow() {
|
public void showUpdateReminderWindow() {
|
||||||
CompletableFuture.runAsync(() -> updateReminderWindowBuilder.create().checkAndShowUpdateReminderWindow(), Platform::runLater);
|
CompletableFuture.runAsync(() -> updateReminderWindowFactory.create().showUpdateReminderWindow(), Platform::runLater);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showDokanySupportEndWindow() {
|
||||||
|
CompletableFuture.runAsync(() -> dialogs.prepareDokanySupportEndDialog(
|
||||||
|
mainWindow.get().window(),
|
||||||
|
stage -> {
|
||||||
|
showPreferencesWindow(SelectedPreferencesTab.VOLUME);
|
||||||
|
stage.close();
|
||||||
|
}
|
||||||
|
).build().showAndWait(), Platform::runLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
|
||||||
|
|||||||
@@ -1,45 +1,57 @@
|
|||||||
package org.cryptomator.ui.fxapp;
|
package org.cryptomator.ui.fxapp;
|
||||||
|
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.SemVerComparator;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Worker;
|
import javafx.concurrent.Worker;
|
||||||
import javafx.concurrent.WorkerStateEvent;
|
import javafx.concurrent.WorkerStateEvent;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
public class UpdateChecker {
|
public class UpdateChecker {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
private static final Logger LOG = LoggerFactory.getLogger(UpdateChecker.class);
|
||||||
private static final Duration AUTOCHECK_DELAY = Duration.seconds(5);
|
private static final Duration AUTO_CHECK_DELAY = Duration.seconds(5);
|
||||||
|
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final StringProperty latestVersionProperty;
|
private final StringProperty latestVersion = new SimpleStringProperty();
|
||||||
private final Comparator<String> semVerComparator;
|
|
||||||
private final ScheduledService<String> updateCheckerService;
|
private final ScheduledService<String> updateCheckerService;
|
||||||
|
private final ObjectProperty<UpdateCheckState> state = new SimpleObjectProperty<>(UpdateCheckState.NOT_CHECKED);
|
||||||
|
private final ObjectProperty<Instant> lastSuccessfulUpdateCheck;
|
||||||
|
private final Comparator<String> versionComparator = new SemVerComparator();
|
||||||
|
private final BooleanBinding updateAvailable;
|
||||||
|
private final BooleanBinding checkFailed;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UpdateChecker(Settings settings, Environment env, @Named("latestVersion") StringProperty latestVersionProperty, @Named("SemVer") Comparator<String> semVerComparator, ScheduledService<String> updateCheckerService) {
|
UpdateChecker(Settings settings, //
|
||||||
|
Environment env, //
|
||||||
|
ScheduledService<String> updateCheckerService) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.latestVersionProperty = latestVersionProperty;
|
|
||||||
this.semVerComparator = semVerComparator;
|
|
||||||
this.updateCheckerService = updateCheckerService;
|
this.updateCheckerService = updateCheckerService;
|
||||||
|
this.lastSuccessfulUpdateCheck = settings.lastSuccessfulUpdateCheck;
|
||||||
|
this.updateAvailable = Bindings.createBooleanBinding(this::isUpdateAvailable, latestVersion);
|
||||||
|
this.checkFailed = Bindings.equal(UpdateCheckState.CHECK_FAILED, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void automaticallyCheckForUpdatesIfEnabled() {
|
public void automaticallyCheckForUpdatesIfEnabled() {
|
||||||
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
|
if (!env.disableUpdateCheck() && settings.checkForUpdates.get()) {
|
||||||
startCheckingForUpdates(AUTOCHECK_DELAY);
|
startCheckingForUpdates(AUTO_CHECK_DELAY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,36 +71,65 @@ public class UpdateChecker {
|
|||||||
|
|
||||||
private void checkStarted(WorkerStateEvent event) {
|
private void checkStarted(WorkerStateEvent event) {
|
||||||
LOG.debug("Checking for updates...");
|
LOG.debug("Checking for updates...");
|
||||||
|
state.set(UpdateCheckState.IS_CHECKING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkSucceeded(WorkerStateEvent event) {
|
private void checkSucceeded(WorkerStateEvent event) {
|
||||||
String latestVersion = updateCheckerService.getValue();
|
var latestVersionString = updateCheckerService.getValue();
|
||||||
LOG.info("Current version: {}, lastest version: {}", getCurrentVersion(), latestVersion);
|
LOG.info("Current version: {}, latest version: {}", getCurrentVersion(), latestVersionString);
|
||||||
|
lastSuccessfulUpdateCheck.set(Instant.now());
|
||||||
if (semVerComparator.compare(getCurrentVersion(), latestVersion) < 0) {
|
latestVersion.set(latestVersionString);
|
||||||
// update is available
|
state.set(UpdateCheckState.CHECK_SUCCESSFUL);
|
||||||
latestVersionProperty.set(latestVersion);
|
|
||||||
} else {
|
|
||||||
latestVersionProperty.set(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFailed(WorkerStateEvent event) {
|
private void checkFailed(WorkerStateEvent event) {
|
||||||
LOG.warn("Error checking for updates", event.getSource().getException());
|
state.set(UpdateCheckState.CHECK_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum UpdateCheckState {
|
||||||
|
NOT_CHECKED,
|
||||||
|
IS_CHECKING,
|
||||||
|
CHECK_SUCCESSFUL,
|
||||||
|
CHECK_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Observable Properties */
|
/* Observable Properties */
|
||||||
|
|
||||||
public BooleanBinding checkingForUpdatesProperty() {
|
public BooleanBinding checkingForUpdatesProperty() {
|
||||||
return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING);
|
return updateCheckerService.stateProperty().isEqualTo(Worker.State.RUNNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyStringProperty latestVersionProperty() {
|
public ReadOnlyStringProperty latestVersionProperty() {
|
||||||
return latestVersionProperty;
|
return latestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding updateAvailableProperty() {
|
||||||
|
return updateAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding checkFailedProperty() {
|
||||||
|
return checkFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUpdateAvailable() {
|
||||||
|
String currentVersion = getCurrentVersion();
|
||||||
|
String latestVersionString = latestVersion.get();
|
||||||
|
|
||||||
|
if (currentVersion == null || latestVersionString == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return versionComparator.compare(currentVersion, latestVersionString) < 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<Instant> lastSuccessfulUpdateCheckProperty() {
|
||||||
|
return lastSuccessfulUpdateCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<UpdateCheckState> updateCheckStateProperty() {
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCurrentVersion() {
|
public String getCurrentVersion() {
|
||||||
return env.getAppVersion();
|
return env.getAppVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import org.slf4j.LoggerFactory;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.util.Duration;
|
import javafx.util.Duration;
|
||||||
@@ -32,13 +30,6 @@ public abstract class UpdateCheckerModule {
|
|||||||
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
|
private static final Duration UPDATE_CHECK_INTERVAL = Duration.hours(3);
|
||||||
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
|
private static final Duration DISABLED_UPDATE_CHECK_INTERVAL = Duration.hours(100000); // Duration.INDEFINITE leads to overflows...
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Named("latestVersion")
|
|
||||||
@FxApplicationScoped
|
|
||||||
static StringProperty provideLatestVersion() {
|
|
||||||
return new SimpleStringProperty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
static Optional<HttpClient> provideHttpClient() {
|
static Optional<HttpClient> provideHttpClient() {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public interface HealthCheckComponent {
|
|||||||
default Stage showHealthCheckWindow() {
|
default Stage showHealthCheckWindow() {
|
||||||
Stage stage = window();
|
Stage stage = window();
|
||||||
stage.setScene(startScene().get());
|
stage.setScene(startScene().get());
|
||||||
|
stage.setMinWidth(420);
|
||||||
|
stage.setMinHeight(300);
|
||||||
stage.show();
|
stage.show();
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class HubConfig {
|
|||||||
public String devicesResourceUrl;
|
public String devicesResourceUrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of String template processors to construct URIs related to this Hub instance.
|
* A collection of functions to construct URIs related to this Hub instance.
|
||||||
*/
|
*/
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public final URIProcessors URIs = new URIProcessors();
|
public final URIProcessors URIs = new URIProcessors();
|
||||||
@@ -49,14 +49,20 @@ public class HubConfig {
|
|||||||
|
|
||||||
public class URIProcessors {
|
public class URIProcessors {
|
||||||
|
|
||||||
|
public final URIProcessor API = this::fromApiEndpoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves paths relative to the <code>/api/</code> endpoint of this Hub instance.
|
* Resolves paths relative to the <code>/api/</code> endpoint of this Hub instance.
|
||||||
*/
|
*/
|
||||||
public final StringTemplate.Processor<URI, RuntimeException> API = template -> {
|
public URI fromApiEndpoint(String path) {
|
||||||
var path = template.interpolate();
|
|
||||||
var relPath = path.startsWith("/") ? path.substring(1) : path;
|
var relPath = path.startsWith("/") ? path.substring(1) : path;
|
||||||
return getApiBaseUrl().resolve(relPath);
|
return getApiBaseUrl().resolve(relPath);
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface URIProcessor {
|
||||||
|
|
||||||
|
URI resolve(String path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
* STEP 0 (Request): GET /api/config
|
* STEP 0 (Request): GET /api/config
|
||||||
*/
|
*/
|
||||||
private void requestApiConfig() {
|
private void requestApiConfig() {
|
||||||
var configUri = hubConfig.URIs.API."config";
|
var configUri = hubConfig.URIs.API.resolve("config");
|
||||||
var request = HttpRequest.newBuilder(configUri) //
|
var request = HttpRequest.newBuilder(configUri) //
|
||||||
.GET() //
|
.GET() //
|
||||||
.timeout(REQ_TIMEOUT) //
|
.timeout(REQ_TIMEOUT) //
|
||||||
@@ -122,7 +122,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
* STEP 1 (Request): GET user key for this device
|
* STEP 1 (Request): GET user key for this device
|
||||||
*/
|
*/
|
||||||
private void requestDeviceData() {
|
private void requestDeviceData() {
|
||||||
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
|
var deviceUri = hubConfig.URIs.API.resolve("devices/" + deviceId);
|
||||||
var request = HttpRequest.newBuilder(deviceUri) //
|
var request = HttpRequest.newBuilder(deviceUri) //
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
.GET() //
|
.GET() //
|
||||||
@@ -162,7 +162,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
* STEP 2 (Request): GET vault key for this user
|
* STEP 2 (Request): GET vault key for this user
|
||||||
*/
|
*/
|
||||||
private void requestVaultMasterkey(String encryptedUserKey) {
|
private void requestVaultMasterkey(String encryptedUserKey) {
|
||||||
var vaultKeyUri = hubConfig.URIs.API."vaults/\{vaultId}/access-token";
|
var vaultKeyUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/access-token");
|
||||||
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
var request = HttpRequest.newBuilder(vaultKeyUri) //
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
.GET() //
|
.GET() //
|
||||||
@@ -205,7 +205,7 @@ public class ReceiveKeyController implements FxController {
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
private void requestLegacyAccessToken() {
|
private void requestLegacyAccessToken() {
|
||||||
var legacyAccessTokenUri = hubConfig.URIs.API."vaults/\{vaultId}/keys/\{deviceId}";
|
var legacyAccessTokenUri = hubConfig.URIs.API.resolve("vaults/" + vaultId + "/keys/" + deviceId);
|
||||||
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
|
var request = HttpRequest.newBuilder(legacyAccessTokenUri) //
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
.GET() //
|
.GET() //
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class RegisterDeviceController implements FxController {
|
|||||||
workInProgress.set(true);
|
workInProgress.set(true);
|
||||||
|
|
||||||
|
|
||||||
var userReq = HttpRequest.newBuilder(hubConfig.URIs.API."users/me") //
|
var userReq = HttpRequest.newBuilder(hubConfig.URIs.API.resolve("users/me")) //
|
||||||
.GET() //
|
.GET() //
|
||||||
.timeout(REQ_TIMEOUT) //
|
.timeout(REQ_TIMEOUT) //
|
||||||
.header("Authorization", "Bearer " + bearerToken) //
|
.header("Authorization", "Bearer " + bearerToken) //
|
||||||
@@ -143,7 +143,7 @@ public class RegisterDeviceController implements FxController {
|
|||||||
var now = Instant.now().toString();
|
var now = Instant.now().toString();
|
||||||
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
|
var dto = new CreateDeviceDto(deviceId, deviceNameField.getText(), BaseEncoding.base64().encode(deviceKeyPair.getPublic().getEncoded()), "DESKTOP", jwe.serialize(), now);
|
||||||
var json = toJson(dto);
|
var json = toJson(dto);
|
||||||
var deviceUri = hubConfig.URIs.API."devices/\{deviceId}";
|
var deviceUri = hubConfig.URIs.fromApiEndpoint("devices/" + deviceId);
|
||||||
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
|
var putDeviceReq = HttpRequest.newBuilder(deviceUri) //
|
||||||
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
.PUT(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8)) //
|
||||||
.timeout(REQ_TIMEOUT) //
|
.timeout(REQ_TIMEOUT) //
|
||||||
@@ -164,7 +164,7 @@ public class RegisterDeviceController implements FxController {
|
|||||||
private void migrateLegacyDevices(ECPublicKey userPublicKey) {
|
private void migrateLegacyDevices(ECPublicKey userPublicKey) {
|
||||||
try {
|
try {
|
||||||
// GET legacy access tokens
|
// GET legacy access tokens
|
||||||
var getUri = hubConfig.URIs.API."devices/\{deviceId}/legacy-access-tokens";
|
var getUri = hubConfig.URIs.API.resolve("devices/" + deviceId + "/legacy-access-tokens");
|
||||||
var getReq = HttpRequest.newBuilder(getUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
var getReq = HttpRequest.newBuilder(getUri).GET().timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
||||||
var getRes = httpClient.send(getReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
var getRes = httpClient.send(getReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
if (getRes.statusCode() != 200) {
|
if (getRes.statusCode() != 200) {
|
||||||
@@ -185,12 +185,12 @@ public class RegisterDeviceController implements FxController {
|
|||||||
LOG.warn("Failed to decrypt legacy access token for vault {}. Skipping migration.", entry.getKey());
|
LOG.warn("Failed to decrypt legacy access token for vault {}. Skipping migration.", entry.getKey());
|
||||||
}
|
}
|
||||||
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
var postUri = hubConfig.URIs.API."users/me/access-tokens";
|
var postUri = hubConfig.URIs.fromApiEndpoint("users/me/access-tokens");
|
||||||
var postBody = JSON.writer().writeValueAsString(newAccessTokens);
|
var postBody = JSON.writer().writeValueAsString(newAccessTokens);
|
||||||
var postReq = HttpRequest.newBuilder(postUri).POST(HttpRequest.BodyPublishers.ofString(postBody)).timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
var postReq = HttpRequest.newBuilder(postUri).POST(HttpRequest.BodyPublishers.ofString(postBody)).timeout(REQ_TIMEOUT).header("Authorization", "Bearer " + bearerToken).build();
|
||||||
var postRes = httpClient.send(postReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
var postRes = httpClient.send(postReq, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||||
if (postRes.statusCode() != 200) {
|
if (postRes.statusCode() != 200) {
|
||||||
throw new IOException(STR."Unexpected response from POST \{postUri}: \{postRes.statusCode()}");
|
throw new IOException("Unexpected response from POST " + postUri + ": " + postRes.statusCode());
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// log and ignore: this is merely a best-effort attempt of migrating legacy devices. Failure is uncritical as this is merely a convenience feature.
|
// log and ignore: this is merely a best-effort attempt of migrating legacy devices. Failure is uncritical as this is merely a convenience feature.
|
||||||
|
|||||||
@@ -112,12 +112,12 @@ public class MasterkeyFileLoadingStrategy implements KeyLoadingStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void savePasswordToSystemkeychain(Passphrase passphrase) {
|
private void savePasswordToSystemkeychain(Passphrase passphrase) {
|
||||||
if (keychain.isSupported()) {
|
try {
|
||||||
try {
|
if (keychain.isSupported() && !keychain.getPassphraseStoredProperty(vault.getId()).getValue()) {
|
||||||
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase);
|
keychain.storePassphrase(vault.getId(), vault.getDisplayName(), passphrase);
|
||||||
} catch (KeychainAccessException e) {
|
|
||||||
LOG.error("Failed to store passphrase in system keychain.", e);
|
|
||||||
}
|
}
|
||||||
|
} catch (KeychainAccessException e) {
|
||||||
|
LOG.error("Failed to store passphrase in system keychain.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.LicenseHolder;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
|
import org.cryptomator.ui.fxapp.UpdateChecker;
|
||||||
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@MainWindowScoped
|
@MainWindowScoped
|
||||||
public class MainWindowController implements FxController {
|
public class MainWindowController implements FxController {
|
||||||
@@ -22,22 +29,61 @@ public class MainWindowController implements FxController {
|
|||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final ReadOnlyObjectProperty<Vault> selectedVault;
|
private final ReadOnlyObjectProperty<Vault> selectedVault;
|
||||||
|
private final Settings settings;
|
||||||
|
private final FxApplicationWindows appWindows;
|
||||||
|
private final BooleanBinding updateAvailable;
|
||||||
|
private final LicenseHolder licenseHolder;
|
||||||
|
|
||||||
public StackPane root;
|
@FXML
|
||||||
|
private StackPane root;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainWindowController(@MainWindow Stage window, ObjectProperty<Vault> selectedVault) {
|
public MainWindowController(@MainWindow Stage window, //
|
||||||
|
ObjectProperty<Vault> selectedVault, //
|
||||||
|
Settings settings, //
|
||||||
|
FxApplicationWindows appWindows, //
|
||||||
|
UpdateChecker updateChecker, //
|
||||||
|
LicenseHolder licenseHolder) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.selectedVault = selectedVault;
|
this.selectedVault = selectedVault;
|
||||||
|
this.settings = settings;
|
||||||
|
this.appWindows = appWindows;
|
||||||
|
this.updateAvailable = updateChecker.updateAvailableProperty();
|
||||||
|
this.licenseHolder = licenseHolder;
|
||||||
|
updateChecker.automaticallyCheckForUpdatesIfEnabled();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
LOG.trace("init MainWindowController");
|
LOG.trace("init MainWindowController");
|
||||||
|
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (SystemUtils.IS_OS_WINDOWS) {
|
||||||
root.getStyleClass().add("os-windows");
|
root.getStyleClass().add("os-windows");
|
||||||
}
|
}
|
||||||
window.focusedProperty().addListener(this::mainWindowFocusChanged);
|
window.focusedProperty().addListener(this::mainWindowFocusChanged);
|
||||||
|
|
||||||
|
if (!neverTouched()) {
|
||||||
|
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
|
||||||
|
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
|
||||||
|
window.setX(settings.windowXPosition.get());
|
||||||
|
window.setY(settings.windowYPosition.get());
|
||||||
|
}
|
||||||
|
window.widthProperty().addListener((_, _, _) -> savePositionalSettings());
|
||||||
|
window.heightProperty().addListener((_, _, _) -> savePositionalSettings());
|
||||||
|
window.xProperty().addListener((_, _, _) -> savePositionalSettings());
|
||||||
|
window.yProperty().addListener((_, _, _) -> savePositionalSettings());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean neverTouched() {
|
||||||
|
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void savePositionalSettings() {
|
||||||
|
settings.windowWidth.setValue(window.getWidth());
|
||||||
|
settings.windowHeight.setValue(window.getHeight());
|
||||||
|
settings.windowXPosition.setValue(window.getX());
|
||||||
|
settings.windowYPosition.setValue(window.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mainWindowFocusChanged(Observable observable) {
|
private void mainWindowFocusChanged(Observable observable) {
|
||||||
@@ -47,4 +93,43 @@ public class MainWindowController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showGeneralPreferences() {
|
||||||
|
appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showContributePreferences() {
|
||||||
|
appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showUpdatePreferences() {
|
||||||
|
appWindows.showPreferencesWindow(SelectedPreferencesTab.UPDATES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyBooleanProperty debugModeEnabledProperty() {
|
||||||
|
return settings.debugMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDebugModeEnabled() {
|
||||||
|
return debugModeEnabledProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding updateAvailableProperty() {
|
||||||
|
return updateAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getUpdateAvailable() {
|
||||||
|
return updateAvailable.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding licenseValidProperty(){
|
||||||
|
return licenseHolder.validLicenseProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getLicenseValid() {
|
||||||
|
return licenseHolder.isValidLicense();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import dagger.Provides;
|
|||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||||
import org.cryptomator.ui.error.ErrorComponent;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxControllerKey;
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
@@ -14,10 +13,9 @@ import org.cryptomator.ui.common.FxmlLoaderFactory;
|
|||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
import org.cryptomator.ui.common.StageFactory;
|
||||||
import org.cryptomator.ui.common.StageInitializer;
|
import org.cryptomator.ui.common.StageInitializer;
|
||||||
|
import org.cryptomator.ui.error.ErrorComponent;
|
||||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
import org.cryptomator.ui.fxapp.PrimaryStage;
|
||||||
import org.cryptomator.ui.health.HealthCheckComponent;
|
|
||||||
import org.cryptomator.ui.migration.MigrationComponent;
|
import org.cryptomator.ui.migration.MigrationComponent;
|
||||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
|
||||||
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
import org.cryptomator.ui.stats.VaultStatisticsComponent;
|
||||||
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
||||||
|
|
||||||
@@ -28,11 +26,10 @@ import javafx.beans.property.SimpleObjectProperty;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.StageStyle;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
|
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
|
||||||
abstract class MainWindowModule {
|
abstract class MainWindowModule {
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -41,9 +38,8 @@ abstract class MainWindowModule {
|
|||||||
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
|
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
|
||||||
initializer.accept(stage);
|
initializer.accept(stage);
|
||||||
stage.setTitle("Cryptomator");
|
stage.setTitle("Cryptomator");
|
||||||
stage.initStyle(StageStyle.UNDECORATED);
|
|
||||||
stage.setMinWidth(650);
|
stage.setMinWidth(650);
|
||||||
stage.setMinHeight(440);
|
stage.setMinHeight(498);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,16 +81,6 @@ abstract class MainWindowModule {
|
|||||||
@FxControllerKey(MainWindowController.class)
|
@FxControllerKey(MainWindowController.class)
|
||||||
abstract FxController bindMainWindowController(MainWindowController controller);
|
abstract FxController bindMainWindowController(MainWindowController controller);
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(MainWindowTitleController.class)
|
|
||||||
abstract FxController bindMainWindowTitleController(MainWindowTitleController controller);
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(ResizeController.class)
|
|
||||||
abstract FxController bindResizeController(ResizeController controller);
|
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(VaultListController.class)
|
@FxControllerKey(VaultListController.class)
|
||||||
|
|||||||
@@ -18,22 +18,20 @@ public class MainWindowSceneFactory extends DefaultSceneFactory {
|
|||||||
protected static final KeyCodeCombination SHORTCUT_N = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN);
|
protected static final KeyCodeCombination SHORTCUT_N = new KeyCodeCombination(KeyCode.N, KeyCombination.SHORTCUT_DOWN);
|
||||||
protected static final KeyCodeCombination SHORTCUT_O = new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN);
|
protected static final KeyCodeCombination SHORTCUT_O = new KeyCodeCombination(KeyCode.O, KeyCombination.SHORTCUT_DOWN);
|
||||||
|
|
||||||
private final Lazy<MainWindowTitleController> mainWindowTitleController;
|
private final Stage window;
|
||||||
private final Lazy<VaultListController> vaultListController;
|
private final Lazy<VaultListController> vaultListController;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MainWindowSceneFactory(Settings settings, Lazy<MainWindowTitleController> mainWindowTitleController, Lazy<VaultListController> vaultListController) {
|
public MainWindowSceneFactory(Settings settings, @MainWindow Stage window, Lazy<VaultListController> vaultListController) {
|
||||||
super(settings);
|
super(settings);
|
||||||
this.mainWindowTitleController = mainWindowTitleController;
|
this.window = window;
|
||||||
this.vaultListController = vaultListController;
|
this.vaultListController = vaultListController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupDefaultAccelerators(Scene scene, Stage stage) {
|
protected void setupDefaultAccelerators(Scene scene, Stage stage) {
|
||||||
if (SystemUtils.IS_OS_WINDOWS) {
|
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||||
scene.getAccelerators().put(ALT_F4, mainWindowTitleController.get()::close);
|
scene.getAccelerators().put(SHORTCUT_W, window::close);
|
||||||
} else {
|
|
||||||
scene.getAccelerators().put(SHORTCUT_W, mainWindowTitleController.get()::close);
|
|
||||||
}
|
}
|
||||||
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddNewVault);
|
scene.getAccelerators().put(SHORTCUT_N, vaultListController.get()::didClickAddNewVault);
|
||||||
scene.getAccelerators().put(SHORTCUT_O, vaultListController.get()::didClickAddExistingVault);
|
scene.getAccelerators().put(SHORTCUT_O, vaultListController.get()::didClickAddExistingVault);
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
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.FxApplicationTerminator;
|
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
|
||||||
import org.cryptomator.ui.fxapp.UpdateChecker;
|
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
|
||||||
import org.cryptomator.ui.traymenu.TrayMenuComponent;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.scene.input.MouseButton;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@MainWindowScoped
|
|
||||||
public class MainWindowTitleController implements FxController {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class);
|
|
||||||
|
|
||||||
private final Stage window;
|
|
||||||
private final FxApplicationTerminator terminator;
|
|
||||||
private final FxApplicationWindows appWindows;
|
|
||||||
private final boolean trayMenuInitialized;
|
|
||||||
private final UpdateChecker updateChecker;
|
|
||||||
private final BooleanBinding updateAvailable;
|
|
||||||
private final LicenseHolder licenseHolder;
|
|
||||||
private final Settings settings;
|
|
||||||
private final BooleanBinding showMinimizeButton;
|
|
||||||
|
|
||||||
public HBox titleBar;
|
|
||||||
private double xOffset;
|
|
||||||
private double yOffset;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
MainWindowTitleController(@MainWindow Stage window, FxApplicationTerminator terminator, FxApplicationWindows appWindows, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
|
|
||||||
this.window = window;
|
|
||||||
this.terminator = terminator;
|
|
||||||
this.appWindows = appWindows;
|
|
||||||
this.trayMenuInitialized = trayMenu.isInitialized();
|
|
||||||
this.updateChecker = updateChecker;
|
|
||||||
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
|
|
||||||
this.licenseHolder = licenseHolder;
|
|
||||||
this.settings = settings;
|
|
||||||
this.showMinimizeButton = Bindings.createBooleanBinding(this::isShowMinimizeButton, settings.showMinimizeButton, settings.showTrayIcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void initialize() {
|
|
||||||
LOG.trace("init MainWindowTitleController");
|
|
||||||
updateChecker.automaticallyCheckForUpdatesIfEnabled();
|
|
||||||
titleBar.setOnMousePressed(event -> {
|
|
||||||
xOffset = event.getSceneX();
|
|
||||||
yOffset = event.getSceneY();
|
|
||||||
|
|
||||||
});
|
|
||||||
titleBar.setOnMouseClicked(event -> {
|
|
||||||
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
|
|
||||||
window.setFullScreen(!window.isFullScreen());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
titleBar.setOnMouseDragged(event -> {
|
|
||||||
if (window.isFullScreen()) return;
|
|
||||||
window.setX(event.getScreenX() - xOffset);
|
|
||||||
window.setY(event.getScreenY() - yOffset);
|
|
||||||
});
|
|
||||||
titleBar.setOnDragDetected(mouseDragEvent -> {
|
|
||||||
titleBar.startFullDrag();
|
|
||||||
});
|
|
||||||
titleBar.setOnMouseDragReleased(mouseDragEvent -> {
|
|
||||||
saveWindowSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.setOnCloseRequest(event -> {
|
|
||||||
close();
|
|
||||||
event.consume();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveWindowSettings() {
|
|
||||||
settings.windowXPosition.setValue(window.getX());
|
|
||||||
settings.windowYPosition.setValue(window.getY());
|
|
||||||
settings.windowWidth.setValue(window.getWidth());
|
|
||||||
settings.windowHeight.setValue(window.getHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void close() {
|
|
||||||
if (trayMenuInitialized) {
|
|
||||||
window.close();
|
|
||||||
} else {
|
|
||||||
terminator.terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void minimize() {
|
|
||||||
window.setIconified(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void showPreferences() {
|
|
||||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void showGeneralPreferences() {
|
|
||||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void showContributePreferences() {
|
|
||||||
appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter/Setter */
|
|
||||||
|
|
||||||
public LicenseHolder getLicenseHolder() {
|
|
||||||
return licenseHolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding updateAvailableProperty() {
|
|
||||||
return updateAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUpdateAvailable() {
|
|
||||||
return updateAvailable.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTrayIconPresent() {
|
|
||||||
return trayMenuInitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyBooleanProperty debugModeEnabledProperty() {
|
|
||||||
return settings.debugMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDebugModeEnabled() {
|
|
||||||
return debugModeEnabledProperty().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding showMinimizeButtonProperty() {
|
|
||||||
return showMinimizeButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowMinimizeButton() {
|
|
||||||
// always show the minimize button if no tray icon is present OR it is explicitly enabled
|
|
||||||
return !trayMenuInitialized || settings.showMinimizeButton.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
|
||||||
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.beans.binding.BooleanBinding;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.geometry.Rectangle2D;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.Region;
|
|
||||||
import javafx.stage.Screen;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import javafx.stage.WindowEvent;
|
|
||||||
|
|
||||||
@MainWindow
|
|
||||||
public class ResizeController implements FxController {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(ResizeController.class);
|
|
||||||
|
|
||||||
private final Stage window;
|
|
||||||
|
|
||||||
public Region tlResizer;
|
|
||||||
public Region trResizer;
|
|
||||||
public Region blResizer;
|
|
||||||
public Region brResizer;
|
|
||||||
public Region tResizer;
|
|
||||||
public Region rResizer;
|
|
||||||
public Region bResizer;
|
|
||||||
public Region lResizer;
|
|
||||||
public Region lDefaultRegion;
|
|
||||||
public Region tDefaultRegion;
|
|
||||||
public Region rDefaultRegion;
|
|
||||||
public Region bDefaultRegion;
|
|
||||||
|
|
||||||
private double origX, origY, origW, origH;
|
|
||||||
|
|
||||||
private final Settings settings;
|
|
||||||
|
|
||||||
private final BooleanBinding showResizingArrows;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ResizeController(@MainWindow Stage window, Settings settings) {
|
|
||||||
this.window = window;
|
|
||||||
this.settings = settings;
|
|
||||||
this.showResizingArrows = window.fullScreenProperty().not();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void initialize() {
|
|
||||||
LOG.trace("init ResizeController");
|
|
||||||
|
|
||||||
if (!neverTouched()) {
|
|
||||||
window.setHeight(settings.windowHeight.get() > window.getMinHeight() ? settings.windowHeight.get() : window.getMinHeight());
|
|
||||||
window.setWidth(settings.windowWidth.get() > window.getMinWidth() ? settings.windowWidth.get() : window.getMinWidth());
|
|
||||||
window.setX(settings.windowXPosition.get());
|
|
||||||
window.setY(settings.windowYPosition.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
window.setOnShowing(this::checkDisplayBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean neverTouched() {
|
|
||||||
return (settings.windowHeight.get() == 0) && (settings.windowWidth.get() == 0) && (settings.windowXPosition.get() == 0) && (settings.windowYPosition.get() == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkDisplayBounds(WindowEvent evt) {
|
|
||||||
// Minimizing a window in Windows and closing it could result in an out of bounds position at (x, y) = (-32000, -32000)
|
|
||||||
// See https://devblogs.microsoft.com/oldnewthing/20041028-00/?p=37453
|
|
||||||
// If the position is (-32000, -32000), restore to the last saved position
|
|
||||||
if (window.getX() == -32000 && window.getY() == -32000) {
|
|
||||||
window.setX(settings.windowXPosition.get());
|
|
||||||
window.setY(settings.windowYPosition.get());
|
|
||||||
window.setWidth(settings.windowWidth.get());
|
|
||||||
window.setHeight(settings.windowHeight.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isOutOfDisplayBounds()) {
|
|
||||||
// If the position is illegal, then the window appears on the main screen in the middle of the window.
|
|
||||||
LOG.debug("Resetting window position due to insufficient screen overlap");
|
|
||||||
Rectangle2D primaryScreenBounds = Screen.getPrimary().getBounds();
|
|
||||||
window.setX((primaryScreenBounds.getWidth() - window.getMinWidth()) / 2);
|
|
||||||
window.setY((primaryScreenBounds.getHeight() - window.getMinHeight()) / 2);
|
|
||||||
window.setWidth(window.getMinWidth());
|
|
||||||
window.setHeight(window.getMinHeight());
|
|
||||||
savePositionalSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isOutOfDisplayBounds() {
|
|
||||||
// define a rect which is inset on all sides from the window's rect:
|
|
||||||
final double x = window.getX() + 20; // 20px left
|
|
||||||
final double y = window.getY() + 5; // 5px top
|
|
||||||
final double w = window.getWidth() - 40; // 20px left + 20px right
|
|
||||||
final double h = window.getHeight() - 25; // 5px top + 20px bottom
|
|
||||||
return isRectangleOutOfScreen(x, y, 0, h) // Left pixel column
|
|
||||||
|| isRectangleOutOfScreen(x + w, y, 0, h) // Right pixel column
|
|
||||||
|| isRectangleOutOfScreen(x, y, w, 0) // Top pixel row
|
|
||||||
|| isRectangleOutOfScreen(x, y + h, w, 0); // Bottom pixel row
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isRectangleOutOfScreen(double x, double y, double width, double height) {
|
|
||||||
return Screen.getScreensForRectangle(x, y, width, height).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startResize(MouseEvent evt) {
|
|
||||||
origX = window.getX();
|
|
||||||
origY = window.getY();
|
|
||||||
origW = window.getWidth();
|
|
||||||
origH = window.getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeTopLeft(MouseEvent evt) {
|
|
||||||
resizeTop(evt);
|
|
||||||
resizeLeft(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeTopRight(MouseEvent evt) {
|
|
||||||
resizeTop(evt);
|
|
||||||
resizeRight(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeBottomLeft(MouseEvent evt) {
|
|
||||||
resizeBottom(evt);
|
|
||||||
resizeLeft(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeBottomRight(MouseEvent evt) {
|
|
||||||
resizeBottom(evt);
|
|
||||||
resizeRight(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeTop(MouseEvent evt) {
|
|
||||||
startResize(evt);
|
|
||||||
double newY = evt.getScreenY();
|
|
||||||
double dy = newY - origY;
|
|
||||||
double newH = origH - dy;
|
|
||||||
if (newH < window.getMaxHeight() && newH > window.getMinHeight()) {
|
|
||||||
window.setY(newY);
|
|
||||||
window.setHeight(newH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeLeft(MouseEvent evt) {
|
|
||||||
startResize(evt);
|
|
||||||
double newX = evt.getScreenX();
|
|
||||||
double dx = newX - origX;
|
|
||||||
double newW = origW - dx;
|
|
||||||
if (newW < window.getMaxWidth() && newW > window.getMinWidth()) {
|
|
||||||
window.setX(newX);
|
|
||||||
window.setWidth(newW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeBottom(MouseEvent evt) {
|
|
||||||
double newH = evt.getSceneY();
|
|
||||||
if (newH < window.getMaxHeight() && newH > window.getMinHeight()) {
|
|
||||||
window.setHeight(newH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private void resizeRight(MouseEvent evt) {
|
|
||||||
double newW = evt.getSceneX();
|
|
||||||
if (newW < window.getMaxWidth() && newW > window.getMinWidth()) {
|
|
||||||
window.setWidth(newW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void savePositionalSettings() {
|
|
||||||
settings.windowWidth.setValue(window.getWidth());
|
|
||||||
settings.windowHeight.setValue(window.getHeight());
|
|
||||||
settings.windowXPosition.setValue(window.getX());
|
|
||||||
settings.windowYPosition.setValue(window.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
public BooleanBinding showResizingArrowsProperty() {
|
|
||||||
return showResizingArrows;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowResizingArrows() {
|
|
||||||
return showResizingArrows.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,9 @@ import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
|||||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@@ -21,7 +21,6 @@ public class VaultDetailLockedController implements FxController {
|
|||||||
private final ReadOnlyObjectProperty<Vault> vault;
|
private final ReadOnlyObjectProperty<Vault> vault;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final KeychainManager keychain;
|
|
||||||
private final Stage mainWindow;
|
private final Stage mainWindow;
|
||||||
private final ObservableValue<Boolean> passwordSaved;
|
private final ObservableValue<Boolean> passwordSaved;
|
||||||
|
|
||||||
@@ -30,13 +29,11 @@ public class VaultDetailLockedController implements FxController {
|
|||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
this.keychain = keychain;
|
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
if (keychain.isSupported() && !keychain.isLocked()) {
|
this.passwordSaved = Bindings.createBooleanBinding(() -> {
|
||||||
this.passwordSaved = vault.flatMap(v -> keychain.getPassphraseStoredProperty(v.getId())).orElse(false);
|
var v = vault.get();
|
||||||
} else {
|
return v != null && keychain.getPassphraseStoredProperty(v.getId()).getValue();
|
||||||
this.passwordSaved = new SimpleBooleanProperty(false);
|
}, vault, keychain.getKeychainImplementation());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ package org.cryptomator.ui.mainwindow;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
@@ -19,17 +20,22 @@ import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
|
|||||||
public class VaultDetailMissingVaultController implements FxController {
|
public class VaultDetailMissingVaultController implements FxController {
|
||||||
|
|
||||||
private final ObjectProperty<Vault> vault;
|
private final ObjectProperty<Vault> vault;
|
||||||
private final RemoveVaultComponent.Builder removeVault;
|
private final ObservableList<Vault> vaults;
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultDetailMissingVaultController(ObjectProperty<Vault> vault, RemoveVaultComponent.Builder removeVault, ResourceBundle resourceBundle, @MainWindow Stage window) {
|
public VaultDetailMissingVaultController(ObjectProperty<Vault> vault, //
|
||||||
|
ObservableList<Vault> vaults, //
|
||||||
|
ResourceBundle resourceBundle, //
|
||||||
|
@MainWindow Stage window, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.removeVault = removeVault;
|
this.vaults = vaults;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -39,7 +45,7 @@ public class VaultDetailMissingVaultController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void didClickRemoveVault() {
|
void didClickRemoveVault() {
|
||||||
removeVault.vault(vault.get()).build().showRemoveVault();
|
dialogs.prepareRemoveVaultDialog(window, vault.get(), vaults).build().showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ package org.cryptomator.ui.mainwindow;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
@@ -18,14 +19,23 @@ public class VaultDetailUnknownErrorController implements FxController {
|
|||||||
private final ObjectProperty<Vault> vault;
|
private final ObjectProperty<Vault> vault;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final Stage errorWindow;
|
private final Stage errorWindow;
|
||||||
private final RemoveVaultComponent.Builder removeVault;
|
private final ObservableList<Vault> vaults;
|
||||||
|
private final Stage mainWindow;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultDetailUnknownErrorController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, @Named("errorWindow") Stage errorWindow, RemoveVaultComponent.Builder removeVault) {
|
public VaultDetailUnknownErrorController(@MainWindow Stage mainWindow, //
|
||||||
|
ObjectProperty<Vault> vault, //
|
||||||
|
ObservableList<Vault> vaults, //
|
||||||
|
FxApplicationWindows appWindows, //
|
||||||
|
@Named("errorWindow") Stage errorWindow, //
|
||||||
|
Dialogs dialogs) {
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
|
this.vaults = vaults;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.errorWindow = errorWindow;
|
this.errorWindow = errorWindow;
|
||||||
this.removeVault = removeVault;
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -40,6 +50,6 @@ public class VaultDetailUnknownErrorController implements FxController {
|
|||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void didClickRemoveVault() {
|
void didClickRemoveVault() {
|
||||||
removeVault.vault(vault.get()).build().showRemoveVault();
|
dialogs.prepareRemoveVaultDialog(mainWindow, vault.get(), vaults).build().showAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.ui.common.Animations;
|
import org.cryptomator.ui.common.Animations;
|
||||||
@@ -18,6 +19,7 @@ public class VaultListCellController implements FxController {
|
|||||||
|
|
||||||
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
private final ObjectProperty<Vault> vault = new SimpleObjectProperty<>();
|
||||||
private final ObservableValue<FontAwesome5Icon> glyph;
|
private final ObservableValue<FontAwesome5Icon> glyph;
|
||||||
|
private final ObservableValue<Boolean> compactMode;
|
||||||
|
|
||||||
private AutoAnimator spinAnimation;
|
private AutoAnimator spinAnimation;
|
||||||
|
|
||||||
@@ -25,8 +27,9 @@ public class VaultListCellController implements FxController {
|
|||||||
public FontAwesome5IconView vaultStateView;
|
public FontAwesome5IconView vaultStateView;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultListCellController() {
|
VaultListCellController(Settings settings) {
|
||||||
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
this.glyph = vault.flatMap(Vault::stateProperty).map(this::getGlyphForVaultState);
|
||||||
|
this.compactMode = settings.compactMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -68,6 +71,14 @@ public class VaultListCellController implements FxController {
|
|||||||
return vault.get();
|
return vault.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Boolean> compactModeProperty() {
|
||||||
|
return compactMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getCompactMode() {
|
||||||
|
return compactMode.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
public void setVault(Vault value) {
|
public void setVault(Vault value) {
|
||||||
vault.set(value);
|
vault.set(value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import org.cryptomator.common.vaults.Vault;
|
|||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.VaultService;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
|
||||||
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
|
||||||
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ import javax.inject.Inject;
|
|||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.value.ObservableValue;
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -33,23 +34,32 @@ public class VaultListContextMenuController implements FxController {
|
|||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
private final VaultService vaultService;
|
private final VaultService vaultService;
|
||||||
private final KeychainManager keychain;
|
private final KeychainManager keychain;
|
||||||
private final RemoveVaultComponent.Builder removeVault;
|
|
||||||
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
private final VaultOptionsComponent.Factory vaultOptionsWindow;
|
||||||
private final ObservableValue<VaultState.Value> selectedVaultState;
|
private final ObservableValue<VaultState.Value> selectedVaultState;
|
||||||
private final ObservableValue<Boolean> selectedVaultPassphraseStored;
|
private final ObservableValue<Boolean> selectedVaultPassphraseStored;
|
||||||
private final ObservableValue<Boolean> selectedVaultRemovable;
|
private final ObservableValue<Boolean> selectedVaultRemovable;
|
||||||
private final ObservableValue<Boolean> selectedVaultUnlockable;
|
private final ObservableValue<Boolean> selectedVaultUnlockable;
|
||||||
private final ObservableValue<Boolean> selectedVaultLockable;
|
private final ObservableValue<Boolean> selectedVaultLockable;
|
||||||
|
private final ObservableList<Vault> vaults;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, @MainWindow Stage mainWindow, FxApplicationWindows appWindows, VaultService vaultService, KeychainManager keychain, RemoveVaultComponent.Builder removeVault, VaultOptionsComponent.Factory vaultOptionsWindow) {
|
VaultListContextMenuController(ObjectProperty<Vault> selectedVault, //
|
||||||
|
ObservableList<Vault> vaults, //
|
||||||
|
@MainWindow Stage mainWindow, //
|
||||||
|
FxApplicationWindows appWindows, //
|
||||||
|
VaultService vaultService, //
|
||||||
|
KeychainManager keychain, //
|
||||||
|
VaultOptionsComponent.Factory vaultOptionsWindow, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.selectedVault = selectedVault;
|
this.selectedVault = selectedVault;
|
||||||
|
this.vaults = vaults;
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
this.vaultService = vaultService;
|
this.vaultService = vaultService;
|
||||||
this.keychain = keychain;
|
this.keychain = keychain;
|
||||||
this.removeVault = removeVault;
|
|
||||||
this.vaultOptionsWindow = vaultOptionsWindow;
|
this.vaultOptionsWindow = vaultOptionsWindow;
|
||||||
|
this.dialogs = dialogs;
|
||||||
|
|
||||||
this.selectedVaultState = selectedVault.flatMap(Vault::stateProperty).orElse(null);
|
this.selectedVaultState = selectedVault.flatMap(Vault::stateProperty).orElse(null);
|
||||||
this.selectedVaultPassphraseStored = selectedVault.map(this::isPasswordStored).orElse(false);
|
this.selectedVaultPassphraseStored = selectedVault.map(this::isPasswordStored).orElse(false);
|
||||||
@@ -65,7 +75,7 @@ public class VaultListContextMenuController implements FxController {
|
|||||||
@FXML
|
@FXML
|
||||||
public void didClickRemoveVault() {
|
public void didClickRemoveVault() {
|
||||||
var vault = Objects.requireNonNull(selectedVault.get());
|
var vault = Objects.requireNonNull(selectedVault.get());
|
||||||
removeVault.vault(vault).build().showRemoveVault();
|
dialogs.prepareRemoveVaultDialog(mainWindow, vault, vaults).build().showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package org.cryptomator.ui.mainwindow;
|
package org.cryptomator.ui.mainwindow;
|
||||||
|
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||||
import org.cryptomator.cryptofs.DirStructure;
|
import org.cryptomator.cryptofs.DirStructure;
|
||||||
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.VaultService;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.cryptomator.ui.removevault.RemoveVaultComponent;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -23,7 +26,6 @@ import javafx.collections.ListChangeListener;
|
|||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Side;
|
import javafx.geometry.Side;
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ContextMenu;
|
import javafx.scene.control.ContextMenu;
|
||||||
import javafx.scene.control.ListView;
|
import javafx.scene.control.ListView;
|
||||||
import javafx.scene.input.ContextMenuEvent;
|
import javafx.scene.input.ContextMenuEvent;
|
||||||
@@ -32,6 +34,7 @@ import javafx.scene.input.KeyCode;
|
|||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.scene.input.TransferMode;
|
import javafx.scene.input.TransferMode;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -58,19 +61,22 @@ public class VaultListController implements FxController {
|
|||||||
|
|
||||||
private final Stage mainWindow;
|
private final Stage mainWindow;
|
||||||
private final ObservableList<Vault> vaults;
|
private final ObservableList<Vault> vaults;
|
||||||
|
private final VaultService vaultService;
|
||||||
private final ObjectProperty<Vault> selectedVault;
|
private final ObjectProperty<Vault> selectedVault;
|
||||||
private final VaultListCellFactory cellFactory;
|
private final VaultListCellFactory cellFactory;
|
||||||
private final AddVaultWizardComponent.Builder addVaultWizard;
|
private final AddVaultWizardComponent.Builder addVaultWizard;
|
||||||
private final BooleanBinding emptyVaultList;
|
private final BooleanBinding emptyVaultList;
|
||||||
private final RemoveVaultComponent.Builder removeVaultDialogue;
|
|
||||||
private final VaultListManager vaultListManager;
|
private final VaultListManager vaultListManager;
|
||||||
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
|
private final BooleanProperty draggingVaultOver = new SimpleBooleanProperty();
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
|
private final ObservableValue<Double> cellSize;
|
||||||
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
public ListView<Vault> vaultList;
|
public ListView<Vault> vaultList;
|
||||||
public StackPane root;
|
public StackPane root;
|
||||||
public Button addVaultBtn;
|
@FXML
|
||||||
|
private HBox addVaultButton;
|
||||||
@FXML
|
@FXML
|
||||||
private ContextMenu addVaultContextMenu;
|
private ContextMenu addVaultContextMenu;
|
||||||
|
|
||||||
@@ -79,29 +85,38 @@ public class VaultListController implements FxController {
|
|||||||
ObservableList<Vault> vaults, //
|
ObservableList<Vault> vaults, //
|
||||||
ObjectProperty<Vault> selectedVault, //
|
ObjectProperty<Vault> selectedVault, //
|
||||||
VaultListCellFactory cellFactory, //
|
VaultListCellFactory cellFactory, //
|
||||||
|
VaultService vaultService, //
|
||||||
AddVaultWizardComponent.Builder addVaultWizard, //
|
AddVaultWizardComponent.Builder addVaultWizard, //
|
||||||
RemoveVaultComponent.Builder removeVaultDialogue, //
|
|
||||||
VaultListManager vaultListManager, //
|
VaultListManager vaultListManager, //
|
||||||
ResourceBundle resourceBundle, //
|
ResourceBundle resourceBundle, //
|
||||||
FxApplicationWindows appWindows) {
|
FxApplicationWindows appWindows, //
|
||||||
|
Settings settings, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.mainWindow = mainWindow;
|
this.mainWindow = mainWindow;
|
||||||
this.vaults = vaults;
|
this.vaults = vaults;
|
||||||
this.selectedVault = selectedVault;
|
this.selectedVault = selectedVault;
|
||||||
this.cellFactory = cellFactory;
|
this.cellFactory = cellFactory;
|
||||||
|
this.vaultService = vaultService;
|
||||||
this.addVaultWizard = addVaultWizard;
|
this.addVaultWizard = addVaultWizard;
|
||||||
this.removeVaultDialogue = removeVaultDialogue;
|
|
||||||
this.vaultListManager = vaultListManager;
|
this.vaultListManager = vaultListManager;
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
|
this.dialogs = dialogs;
|
||||||
|
|
||||||
this.emptyVaultList = Bindings.isEmpty(vaults);
|
this.emptyVaultList = Bindings.isEmpty(vaults);
|
||||||
|
|
||||||
selectedVault.addListener(this::selectedVaultDidChange);
|
selectedVault.addListener(this::selectedVaultDidChange);
|
||||||
|
cellSize = settings.compactMode.map(compact -> compact ? 30.0 : 60.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
vaultList.setItems(vaults);
|
vaultList.setItems(vaults);
|
||||||
vaultList.setCellFactory(cellFactory);
|
vaultList.setCellFactory(cellFactory);
|
||||||
|
|
||||||
|
vaultList.prefHeightProperty().bind(
|
||||||
|
vaultList.fixedCellSizeProperty().multiply(Bindings.size(vaultList.getItems()))
|
||||||
|
);
|
||||||
|
|
||||||
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty());
|
||||||
vaults.addListener((ListChangeListener.Change<? extends Vault> c) -> {
|
vaults.addListener((ListChangeListener.Change<? extends Vault> c) -> {
|
||||||
while (c.next()) {
|
while (c.next()) {
|
||||||
@@ -119,6 +134,9 @@ public class VaultListController implements FxController {
|
|||||||
Optional.ofNullable(selectedVault.get())
|
Optional.ofNullable(selectedVault.get())
|
||||||
.filter(Vault::isLocked)
|
.filter(Vault::isLocked)
|
||||||
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
|
.ifPresent(vault -> appWindows.startUnlockWorkflow(vault, mainWindow));
|
||||||
|
Optional.ofNullable(selectedVault.get())
|
||||||
|
.filter(Vault::isUnlocked)
|
||||||
|
.ifPresent(vaultService::reveal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -164,7 +182,7 @@ public class VaultListController implements FxController {
|
|||||||
if (addVaultContextMenu.isShowing()) {
|
if (addVaultContextMenu.isShowing()) {
|
||||||
addVaultContextMenu.hide();
|
addVaultContextMenu.hide();
|
||||||
} else {
|
} else {
|
||||||
addVaultContextMenu.show(addVaultBtn, Side.BOTTOM, 0.0, 0.0);
|
addVaultContextMenu.show(addVaultButton, Side.BOTTOM, 0.0, 0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +213,7 @@ public class VaultListController implements FxController {
|
|||||||
private void pressedShortcutToRemoveVault() {
|
private void pressedShortcutToRemoveVault() {
|
||||||
final var vault = selectedVault.get();
|
final var vault = selectedVault.get();
|
||||||
if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION).contains(vault.getState())) {
|
if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION).contains(vault.getState())) {
|
||||||
removeVaultDialogue.vault(vault).build().showRemoveVault();
|
dialogs.prepareRemoveVaultDialog(mainWindow, vault, vaults).build().showAndWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +258,11 @@ public class VaultListController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showPreferences() {
|
||||||
|
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||||
|
}
|
||||||
|
|
||||||
// Getter and Setter
|
// Getter and Setter
|
||||||
|
|
||||||
public BooleanBinding emptyVaultListProperty() {
|
public BooleanBinding emptyVaultListProperty() {
|
||||||
@@ -258,5 +281,12 @@ public class VaultListController implements FxController {
|
|||||||
return draggingVaultOver.get();
|
return draggingVaultOver.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Double> cellSizeProperty() {
|
||||||
|
return cellSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getCellSize() {
|
||||||
|
return cellSize.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
package org.cryptomator.ui.preferences;
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
import org.cryptomator.common.Environment;
|
import org.cryptomator.common.Environment;
|
||||||
|
import org.cryptomator.common.Passphrase;
|
||||||
|
import org.cryptomator.common.keychain.KeychainManager;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
import org.cryptomator.integrations.autostart.AutoStartProvider;
|
||||||
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
import org.cryptomator.integrations.autostart.ToggleAutoStartFailedException;
|
||||||
|
import org.cryptomator.integrations.common.NamedServiceProvider;
|
||||||
|
import org.cryptomator.integrations.keychain.KeychainAccessException;
|
||||||
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
|
||||||
|
import org.cryptomator.integrations.quickaccess.QuickAccessService;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
import org.cryptomator.ui.fxapp.FxApplicationWindows;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -12,6 +18,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
@@ -30,12 +37,16 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final Optional<AutoStartProvider> autoStartProvider;
|
private final Optional<AutoStartProvider> autoStartProvider;
|
||||||
|
private final List<QuickAccessService> quickAccessServices;
|
||||||
private final Application application;
|
private final Application application;
|
||||||
private final Environment environment;
|
private final Environment environment;
|
||||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
private final List<KeychainAccessProvider> keychainAccessProviders;
|
||||||
|
private final KeychainManager keychain;
|
||||||
private final FxApplicationWindows appWindows;
|
private final FxApplicationWindows appWindows;
|
||||||
public CheckBox useKeychainCheckbox;
|
public CheckBox useKeychainCheckbox;
|
||||||
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
public ChoiceBox<KeychainAccessProvider> keychainBackendChoiceBox;
|
||||||
|
public CheckBox useQuickAccessCheckbox;
|
||||||
|
public ChoiceBox<QuickAccessService> quickAccessServiceChoiceBox;
|
||||||
public CheckBox startHiddenCheckbox;
|
public CheckBox startHiddenCheckbox;
|
||||||
public CheckBox autoCloseVaultsCheckbox;
|
public CheckBox autoCloseVaultsCheckbox;
|
||||||
public CheckBox debugModeCheckbox;
|
public CheckBox debugModeCheckbox;
|
||||||
@@ -43,11 +54,13 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
public ToggleGroup nodeOrientation;
|
public ToggleGroup nodeOrientation;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, Application application, Environment environment, FxApplicationWindows appWindows) {
|
GeneralPreferencesController(@PreferencesWindow Stage window, Settings settings, Optional<AutoStartProvider> autoStartProvider, List<KeychainAccessProvider> keychainAccessProviders, KeychainManager keychain, Application application, Environment environment, FxApplicationWindows appWindows) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.autoStartProvider = autoStartProvider;
|
this.autoStartProvider = autoStartProvider;
|
||||||
this.keychainAccessProviders = keychainAccessProviders;
|
this.keychainAccessProviders = keychainAccessProviders;
|
||||||
|
this.keychain = keychain;
|
||||||
|
this.quickAccessServices = QuickAccessService.get().toList();
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.appWindows = appWindows;
|
this.appWindows = appWindows;
|
||||||
@@ -60,13 +73,51 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode);
|
debugModeCheckbox.selectedProperty().bindBidirectional(settings.debugMode);
|
||||||
autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled()));
|
autoStartProvider.ifPresent(autoStart -> autoStartCheckbox.setSelected(autoStart.isEnabled()));
|
||||||
|
|
||||||
var keychainSettingsConverter = new KeychainProviderClassNameConverter(keychainAccessProviders);
|
var keychainSettingsConverter = new ServiceToSettingsConverter<>(keychainAccessProviders);
|
||||||
keychainBackendChoiceBox.getItems().addAll(keychainAccessProviders);
|
keychainBackendChoiceBox.getItems().addAll(keychainAccessProviders);
|
||||||
keychainBackendChoiceBox.setValue(keychainSettingsConverter.fromString(settings.keychainProvider.get()));
|
keychainBackendChoiceBox.setValue(keychainSettingsConverter.fromString(settings.keychainProvider.get()));
|
||||||
keychainBackendChoiceBox.setConverter(new KeychainProviderDisplayNameConverter());
|
keychainBackendChoiceBox.setConverter(new KeychainProviderDisplayNameConverter());
|
||||||
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
Bindings.bindBidirectional(settings.keychainProvider, keychainBackendChoiceBox.valueProperty(), keychainSettingsConverter);
|
||||||
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
|
useKeychainCheckbox.selectedProperty().bindBidirectional(settings.useKeychain);
|
||||||
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
|
keychainBackendChoiceBox.disableProperty().bind(useKeychainCheckbox.selectedProperty().not());
|
||||||
|
keychainBackendChoiceBox.valueProperty().addListener(this::migrateKeychainEntriesOnMac);
|
||||||
|
|
||||||
|
useQuickAccessCheckbox.selectedProperty().bindBidirectional(settings.useQuickAccess);
|
||||||
|
var quickAccessSettingsConverter = new ServiceToSettingsConverter<>(quickAccessServices);
|
||||||
|
quickAccessServiceChoiceBox.getItems().addAll(quickAccessServices);
|
||||||
|
quickAccessServiceChoiceBox.setValue(quickAccessSettingsConverter.fromString(settings.quickAccessService.get()));
|
||||||
|
quickAccessServiceChoiceBox.setConverter(new NamedServiceConverter<>());
|
||||||
|
Bindings.bindBidirectional(settings.quickAccessService, quickAccessServiceChoiceBox.valueProperty(), quickAccessSettingsConverter);
|
||||||
|
quickAccessServiceChoiceBox.disableProperty().bind(useQuickAccessCheckbox.selectedProperty().not());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void migrateKeychainEntriesOnMac(Observable observable) {
|
||||||
|
if (!SystemUtils.IS_OS_MAC) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var provider = keychainBackendChoiceBox.getSelectionModel().getSelectedItem();
|
||||||
|
var providerId = "org.cryptomator.macos.keychain.MacSystemKeychainAccess";
|
||||||
|
var isSystemKeychain = provider.getClass().getName().equals(providerId);
|
||||||
|
|
||||||
|
List<String> vaults = settings.directories.stream()
|
||||||
|
.map(vault -> vault.id)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (!vaults.isEmpty()) {
|
||||||
|
LOG.info("Migrating keychain entries for vaults: {}", vaults);
|
||||||
|
}
|
||||||
|
for (String vaultId :vaults) {
|
||||||
|
try {
|
||||||
|
if (keychain.isPassphraseStored(vaultId)) {
|
||||||
|
var passphrase = keychain.loadPassphrase(vaultId);
|
||||||
|
keychain.deletePassphrase(vaultId);
|
||||||
|
keychain.storePassphrase(vaultId, vaultId, new Passphrase(passphrase), !isSystemKeychain);
|
||||||
|
}
|
||||||
|
} catch (KeychainAccessException e) {
|
||||||
|
LOG.error("Failed to migrate keychain entries.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAutoStartSupported() {
|
public boolean isAutoStartSupported() {
|
||||||
@@ -91,6 +142,10 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSomeQuickAccessServiceAvailable() {
|
||||||
|
return !quickAccessServices.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showLogfileDirectory() {
|
public void showLogfileDirectory() {
|
||||||
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||||
@@ -116,29 +171,47 @@ public class GeneralPreferencesController implements FxController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class KeychainProviderClassNameConverter extends StringConverter<KeychainAccessProvider> {
|
private static class NamedServiceConverter<T extends NamedServiceProvider> extends StringConverter<T> {
|
||||||
|
|
||||||
private final List<KeychainAccessProvider> keychainAccessProviders;
|
|
||||||
|
|
||||||
public KeychainProviderClassNameConverter(List<KeychainAccessProvider> keychainAccessProviders) {
|
|
||||||
this.keychainAccessProviders = keychainAccessProviders;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(KeychainAccessProvider provider) {
|
public String toString(T namedService) {
|
||||||
if (provider == null) {
|
if (namedService == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return provider.getClass().getName();
|
return namedService.getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeychainAccessProvider fromString(String string) {
|
public T fromString(String string) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ServiceToSettingsConverter<T> extends StringConverter<T> {
|
||||||
|
|
||||||
|
private final List<T> services;
|
||||||
|
|
||||||
|
public ServiceToSettingsConverter(List<T> services) {
|
||||||
|
this.services = services;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(T service) {
|
||||||
|
if (service == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return service.getClass().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T fromString(String string) {
|
||||||
if (string == null) {
|
if (string == null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return keychainAccessProviders.stream().filter(provider -> provider.getClass().getName().equals(string)).findAny().orElse(null);
|
return services.stream().filter(provider -> provider.getClass().getName().equals(string)).findAny().orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ public class InterfacePreferencesController implements FxController {
|
|||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
private final SupportedLanguages supportedLanguages;
|
private final SupportedLanguages supportedLanguages;
|
||||||
public ChoiceBox<UiTheme> themeChoiceBox;
|
public ChoiceBox<UiTheme> themeChoiceBox;
|
||||||
public CheckBox showMinimizeButtonCheckbox;
|
|
||||||
public CheckBox showTrayIconCheckbox;
|
public CheckBox showTrayIconCheckbox;
|
||||||
|
public CheckBox compactModeCheckbox;
|
||||||
public ChoiceBox<String> preferredLanguageChoiceBox;
|
public ChoiceBox<String> preferredLanguageChoiceBox;
|
||||||
public ToggleGroup nodeOrientation;
|
public ToggleGroup nodeOrientation;
|
||||||
public RadioButton nodeOrientationLtr;
|
public RadioButton nodeOrientationLtr;
|
||||||
@@ -63,9 +63,8 @@ public class InterfacePreferencesController implements FxController {
|
|||||||
themeChoiceBox.valueProperty().bindBidirectional(settings.theme);
|
themeChoiceBox.valueProperty().bindBidirectional(settings.theme);
|
||||||
themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle));
|
themeChoiceBox.setConverter(new UiThemeConverter(resourceBundle));
|
||||||
|
|
||||||
showMinimizeButtonCheckbox.selectedProperty().bindBidirectional(settings.showMinimizeButton);
|
|
||||||
|
|
||||||
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon);
|
showTrayIconCheckbox.selectedProperty().bindBidirectional(settings.showTrayIcon);
|
||||||
|
compactModeCheckbox.selectedProperty().bindBidirectional(settings.compactMode);
|
||||||
|
|
||||||
preferredLanguageChoiceBox.getItems().addAll(supportedLanguages.getLanguageTags());
|
preferredLanguageChoiceBox.getItems().addAll(supportedLanguages.getLanguageTags());
|
||||||
preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.language);
|
preferredLanguageChoiceBox.valueProperty().bindBidirectional(settings.language);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public interface PreferencesComponent {
|
|||||||
selectedTabProperty().set(selectedTab);
|
selectedTabProperty().set(selectedTab);
|
||||||
Stage stage = window();
|
Stage stage = window();
|
||||||
stage.setScene(scene().get());
|
stage.setScene(scene().get());
|
||||||
|
stage.setMinWidth(420);
|
||||||
|
stage.setMinHeight(400);
|
||||||
stage.show();
|
stage.show();
|
||||||
stage.requestFocus();
|
stage.requestFocus();
|
||||||
return stage;
|
return stage;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class PreferencesController implements FxController {
|
|||||||
this.env = env;
|
this.env = env;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.selectedTabProperty = selectedTabProperty;
|
this.selectedTabProperty = selectedTabProperty;
|
||||||
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
|
this.updateAvailable = updateChecker.updateAvailableProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.cryptomator.common.LicenseHolder;
|
|||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.common.settings.UiTheme;
|
import org.cryptomator.common.settings.UiTheme;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.dialogs.Dialogs;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
@@ -12,22 +13,36 @@ import javafx.beans.value.ObservableValue;
|
|||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
import javafx.scene.control.TextFormatter;
|
import javafx.scene.control.TextFormatter;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
|
||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
public class SupporterCertificateController implements FxController {
|
public class SupporterCertificateController implements FxController {
|
||||||
|
|
||||||
|
private static final String DONATE_URI = "https://cryptomator.org/donate";
|
||||||
|
private static final String SPONSORS_URI = "https://cryptomator.org/sponsors";
|
||||||
private static final String SUPPORTER_URI = "https://store.cryptomator.org/desktop";
|
private static final String SUPPORTER_URI = "https://store.cryptomator.org/desktop";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
|
private final Stage window;
|
||||||
private final LicenseHolder licenseHolder;
|
private final LicenseHolder licenseHolder;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
public TextArea supporterCertificateField;
|
private final Dialogs dialogs;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextArea supporterCertificateField;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SupporterCertificateController(Application application, LicenseHolder licenseHolder, Settings settings) {
|
SupporterCertificateController(Application application, //
|
||||||
|
@PreferencesWindow Stage window, //
|
||||||
|
LicenseHolder licenseHolder, //
|
||||||
|
Settings settings, //
|
||||||
|
Dialogs dialogs) {
|
||||||
this.application = application;
|
this.application = application;
|
||||||
|
this.window = window;
|
||||||
this.licenseHolder = licenseHolder;
|
this.licenseHolder = licenseHolder;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.dialogs = dialogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -35,6 +50,11 @@ public class SupporterCertificateController implements FxController {
|
|||||||
supporterCertificateField.setText(licenseHolder.getLicenseKey().orElse(null));
|
supporterCertificateField.setText(licenseHolder.getLicenseKey().orElse(null));
|
||||||
supporterCertificateField.textProperty().addListener(this::registrationKeyChanged);
|
supporterCertificateField.textProperty().addListener(this::registrationKeyChanged);
|
||||||
supporterCertificateField.setTextFormatter(new TextFormatter<>(this::removeWhitespaces));
|
supporterCertificateField.setTextFormatter(new TextFormatter<>(this::removeWhitespaces));
|
||||||
|
settings.licenseKey.addListener((_, _, newValue) -> {
|
||||||
|
if (newValue == null) {
|
||||||
|
supporterCertificateField.setText(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private TextFormatter.Change removeWhitespaces(TextFormatter.Change change) {
|
private TextFormatter.Change removeWhitespaces(TextFormatter.Change change) {
|
||||||
@@ -57,7 +77,23 @@ public class SupporterCertificateController implements FxController {
|
|||||||
application.getHostServices().showDocument(SUPPORTER_URI);
|
application.getHostServices().showDocument(SUPPORTER_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showDonate() {
|
||||||
|
application.getHostServices().showDocument(DONATE_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showSponsors() {
|
||||||
|
application.getHostServices().showDocument(SPONSORS_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void didClickRemoveCert() {
|
||||||
|
dialogs.prepareRemoveCertDialog(window, settings).build().showAndWait();
|
||||||
|
}
|
||||||
|
|
||||||
public LicenseHolder getLicenseHolder() {
|
public LicenseHolder getLicenseHolder() {
|
||||||
return licenseHolder;
|
return licenseHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,33 @@
|
|||||||
package org.cryptomator.ui.preferences;
|
package org.cryptomator.ui.preferences;
|
||||||
|
|
||||||
|
import org.cryptomator.common.Environment;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.fxapp.UpdateChecker;
|
import org.cryptomator.ui.fxapp.UpdateChecker;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javafx.animation.PauseTransition;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.BooleanBinding;
|
import javafx.beans.binding.BooleanBinding;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.format.FormatStyle;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
|
||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
public class UpdatesPreferencesController implements FxController {
|
public class UpdatesPreferencesController implements FxController {
|
||||||
@@ -20,29 +35,55 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
|
private static final String DOWNLOADS_URI = "https://cryptomator.org/downloads";
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
|
private final Environment environment;
|
||||||
|
private final ResourceBundle resourceBundle;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final UpdateChecker updateChecker;
|
private final UpdateChecker updateChecker;
|
||||||
private final ObjectBinding<ContentDisplay> checkForUpdatesButtonState;
|
private final ObjectBinding<ContentDisplay> checkForUpdatesButtonState;
|
||||||
private final ReadOnlyStringProperty latestVersion;
|
private final ReadOnlyStringProperty latestVersion;
|
||||||
|
private final ObservableValue<Instant> lastSuccessfulUpdateCheck;
|
||||||
|
private final StringBinding lastUpdateCheckMessage;
|
||||||
|
private final ObservableValue<String> timeDifferenceMessage;
|
||||||
private final String currentVersion;
|
private final String currentVersion;
|
||||||
private final BooleanBinding updateAvailable;
|
private final BooleanBinding updateAvailable;
|
||||||
|
private final BooleanBinding checkFailed;
|
||||||
|
private final BooleanProperty upToDateLabelVisible = new SimpleBooleanProperty(false);
|
||||||
|
private final DateTimeFormatter formatter;
|
||||||
|
private final BooleanBinding upToDate;
|
||||||
|
|
||||||
/* FXML */
|
/* FXML */
|
||||||
public CheckBox checkForUpdatesCheckbox;
|
public CheckBox checkForUpdatesCheckbox;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UpdatesPreferencesController(Application application, Settings settings, UpdateChecker updateChecker) {
|
UpdatesPreferencesController(Application application, Environment environment, ResourceBundle resourceBundle, Settings settings, UpdateChecker updateChecker) {
|
||||||
this.application = application;
|
this.application = application;
|
||||||
|
this.environment = environment;
|
||||||
|
this.resourceBundle = resourceBundle;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.updateChecker = updateChecker;
|
this.updateChecker = updateChecker;
|
||||||
this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
|
this.checkForUpdatesButtonState = Bindings.when(updateChecker.checkingForUpdatesProperty()).then(ContentDisplay.LEFT).otherwise(ContentDisplay.TEXT_ONLY);
|
||||||
this.latestVersion = updateChecker.latestVersionProperty();
|
this.latestVersion = updateChecker.latestVersionProperty();
|
||||||
this.updateAvailable = latestVersion.isNotNull();
|
this.lastSuccessfulUpdateCheck = updateChecker.lastSuccessfulUpdateCheckProperty();
|
||||||
|
this.timeDifferenceMessage = Bindings.createStringBinding(this::getTimeDifferenceMessage, lastSuccessfulUpdateCheck);
|
||||||
this.currentVersion = updateChecker.getCurrentVersion();
|
this.currentVersion = updateChecker.getCurrentVersion();
|
||||||
|
this.updateAvailable = updateChecker.updateAvailableProperty();
|
||||||
|
this.formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
|
||||||
|
this.upToDate = updateChecker.updateCheckStateProperty().isEqualTo(UpdateChecker.UpdateCheckState.CHECK_SUCCESSFUL).and(latestVersion.isEqualTo(currentVersion));
|
||||||
|
this.checkFailed = updateChecker.checkFailedProperty();
|
||||||
|
this.lastUpdateCheckMessage = Bindings.createStringBinding(this::getLastUpdateCheckMessage, lastSuccessfulUpdateCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates);
|
checkForUpdatesCheckbox.selectedProperty().bindBidirectional(settings.checkForUpdates);
|
||||||
|
|
||||||
|
upToDate.addListener((_, _, newVal) -> {
|
||||||
|
if (newVal) {
|
||||||
|
upToDateLabelVisible.set(true);
|
||||||
|
PauseTransition delay = new PauseTransition(javafx.util.Duration.seconds(5));
|
||||||
|
delay.setOnFinished(_ -> upToDateLabelVisible.set(false));
|
||||||
|
delay.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -55,6 +96,11 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
application.getHostServices().showDocument(DOWNLOADS_URI);
|
application.getHostServices().showDocument(DOWNLOADS_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showLogfileDirectory() {
|
||||||
|
environment.getLogDir().ifPresent(logDirPath -> application.getHostServices().showDocument(logDirPath.toUri().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
/* Observable Properties */
|
/* Observable Properties */
|
||||||
|
|
||||||
public ObjectBinding<ContentDisplay> checkForUpdatesButtonStateProperty() {
|
public ObjectBinding<ContentDisplay> checkForUpdatesButtonStateProperty() {
|
||||||
@@ -77,6 +123,46 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
return currentVersion;
|
return currentVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StringBinding lastUpdateCheckMessageProperty() {
|
||||||
|
return lastUpdateCheckMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastUpdateCheckMessage() {
|
||||||
|
Instant lastCheck = lastSuccessfulUpdateCheck.getValue();
|
||||||
|
if (lastCheck != null && !lastCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
|
||||||
|
return formatter.format(LocalDateTime.ofInstant(lastCheck, ZoneId.systemDefault()));
|
||||||
|
} else {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<String> timeDifferenceMessageProperty() {
|
||||||
|
return timeDifferenceMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTimeDifferenceMessage() {
|
||||||
|
var lastSuccessCheck = lastSuccessfulUpdateCheck.getValue();
|
||||||
|
var duration = Duration.between(lastSuccessCheck, Instant.now());
|
||||||
|
var hours = duration.toHours();
|
||||||
|
if (lastSuccessCheck.equals(Settings.DEFAULT_TIMESTAMP)) {
|
||||||
|
return resourceBundle.getString("preferences.updates.lastUpdateCheck.never");
|
||||||
|
} else if (hours < 1) {
|
||||||
|
return resourceBundle.getString("preferences.updates.lastUpdateCheck.recently");
|
||||||
|
} else if (hours < 24) {
|
||||||
|
return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.hoursAgo"), hours);
|
||||||
|
} else {
|
||||||
|
return String.format(resourceBundle.getString("preferences.updates.lastUpdateCheck.daysAgo"), duration.toDays());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty upToDateLabelVisibleProperty() {
|
||||||
|
return upToDateLabelVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUpToDateLabelVisible() {
|
||||||
|
return upToDateLabelVisible.get();
|
||||||
|
}
|
||||||
|
|
||||||
public BooleanBinding updateAvailableProperty() {
|
public BooleanBinding updateAvailableProperty() {
|
||||||
return updateAvailable;
|
return updateAvailable;
|
||||||
}
|
}
|
||||||
@@ -84,4 +170,13 @@ public class UpdatesPreferencesController implements FxController {
|
|||||||
public boolean isUpdateAvailable() {
|
public boolean isUpdateAvailable() {
|
||||||
return updateAvailable.get();
|
return updateAvailable.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BooleanBinding checkFailedProperty() {
|
||||||
|
return checkFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCheckFailed() {
|
||||||
|
return checkFailed.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package org.cryptomator.ui.removevault;
|
|
||||||
|
|
||||||
import dagger.BindsInstance;
|
|
||||||
import dagger.Lazy;
|
|
||||||
import dagger.Subcomponent;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
|
||||||
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@RemoveVaultScoped
|
|
||||||
@Subcomponent(modules = {RemoveVaultModule.class})
|
|
||||||
public interface RemoveVaultComponent {
|
|
||||||
|
|
||||||
@RemoveVaultWindow
|
|
||||||
Stage window();
|
|
||||||
|
|
||||||
@FxmlScene(FxmlFile.REMOVE_VAULT)
|
|
||||||
Lazy<Scene> scene();
|
|
||||||
|
|
||||||
default void showRemoveVault() {
|
|
||||||
Stage stage = window();
|
|
||||||
stage.setScene(scene().get());
|
|
||||||
stage.sizeToScene();
|
|
||||||
stage.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subcomponent.Builder
|
|
||||||
interface Builder {
|
|
||||||
|
|
||||||
@BindsInstance
|
|
||||||
Builder vault(@RemoveVaultWindow Vault vault);
|
|
||||||
|
|
||||||
RemoveVaultComponent build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package org.cryptomator.ui.removevault;
|
|
||||||
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
@RemoveVaultScoped
|
|
||||||
public class RemoveVaultController implements FxController {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(RemoveVaultController.class);
|
|
||||||
|
|
||||||
private final Stage window;
|
|
||||||
private final Vault vault;
|
|
||||||
private final ObservableList<Vault> vaults;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public RemoveVaultController(@RemoveVaultWindow Stage window, @RemoveVaultWindow Vault vault, ObservableList<Vault> vaults) {
|
|
||||||
this.window = window;
|
|
||||||
this.vault = vault;
|
|
||||||
this.vaults = vaults;
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void close() {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void finish() {
|
|
||||||
vaults.remove(vault);
|
|
||||||
LOG.debug("Removing vault {}.", vault.getDisplayName());
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package org.cryptomator.ui.removevault;
|
|
||||||
|
|
||||||
import dagger.Binds;
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
import dagger.multibindings.IntoMap;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
|
||||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
|
||||||
import org.cryptomator.ui.common.FxControllerKey;
|
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
|
||||||
import org.cryptomator.ui.common.FxmlLoaderFactory;
|
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
|
||||||
import org.cryptomator.ui.common.StageFactory;
|
|
||||||
import org.cryptomator.ui.fxapp.PrimaryStage;
|
|
||||||
|
|
||||||
import javax.inject.Provider;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.stage.Modality;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
abstract class RemoveVaultModule {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@RemoveVaultWindow
|
|
||||||
@RemoveVaultScoped
|
|
||||||
static FxmlLoaderFactory provideFxmlLoaderFactory(Map<Class<? extends FxController>, Provider<FxController>> factories, DefaultSceneFactory sceneFactory, ResourceBundle resourceBundle) {
|
|
||||||
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@RemoveVaultWindow
|
|
||||||
@RemoveVaultScoped
|
|
||||||
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, @RemoveVaultWindow Vault vault, ResourceBundle resourceBundle) {
|
|
||||||
Stage stage = factory.create();
|
|
||||||
stage.setTitle(String.format(resourceBundle.getString("removeVault.title"), vault.getDisplayName()));
|
|
||||||
stage.setResizable(false);
|
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
|
||||||
stage.initOwner(primaryStage);
|
|
||||||
return stage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@FxmlScene(FxmlFile.REMOVE_VAULT)
|
|
||||||
@RemoveVaultScoped
|
|
||||||
static Scene provideRemoveVaultScene(@RemoveVaultWindow FxmlLoaderFactory fxmlLoaders) {
|
|
||||||
return fxmlLoaders.createScene(FxmlFile.REMOVE_VAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------
|
|
||||||
|
|
||||||
@Binds
|
|
||||||
@IntoMap
|
|
||||||
@FxControllerKey(RemoveVaultController.class)
|
|
||||||
abstract FxController bindRemoveVaultController(RemoveVaultController controller);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.cryptomator.ui.removevault;
|
|
||||||
|
|
||||||
import javax.inject.Scope;
|
|
||||||
import java.lang.annotation.Documented;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
@Scope
|
|
||||||
@Documented
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface RemoveVaultScoped {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.cryptomator.ui.removevault;
|
|
||||||
|
|
||||||
import javax.inject.Qualifier;
|
|
||||||
import java.lang.annotation.Documented;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
|
||||||
|
|
||||||
@Qualifier
|
|
||||||
@Documented
|
|
||||||
@Retention(RUNTIME)
|
|
||||||
@interface RemoveVaultWindow {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -116,7 +116,10 @@ public class UnlockWorkflow extends Task<Void> {
|
|||||||
double x = mainWindow.getX() + (mainWindow.getWidth() - window.getWidth()) / 2;
|
double x = mainWindow.getX() + (mainWindow.getWidth() - window.getWidth()) / 2;
|
||||||
double y = mainWindow.getY() + (mainWindow.getHeight() - window.getHeight()) / 2;
|
double y = mainWindow.getY() + (mainWindow.getHeight() - window.getHeight()) / 2;
|
||||||
if(!mainWindow.isShowing()) {
|
if(!mainWindow.isShowing()) {
|
||||||
Screen screen = Screen.getScreensForRectangle(mainWindow.getX(), mainWindow.getY(), mainWindow.getWidth(), mainWindow.getHeight()).get(0);
|
Screen screen = Screen.getScreensForRectangle(mainWindow.getX(), mainWindow.getY(), mainWindow.getWidth(), mainWindow.getHeight())
|
||||||
|
.stream()
|
||||||
|
.findFirst()
|
||||||
|
.orElse(Screen.getPrimary());
|
||||||
Rectangle2D bounds = screen.getVisualBounds();
|
Rectangle2D bounds = screen.getVisualBounds();
|
||||||
x = bounds.getMinX() + (bounds.getWidth() - window.getWidth()) / 2;
|
x = bounds.getMinX() + (bounds.getWidth() - window.getWidth()) / 2;
|
||||||
y = bounds.getMinY() + (bounds.getHeight() - window.getHeight()) / 2;
|
y = bounds.getMinY() + (bounds.getHeight() - window.getHeight()) / 2;
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ package org.cryptomator.ui.updatereminder;
|
|||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import dagger.Subcomponent;
|
import dagger.Subcomponent;
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.time.LocalDate;
|
|
||||||
|
|
||||||
@UpdateReminderScoped
|
@UpdateReminderScoped
|
||||||
@Subcomponent(modules = {UpdateReminderModule.class})
|
@Subcomponent(modules = {UpdateReminderModule.class})
|
||||||
@@ -20,19 +18,16 @@ public interface UpdateReminderComponent {
|
|||||||
@FxmlScene(FxmlFile.UPDATE_REMINDER)
|
@FxmlScene(FxmlFile.UPDATE_REMINDER)
|
||||||
Lazy<Scene> updateReminderScene();
|
Lazy<Scene> updateReminderScene();
|
||||||
|
|
||||||
Settings settings();
|
default void showUpdateReminderWindow() {
|
||||||
|
Stage stage = window();
|
||||||
default void checkAndShowUpdateReminderWindow() {
|
stage.setScene(updateReminderScene().get());
|
||||||
if (LocalDate.parse(settings().lastUpdateCheck.get()).isBefore(LocalDate.now().minusDays(14)) && !settings().checkForUpdates.getValue()) {
|
stage.sizeToScene();
|
||||||
Stage stage = window();
|
stage.show();
|
||||||
stage.setScene(updateReminderScene().get());
|
|
||||||
stage.sizeToScene();
|
|
||||||
stage.show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subcomponent.Factory
|
@Subcomponent.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
||||||
UpdateReminderComponent create();
|
UpdateReminderComponent create();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,8 +7,7 @@ import org.cryptomator.ui.fxapp.UpdateChecker;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import java.time.LocalDate;
|
import java.time.Instant;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
|
|
||||||
@UpdateReminderScoped
|
@UpdateReminderScoped
|
||||||
public class UpdateReminderController implements FxController {
|
public class UpdateReminderController implements FxController {
|
||||||
@@ -25,22 +24,24 @@ public class UpdateReminderController implements FxController {
|
|||||||
this.updateChecker = updateChecker;
|
this.updateChecker = updateChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
settings.lastUpdateCheckReminder.set(Instant.now());
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
|
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void once() {
|
public void once() {
|
||||||
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
|
|
||||||
updateChecker.checkForUpdatesNow();
|
updateChecker.checkForUpdatesNow();
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void automatically() {
|
public void automatically() {
|
||||||
settings.lastUpdateCheck.set(LocalDate.now().format(DateTimeFormatter.ISO_DATE));
|
|
||||||
updateChecker.checkForUpdatesNow();
|
updateChecker.checkForUpdatesNow();
|
||||||
settings.checkForUpdates.set(true);
|
settings.checkForUpdates.set(true);
|
||||||
window.close();
|
window.close();
|
||||||
|
|||||||
@@ -57,9 +57,14 @@ public class GeneralVaultOptionsController implements FxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void trimVaultNameOnFocusLoss(Observable observable, Boolean wasFocussed, Boolean isFocussed) {
|
private void trimVaultNameOnFocusLoss(Observable observable, Boolean wasFocussed, Boolean isFocussed) {
|
||||||
|
var displayNameSetting = vault.getVaultSettings().displayName;
|
||||||
if (!isFocussed) {
|
if (!isFocussed) {
|
||||||
var trimmed = vaultName.getText().trim();
|
var trimmed = vaultName.getText().trim();
|
||||||
vault.getVaultSettings().displayName.set(trimmed);
|
if (!trimmed.isEmpty()) {
|
||||||
|
displayNameSetting.set(trimmed); //persist changes
|
||||||
|
} else {
|
||||||
|
vaultName.setText(displayNameSetting.get()); //revert changes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@
|
|||||||
|
|
||||||
.label-extra-large {
|
.label-extra-large {
|
||||||
-fx-font-family: 'Open Sans SemiBold';
|
-fx-font-family: 'Open Sans SemiBold';
|
||||||
|
-fx-fill: TEXT_FILL;
|
||||||
-fx-font-size: 1.5em;
|
-fx-font-size: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,31 +182,20 @@
|
|||||||
-fx-border-width: 1px;
|
-fx-border-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title {
|
.main-window .button-bar {
|
||||||
-fx-background-color: CONTROL_BORDER_NORMAL, TITLE_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-background-insets: 0, 0 0 1px 0;
|
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
||||||
|
-fx-border-width: 1px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button {
|
.main-window .button-left {
|
||||||
-fx-pref-height: 30px;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-pref-width: 30px;
|
-fx-border-width: 0 1px 0 0;
|
||||||
-fx-background-color: none;
|
|
||||||
-fx-padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button .glyph-icon {
|
.main-window .button-right {
|
||||||
-fx-fill: white;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
}
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
|
||||||
.main-window .title .button:armed .glyph-icon {
|
|
||||||
-fx-fill: GRAY_8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-window .update-indicator {
|
|
||||||
-fx-background-color: white, RED_5;
|
|
||||||
-fx-background-insets: 1px, 2px;
|
|
||||||
-fx-background-radius: 6px, 5px;
|
|
||||||
-fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -322,23 +312,33 @@
|
|||||||
-fx-fill: transparent;
|
-fx-fill: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button {
|
/*******************************************************************************
|
||||||
-fx-min-height: 40px;
|
* *
|
||||||
-fx-background-color: transparent;
|
* NotificationBar *
|
||||||
-fx-background-insets: 0;
|
* *
|
||||||
-fx-background-radius: 0;
|
******************************************************************************/
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
|
||||||
-fx-border-width: 1px 0 0 0;
|
.notification-label {
|
||||||
-fx-padding: 0;
|
-fx-text-fill: white;
|
||||||
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button:focused {
|
.notification-debug {
|
||||||
-fx-background-color: CONTROL_BORDER_FOCUSED, MAIN_BG;
|
-fx-min-height:24px;
|
||||||
-fx-background-insets: 0, 2px 1px 1px 1px;
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: RED_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button:armed {
|
.notification-update {
|
||||||
-fx-background-color: CONTROL_BG_ARMED;
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: YELLOW_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-support {
|
||||||
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -394,16 +394,6 @@
|
|||||||
-fx-background-color: MUTED_BG;
|
-fx-background-color: MUTED_BG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: These values below are kinda random such that it looks ok. I'm pretty sure there is room for improvement. Additionally, fx-text-fill does not work*/
|
|
||||||
.badge-debug {
|
|
||||||
-fx-font-family: 'Open Sans Bold';
|
|
||||||
-fx-font-size: 1.0em;
|
|
||||||
-fx-background-radius: 8px;
|
|
||||||
-fx-padding: 0.3em 0.55em 0.3em 0.55em;
|
|
||||||
-fx-background-color: RED_5;
|
|
||||||
-fx-background-radius: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Password Strength Indicator *
|
* Password Strength Indicator *
|
||||||
|
|||||||
@@ -177,34 +177,24 @@
|
|||||||
|
|
||||||
/* windows needs an explicit border: */
|
/* windows needs an explicit border: */
|
||||||
.main-window.os-windows {
|
.main-window.os-windows {
|
||||||
-fx-border-color: TITLE_BG;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-border-width: 1px;
|
-fx-border-width: 1px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title {
|
.main-window .button-bar {
|
||||||
-fx-background-color: TITLE_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
|
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
||||||
|
-fx-border-width: 1px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button {
|
.main-window .button-bar .button-left {
|
||||||
-fx-pref-height: 30px;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
-fx-pref-width: 30px;
|
-fx-border-width: 0 1px 0 0;
|
||||||
-fx-background-color: none;
|
|
||||||
-fx-padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button .glyph-icon {
|
.main-window .button-bar .button-right {
|
||||||
-fx-fill: white;
|
-fx-border-color: CONTROL_BORDER_NORMAL;
|
||||||
}
|
-fx-border-width: 0 0 0 1px;
|
||||||
|
|
||||||
.main-window .title .button:armed .glyph-icon {
|
|
||||||
-fx-fill: GRAY_8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-window .update-indicator {
|
|
||||||
-fx-background-color: white, RED_5;
|
|
||||||
-fx-background-insets: 1px, 2px;
|
|
||||||
-fx-background-radius: 6px, 5px;
|
|
||||||
-fx-effect: dropshadow(three-pass-box, rgba(0, 0, 0, 0.8), 2, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -321,23 +311,33 @@
|
|||||||
-fx-fill: transparent;
|
-fx-fill: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button {
|
/*******************************************************************************
|
||||||
-fx-min-height: 40px;
|
* *
|
||||||
-fx-background-color: transparent;
|
* NotificationBar *
|
||||||
-fx-background-insets: 0;
|
* *
|
||||||
-fx-background-radius: 0;
|
******************************************************************************/
|
||||||
-fx-border-color: CONTROL_BORDER_NORMAL transparent transparent transparent;
|
|
||||||
-fx-border-width: 1px 0 0 0;
|
.notification-label {
|
||||||
-fx-padding: 0;
|
-fx-text-fill: white;
|
||||||
|
-fx-font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button:focused {
|
.notification-debug {
|
||||||
-fx-background-color: CONTROL_BORDER_FOCUSED, MAIN_BG;
|
-fx-min-height:24px;
|
||||||
-fx-background-insets: 0, 2px 1px 1px 1px;
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: RED_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button.toolbar-button:armed {
|
.notification-update {
|
||||||
-fx-background-color: CONTROL_BG_ARMED;
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: YELLOW_5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-support {
|
||||||
|
-fx-min-height:24px;
|
||||||
|
-fx-max-height:24px;
|
||||||
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -393,16 +393,6 @@
|
|||||||
-fx-background-color: MUTED_BG;
|
-fx-background-color: MUTED_BG;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note: These values below are kinda random such that it looks ok. I'm pretty sure there is room for improvement. Additionally, fx-text-fill does not work*/
|
|
||||||
.badge-debug {
|
|
||||||
-fx-font-family: 'Open Sans Bold';
|
|
||||||
-fx-font-size: 1.0em;
|
|
||||||
-fx-background-radius: 8px;
|
|
||||||
-fx-padding: 0.3em 0.55em 0.3em 0.55em;
|
|
||||||
-fx-background-color: RED_5;
|
|
||||||
-fx-background-radius: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Password Strength Indicator *
|
* Password Strength Indicator *
|
||||||
|
|||||||
@@ -9,86 +9,97 @@
|
|||||||
<?import javafx.scene.control.Hyperlink?>
|
<?import javafx.scene.control.Hyperlink?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.TextArea?>
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.Group?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.shape.Circle?>
|
<?import javafx.scene.shape.Circle?>
|
||||||
<VBox xmlns:fx="http://javafx.com/fxml"
|
<HBox xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:controller="org.cryptomator.ui.error.ErrorController"
|
fx:controller="org.cryptomator.ui.error.ErrorController"
|
||||||
|
minWidth="450"
|
||||||
|
minHeight="450"
|
||||||
prefWidth="450"
|
prefWidth="450"
|
||||||
prefHeight="450"
|
prefHeight="450"
|
||||||
spacing="18"
|
spacing="12">
|
||||||
alignment="TOP_CENTER">
|
|
||||||
<padding>
|
<padding>
|
||||||
<Insets topRightBottomLeft="24"/>
|
<Insets topRightBottomLeft="24"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<Group>
|
||||||
<HBox spacing="12" VBox.vgrow="NEVER">
|
<StackPane>
|
||||||
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
|
<padding>
|
||||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
<Insets topRightBottomLeft="6"/>
|
||||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
</padding>
|
||||||
</StackPane>
|
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||||
<VBox spacing="6" HBox.hgrow="ALWAYS">
|
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
||||||
<FormattedLabel styleClass="label-extra-large" format="%error.message" arg1="${controller.errorCode}"/>
|
</StackPane>
|
||||||
<VBox visible="${!controller.askedForLookupDatabasePermission}" managed="${!controller.askedForLookupDatabasePermission}">
|
</Group>
|
||||||
<Label text="%error.lookupPermissionMessage" wrapText="true"/>
|
<VBox HBox.hgrow="ALWAYS">
|
||||||
<Region VBox.vgrow="ALWAYS" minHeight="18"/>
|
<FormattedLabel styleClass="label-extra-large" format="%error.message" arg1="${controller.errorCode}">
|
||||||
<ButtonBar buttonMinWidth="120" buttonOrder="+NY">
|
<padding>
|
||||||
<buttons>
|
<Insets bottom="6" top="6"/>
|
||||||
<Button text="%error.dismiss" ButtonBar.buttonData="NO" onAction="#dismiss"/>
|
</padding>
|
||||||
<Button text="%error.lookUpSolution" ButtonBar.buttonData="YES" defaultButton="true" onAction="#lookUpSolution"/>
|
</FormattedLabel>
|
||||||
</buttons>
|
|
||||||
</ButtonBar>
|
|
||||||
</VBox>
|
|
||||||
<VBox visible="${controller.askedForLookupDatabasePermission}" managed="${controller.askedForLookupDatabasePermission}">
|
|
||||||
<FontAwesome5Spinner glyphSize="24" visible="${controller.isLoadingHttpResponse}" managed="${controller.isLoadingHttpResponse}"/>
|
|
||||||
<VBox visible="${!controller.isLoadingHttpResponse}" managed="${!controller.isLoadingHttpResponse}">
|
|
||||||
<Label text="%error.existingSolutionDescription" wrapText="true" visible="${controller.errorSolutionFound}" managed="${controller.errorSolutionFound}"/>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.solution" onAction="#showSolution" contentDisplay="LEFT" visible="${controller.errorSolutionFound}" managed="${controller.errorSolutionFound}">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
|
||||||
</graphic>
|
|
||||||
</Hyperlink>
|
|
||||||
<Label text="%error.description" wrapText="true" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}"/>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.lookup" onAction="#searchError" contentDisplay="LEFT" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
|
||||||
</graphic>
|
|
||||||
</Hyperlink>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.report" onAction="#reportError" contentDisplay="LEFT" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
|
||||||
</graphic>
|
|
||||||
</Hyperlink>
|
|
||||||
</VBox>
|
|
||||||
</VBox>
|
|
||||||
</VBox>
|
|
||||||
</HBox>
|
|
||||||
<VBox spacing="6" VBox.vgrow="ALWAYS">
|
|
||||||
<HBox>
|
|
||||||
<Label text="%error.technicalDetails"/>
|
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="12"/>
|
|
||||||
</graphic>
|
|
||||||
</Hyperlink>
|
|
||||||
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copied" onAction="#copyDetails" contentDisplay="LEFT" visible="${controller.copiedDetails}" managed="${controller.copiedDetails}">
|
|
||||||
<graphic>
|
|
||||||
<FontAwesome5IconView glyph="CHECK" glyphSize="12"/>
|
|
||||||
</graphic>
|
|
||||||
</Hyperlink>
|
|
||||||
</HBox>
|
|
||||||
<TextArea VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
|
|
||||||
</VBox>
|
|
||||||
|
|
||||||
|
<VBox visible="${!controller.askedForLookupDatabasePermission}" managed="${!controller.askedForLookupDatabasePermission}">
|
||||||
|
<Label text="%error.lookupPermissionMessage" wrapText="true"/>
|
||||||
|
<Region minHeight="12"/>
|
||||||
|
<ButtonBar buttonMinWidth="120" buttonOrder="+NY">
|
||||||
|
<buttons>
|
||||||
|
<Button text="%error.dismiss" ButtonBar.buttonData="NO" onAction="#dismiss"/>
|
||||||
|
<Button text="%error.lookUpSolution" ButtonBar.buttonData="YES" defaultButton="true" onAction="#lookUpSolution"/>
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</VBox>
|
||||||
|
<VBox visible="${controller.askedForLookupDatabasePermission}" managed="${controller.askedForLookupDatabasePermission}">
|
||||||
|
<FontAwesome5Spinner glyphSize="24" visible="${controller.isLoadingHttpResponse}" managed="${controller.isLoadingHttpResponse}"/>
|
||||||
|
<VBox visible="${!controller.isLoadingHttpResponse}" managed="${!controller.isLoadingHttpResponse}">
|
||||||
|
<Label text="%error.existingSolutionDescription" wrapText="true" visible="${controller.errorSolutionFound}" managed="${controller.errorSolutionFound}"/>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.solution" onAction="#showSolution" contentDisplay="LEFT" visible="${controller.errorSolutionFound}" managed="${controller.errorSolutionFound}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
</Hyperlink>
|
||||||
|
<Label text="%error.description" wrapText="true" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}"/>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.lookup" onAction="#searchError" contentDisplay="LEFT" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
</Hyperlink>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%error.hyperlink.report" onAction="#reportError" contentDisplay="LEFT" visible="${!controller.errorSolutionFound}" managed="${!controller.errorSolutionFound}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="LINK" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
</Hyperlink>
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
||||||
|
<Region minHeight="12"/>
|
||||||
|
<HBox>
|
||||||
|
<padding>
|
||||||
|
<Insets top="6" bottom="6"/>
|
||||||
|
</padding>
|
||||||
|
<Label text="%error.technicalDetails"/>
|
||||||
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copy" onAction="#copyDetails" contentDisplay="LEFT" visible="${!controller.copiedDetails}" managed="${!controller.copiedDetails}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="CLIPBOARD" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
</Hyperlink>
|
||||||
|
<Hyperlink styleClass="hyperlink-underline" text="%generic.button.copied" onAction="#copyDetails" contentDisplay="LEFT" visible="${controller.copiedDetails}" managed="${controller.copiedDetails}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="CHECK" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
</Hyperlink>
|
||||||
|
</HBox>
|
||||||
|
<TextArea VBox.vgrow="ALWAYS" text="${controller.detailText}" prefRowCount="5" editable="false"/>
|
||||||
|
|
||||||
|
<Region minHeight="18"/>
|
||||||
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
|
<ButtonBar buttonMinWidth="120" buttonOrder="B+C">
|
||||||
<buttons>
|
<buttons>
|
||||||
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back" visible="${controller.previousScenePresent}"/>
|
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" onAction="#back" visible="${controller.previousScenePresent}"/>
|
||||||
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#close"/>
|
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" onAction="#close"/>
|
||||||
</buttons>
|
</buttons>
|
||||||
</ButtonBar>
|
</ButtonBar>
|
||||||
</children>
|
</VBox>
|
||||||
</VBox>
|
</HBox>
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
<children>
|
<children>
|
||||||
<HBox VBox.vgrow="ALWAYS">
|
<HBox VBox.vgrow="ALWAYS">
|
||||||
<VBox alignment="CENTER" minWidth="175" maxWidth="175">
|
<VBox alignment="CENTER" minWidth="175" maxWidth="175">
|
||||||
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" smooth="true" cache="true">
|
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" cache="true">
|
||||||
<Image url="@../img/logo.png"/>
|
<Image url="@../img/logo128.png"/>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
</VBox>
|
</VBox>
|
||||||
<VBox HBox.hgrow="ALWAYS" alignment="CENTER">
|
<VBox HBox.hgrow="ALWAYS" alignment="CENTER">
|
||||||
|
|||||||
@@ -3,17 +3,31 @@
|
|||||||
<?import javafx.scene.control.SplitPane?>
|
<?import javafx.scene.control.SplitPane?>
|
||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import org.cryptomator.ui.controls.NotificationBar?>
|
||||||
|
|
||||||
<StackPane xmlns:fx="http://javafx.com/fxml"
|
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||||
xmlns="http://javafx.com/javafx"
|
xmlns="http://javafx.com/javafx"
|
||||||
fx:id="root"
|
fx:id="root"
|
||||||
fx:controller="org.cryptomator.ui.mainwindow.MainWindowController"
|
fx:controller="org.cryptomator.ui.mainwindow.MainWindowController"
|
||||||
styleClass="main-window">
|
styleClass="main-window">
|
||||||
<VBox minWidth="650">
|
<VBox minWidth="600">
|
||||||
<fx:include source="main_window_title.fxml" VBox.vgrow="NEVER"/>
|
<NotificationBar onMouseClicked="#showUpdatePreferences"
|
||||||
|
text="%main.notification.updateAvailable"
|
||||||
|
dismissable="true"
|
||||||
|
notify="${controller.updateAvailable}"
|
||||||
|
styleClass="notification-update"/>
|
||||||
|
<NotificationBar onMouseClicked="#showContributePreferences"
|
||||||
|
text="%main.notification.support"
|
||||||
|
dismissable="true"
|
||||||
|
notify="${!controller.licenseValid}"
|
||||||
|
styleClass="notification-support"/>
|
||||||
<SplitPane dividerPositions="0.33" orientation="HORIZONTAL" VBox.vgrow="ALWAYS">
|
<SplitPane dividerPositions="0.33" orientation="HORIZONTAL" VBox.vgrow="ALWAYS">
|
||||||
<fx:include source="vault_list.fxml" SplitPane.resizableWithParent="false"/>
|
<fx:include source="vault_list.fxml" SplitPane.resizableWithParent="false"/>
|
||||||
<fx:include source="vault_detail.fxml" SplitPane.resizableWithParent="true"/>
|
<fx:include source="vault_detail.fxml" SplitPane.resizableWithParent="true"/>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
|
<NotificationBar onMouseClicked="#showGeneralPreferences"
|
||||||
|
text="DEBUG MODE"
|
||||||
|
styleClass="notification-debug"
|
||||||
|
notify="${controller.debugModeEnabled}"/>
|
||||||
</VBox>
|
</VBox>
|
||||||
<fx:include source="main_window_resize.fxml"/>
|
|
||||||
</StackPane>
|
</StackPane>
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
<?import javafx.scene.Cursor?>
|
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
|
||||||
<?import javafx.scene.layout.Region?>
|
|
||||||
<AnchorPane xmlns:fx="http://javafx.com/fxml"
|
|
||||||
xmlns="http://javafx.com/javafx"
|
|
||||||
fx:controller="org.cryptomator.ui.mainwindow.ResizeController"
|
|
||||||
nodeOrientation="LEFT_TO_RIGHT"
|
|
||||||
pickOnBounds="false">
|
|
||||||
<fx:define>
|
|
||||||
<Cursor fx:id="nwResize" fx:constant="NW_RESIZE"/>
|
|
||||||
<Cursor fx:id="neResize" fx:constant="NE_RESIZE"/>
|
|
||||||
<Cursor fx:id="nsResize" fx:constant="N_RESIZE"/>
|
|
||||||
<Cursor fx:id="ewResize" fx:constant="E_RESIZE"/>
|
|
||||||
<Cursor fx:id="default" fx:constant="DEFAULT"/>
|
|
||||||
</fx:define>
|
|
||||||
|
|
||||||
<Region fx:id="tlResizer" cursor="${nwResize}" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeTopLeft" onMouseReleased="#savePositionalSettings" AnchorPane.topAnchor="0" AnchorPane.leftAnchor="0"/>
|
|
||||||
<Region fx:id="trResizer" cursor="${neResize}" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeTopRight" onMouseReleased="#savePositionalSettings" AnchorPane.topAnchor="0" AnchorPane.rightAnchor="0"/>
|
|
||||||
<Region fx:id="blResizer" cursor="${neResize}" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeBottomLeft" onMouseReleased="#savePositionalSettings" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"/>
|
|
||||||
<Region fx:id="brResizer" cursor="${nwResize}" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeBottomRight" onMouseReleased="#savePositionalSettings" AnchorPane.bottomAnchor="0" AnchorPane.rightAnchor="0"/>
|
|
||||||
<Region fx:id="tResizer" cursor="${nsResize}" prefWidth="0" prefHeight="6" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeTop" onMouseReleased="#savePositionalSettings" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="0.0"/>
|
|
||||||
<Region fx:id="rResizer" cursor="${ewResize}" prefWidth="6" prefHeight="0" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeRight" onMouseReleased="#savePositionalSettings" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="10.0"/>
|
|
||||||
<Region fx:id="bResizer" cursor="${nsResize}" prefWidth="6" prefHeight="6" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeBottom" onMouseReleased="#savePositionalSettings" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0"/>
|
|
||||||
<Region fx:id="lResizer" cursor="${ewResize}" prefWidth="6" prefHeight="6" maxWidth="-Infinity" maxHeight="-Infinity" visible="${controller.showResizingArrows}" managed="${controller.showResizingArrows}" onMouseDragged="#resizeLeft" onMouseReleased="#savePositionalSettings" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="10.0"/>
|
|
||||||
<Region fx:id="lDefaultRegion" cursor="${default}" prefWidth="1" prefHeight="1" maxWidth="-Infinity" maxHeight="-Infinity" AnchorPane.bottomAnchor="-1" AnchorPane.leftAnchor="-1" AnchorPane.topAnchor="-1"/>
|
|
||||||
<Region fx:id="tDefaultRegion" cursor="${default}" prefWidth="1" prefHeight="1" maxWidth="-Infinity" maxHeight="-Infinity" AnchorPane.leftAnchor="-1" AnchorPane.topAnchor="-1" AnchorPane.rightAnchor="-1"/>
|
|
||||||
<Region fx:id="rDefaultRegion" cursor="${default}" prefWidth="1" prefHeight="1" maxWidth="-Infinity" maxHeight="-Infinity" AnchorPane.topAnchor="-1" AnchorPane.rightAnchor="-1" AnchorPane.bottomAnchor="-1"/>
|
|
||||||
<Region fx:id="bDefaultRegion" cursor="${default}" prefWidth="1" prefHeight="1" maxWidth="-Infinity" maxHeight="-Infinity" AnchorPane.rightAnchor="-1" AnchorPane.bottomAnchor="-1" AnchorPane.leftAnchor="-1"/>
|
|
||||||
|
|
||||||
</AnchorPane>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user