Compare commits

...
Author SHA1 Message Date
JaniruTEC 0dca0d6f1c Fixed #2270 2022-06-30 02:28:42 +02:00
Tobias HagemannandGitHub 8043a4ea10 Update README.md 2022-06-22 10:42:59 +02:00
Tobias HagemannandGitHub e5cb271e28 Update README.md 2022-06-22 10:42:13 +02:00
Armin Schrenk 8c65bd19c2 Make FUSE the default volume impl 2022-06-13 10:00:14 +02:00
JaniruTECandGitHub e6ff7a6d88 Merge pull request #2259 from cryptomator/fix/2258-catching-executors
Added catching executors
2022-05-27 18:24:55 +02:00
Sebastian StenzelandGitHub aca55734b0 Use Xcode Notarization Action (#2267)
[ci skip]
2022-05-27 12:21:13 +02:00
JaniruTEC f740a93b6c Applied suggestions from code review
Changed signature of afterExecuteTask
See: https://github.com/cryptomator/cryptomator/pull/2259/files#r875548549

--

Changed name of "afterExecute0" to "afterExecuteInternal"
See: https://github.com/cryptomator/cryptomator/pull/2259/files#r875551994
2022-05-27 01:48:11 +02:00
e9a71827ed Apply suggestions from code review
Establishing symmetry with `afterExecuteTask`

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-27 01:36:10 +02:00
Sebastian Stenzel dc3f80ffc9 fix build 2022-05-26 12:04:33 +02:00
Tobias Hagemann 47f7c437fe added separate definition for green tones used for password strength indicator 2022-05-23 13:39:07 +02:00
Tobias Hagemann 15e46f8ca5 Merge branch 'feature/optimize-logo' into develop 2022-05-23 13:17:38 +02:00
Tobias Hagemann 5b13553866 cleanup
[ci skip]
2022-05-23 13:17:02 +02:00
Tobias Hagemann e14d1f4028 cleaned up css files
[ci skip]
2022-05-23 13:15:24 +02:00
Jan-Peter Klein 8c52c740d3 removed /bot/bot.png 2022-05-20 11:30:23 +02:00
Jan-Peter Klein 1d70eecbb7 added title-logo file and rearranged files 2022-05-20 11:24:51 +02:00
3d95276a7f Feature/2200 illegal use of builders (#2262)
* Replaced Builder in VaultComponent with Factory
* migrate VaultOptionsComponent

Co-authored-by: JaniruTEC <JaniruTEC@gmail.com>
2022-05-19 22:07:56 +02:00
Tobias Hagemann 5233e6b690 improved cleanup in manual build script for macos
[ci skip]
2022-05-17 18:33:02 +02:00
JaniruTEC 0040c8a5f8 Added catching executors 2022-05-17 16:30:23 +02:00
Armin Schrenk 4989eacaf9 Fixes #2256 2022-05-16 12:58:36 +02:00
Sebastian Stenzel cd709260ce ignore autogenerated file
[ci skip]
2022-05-13 15:48:15 +02:00
Sebastian Stenzel 87202453b1 added Swahili to language picker
[ci skip]
2022-05-13 15:43:43 +02:00
4786193731 New Crowdin updates (#2212)
[ci skip]

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-13 15:28:33 +02:00
90f523bee8 parameterize patchWebDAV.ps1 and add default values to .bat file (#2251)
Co-authored-by: Armin Schrenk <armin.schrenk@skymatic.de>
2022-05-13 15:11:41 +02:00
Tobias Hagemann 78bb996072 updated integrations-mac dependency 2022-05-13 13:42:40 +02:00
Armin Schrenk 822963e1a9 bump cryptofs
Fixes #2238
2022-05-13 13:17:11 +02:00
Tobias HagemannandGitHub 773a5d3570 Merge pull request #2244 from cryptomator/feature/parameterize-mac-build-script
Parameterized macOS build script
2022-05-12 16:19:14 +02:00
Armin SchrenkandGitHub acc5277258 Merge pull request #2242 from cryptomator/feature/parameterize-win-build-script
Parameterize local windows build script
2022-05-12 16:17:34 +02:00
Armin SchrenkandGitHub 4ecb98a5e6 Merge pull request #2245 from cryptomator/feature/location-preset-enum
Enumeenize well-known local cloud storage paths
2022-05-12 16:14:43 +02:00
Armin Schrenk 75093f1c09 Make lauchner class configurable 2022-05-12 16:12:20 +02:00
Tobias Hagemann 0351595243 parameterized MODULE_AND_MAIN_CLASS 2022-05-12 16:07:47 +02:00
Armin SchrenkandSebastian Stenzel 5ab74affb5 Refactor LocationPreset enum:
* use static inner class
* add getDisplayName method
* make instance variables final

Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-05-12 13:47:57 +02:00
Sebastian Stenzel 8230b74c78 moved file picker filter glob to Constants.java 2022-05-12 13:17:33 +02:00
Armin Schrenk 5832e8cd49 Apply suggestions from code review
Co-authored by: Tobias Hagemann <tobihagemann@users.noreply.github.com>
2022-05-12 12:15:16 +02:00
Tobias Hagemann 0220af24d0 actually use vault config filename from constants when creating vault 2022-05-12 10:51:00 +02:00
Armin Schrenk 3e6d81ef1b Use parameter for main jar glob pattern 2022-05-12 09:15:04 +02:00
Tobias Hagemann 852daaa88e parameterized mac build script 2022-05-12 09:14:10 +02:00
Armin Schrenk 7dfc4d9ded allow custom name for c9r/c9s icon file 2022-05-12 09:14:03 +02:00
Armin Schrenk 98002f63dc Use enum for well-known local cloud storage paths and move class to commons package 2022-05-12 08:45:03 +02:00
Armin Schrenk 80696972cb allow upgrade-uuid to be customized in build script 2022-05-11 13:00:32 +02:00
Sebastian Stenzel a3d4eb6048 adjust build to integrations-win 1.1.1 2022-05-11 12:51:48 +02:00
Armin Schrenk 0f88e6c2fe fix errors 2022-05-11 12:48:56 +02:00
Armin Schrenk 3524f273de Merge branch 'develop' into feature/parameterize-win-build-script
# Conflicts:
#	dist/win/build.ps1
2022-05-11 12:48:31 +02:00
Armin Schrenk fb3d64c43f update integrations-win dependency 2022-05-11 12:41:33 +02:00
Armin Schrenk 90d43f28d9 eradicate last hard coded app name occurences in build script 2022-05-11 12:25:53 +02:00
Armin Schrenk a29f10a504 refactor build scipt to be easiert customizable 2022-05-11 12:22:29 +02:00
Armin Schrenk 6702747652 separate cryptomator labels and asset paths from installer file 2022-05-10 18:35:04 +02:00
Armin Schrenk a8b920698d Generify error message in installer 2022-05-10 16:32:59 +02:00
Sebastian Stenzel 0b2685f271 adjusted .gitignore to latest IntelliJ IDEA standards
see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems

[ci skip]
2022-05-10 15:23:02 +02:00
Sebastian Stenzel 120be431ee prepare Cryptomator to eventually change the update check URL 2022-05-10 11:36:40 +02:00
Armin Schrenk f50eb1f047 Revert "Reset webdavport field if changes are not applied and focus lost"
This reverts commit c6e9e33feb.
2022-05-09 13:06:15 +02:00
Armin SchrenkandGitHub 32d6661a72 Merge pull request #2235 from cryptomator/feature/automate-winget
Add automatic release to winget to msi/exe ci job
2022-05-06 17:44:33 +02:00
Armin Schrenk f57d8dab6d instead of defining pwsh as default for winget job, only use it in specific step 2022-05-06 17:34:04 +02:00
Armin Schrenk 5ba0e674f2 Add automatic release to winget to msi/exe ci job 2022-05-06 13:13:45 +02:00
Sebastian Stenzel 55f67785cd don't cause the job to fail if the upload fails
[ci skip]
2022-05-05 09:05:05 +02:00
Sebastian Stenzel 6d082330e6 replace failing curl with http-request-action
[ci skip]
2022-05-05 08:00:21 +02:00
Sebastian Stenzel 0fdc5b2826 fix duplicate arm64 dmg and duplicate appimage
[ci skip]
2022-05-05 07:50:06 +02:00
Sebastian Stenzel 31f77e62ea configurable time interval 2022-05-05 07:44:28 +02:00
Sebastian Stenzel 3435a6701a round timestamp to latest 15min mark 2022-05-05 07:41:36 +02:00
Sebastian Stenzel d4367b97a6 adjust interval to 900s, when reporting stats every 15min 2022-05-05 07:39:05 +02:00
Sebastian StenzelandGitHub 1151157dff Report Download Stats (#2230)
[ci skip]
2022-05-05 07:32:19 +02:00
Julian Raufelder e9c58ac3d5 Merge branch 'hotfix/1.6.10' into develop 2022-05-03 16:38:51 +02:00
Julian Raufelder 12ef32835c Prepare 1.6.10
[ci skip]
2022-05-03 16:15:33 +02:00
Julian Raufelder eae5f4d870 Fix truncation if new size is larger than the current file size when FUSE is used to mount the vault
Fixes #2218
2022-05-03 16:10:41 +02:00
JaniruTECandGitHub 30e1c989d0 Fixed typo 2022-05-01 00:46:36 +02:00
Armin Schrenk c6e9e33feb Reset webdavport field if changes are not applied and focus lost 2022-04-28 12:00:06 +02:00
Armin Schrenk 62c7f95f7c removed logging of unused property 2022-04-28 11:54:51 +02:00
Armin Schrenk a304427973 Remove last reference of "donationkey" 2022-04-28 11:53:54 +02:00
Sebastian Stenzel c04597eca5 Merge branch 'main' into develop
[ci skip]
2022-04-27 15:52:15 +02:00
Sebastian Stenzel ba1945ebb8 Merge branch 'release/1.6.9' 2022-04-27 15:51:50 +02:00
Sebastian Stenzel e9551a076d Merge branch 'develop' into release/1.6.9 2022-04-27 15:30:59 +02:00
Sebastian StenzelandGitHub ccd3da3b09 Merge pull request #2096 from cryptomator/feature/integrations-api-1.1.0
Integrations API 1.1.0
2022-04-27 15:29:47 +02:00
Sebastian Stenzel 5e06fa7385 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	pom.xml
2022-04-27 15:23:38 +02:00
Sebastian Stenzel c3969f2f33 bumped integrations version 2022-04-27 15:19:54 +02:00
Sebastian Stenzel 0a8cc7ed1e Merge branch 'develop' into release/1.6.9 2022-04-27 13:19:41 +02:00
CryptobotandGitHub 74f5be1605 New Crowdin updates (#2146)
[ci skip]
2022-04-27 13:19:03 +02:00
Sebastian Stenzel 3c376c4bf6 add -Dsun.java2d.metal=true, which fixes #2194 2022-04-27 13:18:25 +02:00
Armin SchrenkandGitHub fedb62d99a Merge pull request #2175 from cryptomator/feature/binary-stylesheets
Prefer binary stylesheets
2022-04-27 13:06:18 +02:00
Armin SchrenkandGitHub 8f97235b9a Merge pull request #2189 from cryptomator/feature/on-the-fly-license
Generate app license on release/ installer build and remove commited third party file
2022-04-27 13:05:53 +02:00
Julian Raufelder d127121007 Update metainfo.xml to 1.6.9
[ci skip]
2022-04-26 19:51:43 +02:00
Armin Schrenk d6a55d3006 preparing 1.6.9 2022-04-26 18:14:40 +02:00
Armin Schrenk d3d0cef2bb bumping dependencies 2022-04-26 18:07:37 +02:00
Armin Schrenk c514dc6235 fixes #2205 2022-04-26 17:58:37 +02:00
Armin Schrenk 98b3b14e6a Reverted bundle license to show links again and adjusted license template
* only those are depicted as clickable http links
2022-04-26 17:48:05 +02:00
Armin Schrenk 835a5592fc use pwsh for license generation to circumvent slash problem in url 2022-04-22 17:18:28 +02:00
Armin Schrenk c429de6a66 third slash's the charm 2022-04-22 16:52:49 +02:00
Armin Schrenk afe7737129 wrong usage of github workspace var 2022-04-22 16:25:00 +02:00
Armin Schrenk 94b3ea37d6 use absolute path in workflow 2022-04-22 16:19:09 +02:00
Tobias Hagemann 4d7e16d58e updated license templates 2022-04-22 12:34:25 +02:00
Armin Schrenk 2f17d80fe6 Revert "removed license dir, renamed merge file and adjusted paths"
This reverts commit d053367f83.
2022-04-22 11:51:22 +02:00
Armin Schrenk fbc598fed7 Reordered steps in vault creation and renamed methods 2022-04-22 10:40:18 +02:00
Armin Schrenk d053367f83 removed license dir, renamed merge file and adjusted paths 2022-04-22 09:49:04 +02:00
Armin Schrenk 9f5c31e4e7 Move app specific license template to resources directory 2022-04-21 14:21:40 +02:00
Armin Schrenk 68d4b440e1 use spaces instead of tabs in app license file 2022-04-20 17:47:50 +02:00
Armin Schrenk bcfba21900 Create third-party-license file in target directory, use plugin defaults to let it placed inside jar 2022-04-20 17:46:58 +02:00
Armin Schrenk c1801e3d22 fixed typo 2022-04-20 12:12:25 +02:00
Armin Schrenk bff0194f3e Fixed wrong file format and url of lcense merge file 2022-04-20 12:12:00 +02:00
Armin Schrenk 75f96eb08f removing static third-party-licenses file 2022-04-20 10:37:13 +02:00
Armin SchrenkandSebastian Stenzel 4d1d6f449e Apply suggestions from code review
Co-authored-by: Sebastian Stenzel <overheadhunter@users.noreply.github.com>
2022-04-20 10:31:25 +02:00
Armin Schrenk eb0a2c1267 On macOS show example in dark mode when adding existing vault 2022-04-19 17:34:12 +02:00
Armin Schrenk 51d715caa1 apply same config to license generation 2022-04-19 16:47:33 +02:00
Sebastian Stenzel b55bd8075c relax stale bot
[ci skip]
2022-04-18 09:18:51 +02:00
Sebastian StenzelandGitHub d368b01038 Merge pull request #2105 from cryptomator/feature/gtk2-launcher
Add additional GTK2 launcher to AppImage, fixes #1432
2022-04-18 08:24:13 +02:00
Tobias Hagemann dc3894290b updated license template for macos, fixed manual build script 2022-04-14 13:52:10 +02:00
Armin Schrenk 597bbdbdb8 for mac build generate license on the fly
References 7172462b4b
2022-04-13 17:53:44 +02:00
Sebastian Stenzel 2b255ed101 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
2022-04-13 07:21:59 +02:00
Sebastian Stenzel 9ece1f66a1 bumped API to 1.1.0-rc1 2022-04-13 07:16:19 +02:00
JaniruTECandGitHub e71b375437 Added "--version" (short: "-v")
Merge pull request #2064 from cryptomator/1996-cli-version
2022-04-13 00:57:08 +02:00
JaniruTEC bcb2b088ba Applied suggestions from code review 2022-04-13 00:46:11 +02:00
JaniruTEC f123dff0cd Merge remote-tracking branch 'origin/develop' into 1996-cli-version
# Conflicts:
#	src/main/java/org/cryptomator/launcher/Cryptomator.java
2022-04-12 17:44:13 +02:00
Sebastian Stenzel 0ace5bb42b fix copy-paste error in log message 2022-04-11 12:34:50 +02:00
Sebastian Stenzel b656b591ed prefer .bss over .css 2022-04-11 12:32:07 +02:00
Sebastian Stenzel 564370ec7a compile .css into .bss file 2022-04-11 12:31:57 +02:00
Tobias HagemannandGitHub 7dd0f0cb31 Merge pull request #2173 from cryptomator/feature/preferences-interface
Moved interface preferences to its own tab
2022-04-11 10:49:14 +02:00
Tobias Hagemann 79e1285b38 increased margins and spacings even further 2022-04-11 10:48:47 +02:00
Sebastian Stenzel 9c26d17733 log durations of application start 2022-04-11 09:36:34 +02:00
Sebastian Stenzel 69f3a2bd5a log errors when showing main window 2022-04-11 09:35:35 +02:00
Sebastian Stenzel 9a9ef6c583 updated license file
[ci skip]
2022-04-11 09:23:53 +02:00
Sebastian Stenzel a8e73350a6 renamed translation keys 2022-04-09 15:30:16 +02:00
Sebastian Stenzel c8e131c49f reordered controls 2022-04-09 15:25:27 +02:00
Sebastian Stenzel 3b8f2adedf increased margins and spacings 2022-04-09 15:18:44 +02:00
Sebastian Stenzel 9b00cd923c moved interface settings to separate preferences tab 2022-04-09 15:13:53 +02:00
Sebastian Stenzel 12d38335d8 increase width of preferences window, which should fix #2107 2022-04-09 15:05:05 +02:00
Sebastian StenzelandGitHub 752e61219c Merge pull request #2161 from cryptomator/feature/check-vaultconfig-sig-during-key-recovery
Prevent key recovery for foreign vault
2022-04-05 12:03:12 +02:00
Sebastian Stenzel d308ee626a more descriptive name
[ci skip]
2022-04-05 09:27:44 +02:00
Sebastian Stenzel d861ef0d22 Make use of extended validation to prevent applying a recovery key that didn't sign the vault config 2022-04-05 09:21:33 +02:00
Sebastian Stenzel 78a50548ab optionally allow extended key validation in RecoveryKeyFactory#validateRecoveryKey 2022-04-05 09:19:58 +02:00
Sebastian Stenzel 4c363a9abc dealing with deprecation 2022-04-05 08:14:14 +02:00
Sebastian Stenzel 9ed7438f05 bump zxcvbn4j version, fixes #979 2022-04-05 08:00:05 +02:00
Sebastian Stenzel 5a4f714c93 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
2022-04-05 07:49:25 +02:00
Sebastian Stenzel c03bc9ed0b removed unused imports
[ci skip]
2022-04-05 07:47:54 +02:00
Sebastian Stenzel 7c772e2767 removed user.language=en and jdk.gtk.version=2 flags from run configurations
[ci skip]
2022-04-05 07:46:17 +02:00
Sebastian StenzelandGitHub 3136e22414 Merge pull request #2150 from cryptomator/feature/language-switcher 2022-04-04 20:59:23 +02:00
Sebastian Stenzel 508b9f5c64 test if bundle for supported locales exist and are not empty 2022-04-04 15:57:01 +02:00
Sebastian Stenzel d10c8fcf17 amend settings deserialization test 2022-04-04 15:13:27 +02:00
Sebastian Stenzel ceb3cbc43f remove unused imports 2022-04-04 15:10:35 +02:00
Sebastian Stenzel ec909ce723 rethrow AWTException as TrayMenuException 2022-04-04 15:10:24 +02:00
Sebastian Stenzel 2ce7e84e3c Merge branch 'develop' into feature/gtk2-launcher 2022-04-04 06:58:07 +02:00
Sebastian Stenzel d7da78fc07 adjust to latest API 2022-04-03 15:26:04 +02:00
Sebastian Stenzel e93e31e745 Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	pom.xml
#	src/main/java/org/cryptomator/ui/launcher/UiLauncherModule.java
#	src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
#	src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
#	src/main/resources/license/THIRD-PARTY.txt
2022-04-03 12:53:08 +02:00
Sebastian Stenzel 5dc8fd2582 made constant final 2022-04-03 12:25:15 +02:00
Sebastian Stenzel 77d81acb1e added "English" as first option 2022-04-02 15:54:37 +02:00
Sebastian Stenzel 71d346eddd apply chosen language at application start 2022-04-02 12:17:10 +02:00
Sebastian Stenzel d9aa6ae91a display "language" chooser in general preferences 2022-04-02 12:16:57 +02:00
Sebastian Stenzel c130441700 add setting for user-chosen "language" 2022-04-02 12:15:09 +02:00
Sebastian Stenzel 6af016f1fe log errors occuring in async "show window" tasks 2022-04-02 11:51:34 +02:00
Sebastian Stenzel ec2524f6ff use correct StartupWMClass (fixes #1955)
[ci skip]
2022-04-02 10:06:15 +02:00
Sebastian Stenzel 27d4e00210 Merge branch 'develop' into feature/gtk2-launcher 2022-04-02 10:01:25 +02:00
Sebastian StenzelandGitHub 0cd2ecb0b3 Merge pull request #2139 from cryptomator/feature/javafx-launcher
Refactored Launcher Component Graph
2022-04-02 09:59:45 +02:00
Sebastian Stenzel a143ecdcf8 Refactored use of StageFactory 2022-04-01 17:42:29 +02:00
Sebastian Stenzel 2d830cdb31 easier-to-understand if/else for combinations of "startHidden" and "hasTrayIcon"
fixes bug when startHidden was false and hasTrayIcon was true
2022-04-01 15:36:20 +02:00
Sebastian Stenzel 887e0332da changed misleading comment
[ci skip]
2022-04-01 14:50:27 +02:00
Sebastian Stenzel c1504e29c1 added documentation
[ci skip]
2022-04-01 14:48:14 +02:00
Sebastian Stenzel 0bb6e64d83 when "starting hidden", only minimize when there is no tray icon 2022-04-01 14:01:58 +02:00
Sebastian Stenzel c7d1b9dbd6 don't prompt user if quitting directly after app start 2022-03-31 16:32:17 +02:00
Armin Schrenk a4c50da222 replicate naming scheme of win exe installer in CI job to local build 2022-03-31 11:12:50 +02:00
Sebastian StenzelandGitHub a558135fec Rename .exe Installer Bundle (#2145) 2022-03-31 11:03:15 +02:00
Sebastian Stenzel 7df449524a fixed lock workflow 2022-03-31 10:03:02 +02:00
Sebastian Stenzel d475444829 fixed some code smells 2022-03-31 09:59:16 +02:00
Sebastian Stenzel 828f32d3f6 Merge branch 'develop' into feature/gtk2-launcher 2022-03-31 08:57:27 +02:00
Sebastian Stenzel dc6b5774b8 Merge branch 'develop' into feature/javafx-launcher
# Conflicts:
#	pom.xml
2022-03-31 08:55:49 +02:00
Sebastian Stenzel 28db04e621 arm av allowlisting
[ci skip]
2022-03-31 08:37:00 +02:00
Sebastian Stenzel e2184ec009 upload .msi and .exe to AV allowlisting servers 2022-03-31 08:20:49 +02:00
Sebastian Stenzel 5f1f95da74 Merge branch 'main' into develop
[ci skip]
2022-03-31 08:05:53 +02:00
Sebastian Stenzel e907360419 refactored launcher, deleted UiLauncherModule
component graph is now: Main → FxApplicationComponent → Tray, MainWindow, etc
2022-03-29 20:39:46 +02:00
Sebastian Stenzel 73bbcdcca1 start javafx via Application.launch(...) again 2022-03-28 17:52:39 +02:00
Sebastian Stenzel 4abb41aebd Merge branch 'develop' into feature/integrations-api-1.1.0
# Conflicts:
#	src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
2022-03-23 11:02:48 +01:00
Sebastian Stenzel 67e1626de0 Merge branch 'develop' into feature/gtk2-launcher 2022-03-23 10:56:01 +01:00
Sebastian Stenzel 504a384225 fix envsubst 2022-03-18 17:58:21 +01:00
Sebastian Stenzel 550546c4b7 not required in yml multiline strings 2022-03-18 17:42:28 +01:00
Sebastian Stenzel 8dce21ea40 run envsubst as an independent step 2022-03-18 17:31:49 +01:00
Sebastian Stenzel b565f1d0c0 remove unused java binaries from jlinked runtime image 2022-03-18 17:26:28 +01:00
Sebastian Stenzel fab70ef8c9 run Cryptomator binaries instead of java from AppImage launcher .sh 2022-03-18 17:25:04 +01:00
Sebastian Stenzel ad6d5bfae9 resolved class name conflict 2022-03-07 17:42:56 +01:00
Sebastian Stenzel a404740ceb Updated to integrations-api 1.1.0-beta2 2022-03-07 17:35:48 +01:00
JaniruTEC 12a21bbb71 Added "--version" (short: "-v") 2022-02-17 02:39:48 +01:00
204 changed files with 3789 additions and 2331 deletions
+4 -2
View File
@@ -1,11 +1,13 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 180
daysUntilStale: 365
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 30
daysUntilClose: 90
# Issues with these labels will never be considered stale
exemptLabels:
- type:security-issue # never close automatically
- type:feature-request # never close automatically
- type:enhancement # never close automatically
- type:upstream-bug # never close automatically
- state:awaiting-response # handled by different bot
- state:blocked
- state:confirmed
+7 -4
View File
@@ -61,10 +61,16 @@ jobs:
--output runtime
--module-path "${JAVA_HOME}/jmods"
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr
--strip-native-commands
--no-header-files
--no-man-pages
--strip-debug
--compress=1
- name: Prepare additional launcher
run: envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
env:
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
REVISION_NUM: ${{ steps.versions.outputs.revNum }}
- name: Run jpackage
run: >
${JAVA_HOME}/bin/jpackage
@@ -90,12 +96,12 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\""
--java-options "-Dcryptomator.showTrayIcon=false"
--java-options "-Dcryptomator.buildNumber=\"appimage-${{ steps.versions.outputs.revNum }}\""
--add-launcher Cryptomator-gtk2=launcher-gtk2.properties
--resource-dir dist/linux/resources
- name: Patch Cryptomator.AppDir
run: |
mv appdir/Cryptomator Cryptomator.AppDir
cp -r dist/linux/appimage/resources/AppDir/* Cryptomator.AppDir/
envsubst '${REVISION_NO} ${SEMVER_STR}' < dist/linux/appimage/resources/AppDir/bin/cryptomator.sh > Cryptomator.AppDir/bin/cryptomator.sh
cp dist/linux/common/org.cryptomator.Cryptomator256.png Cryptomator.AppDir/usr/share/icons/hicolor/256x256/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator512.png Cryptomator.AppDir/usr/share/icons/hicolor/512x512/apps/org.cryptomator.Cryptomator.png
cp dist/linux/common/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg
@@ -107,9 +113,6 @@ jobs:
ln -s usr/share/icons/hicolor/scalable/apps/org.cryptomator.Cryptomator.svg Cryptomator.AppDir/.DirIcon
ln -s usr/share/applications/org.cryptomator.Cryptomator.desktop Cryptomator.AppDir/Cryptomator.desktop
ln -s bin/cryptomator.sh Cryptomator.AppDir/AppRun
env:
REVISION_NO: ${{ steps.versions.outputs.revNum }}
SEMVER_STR: ${{ steps.versions.outputs.semVerStr }}
- name: Extract libjffi.so # workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
run: |
JFFI_NATIVE_JAR=`ls lib/app/ | grep -e 'jffi-[1-9]\.[0-9]\{1,2\}.[0-9]\{1,2\}-native.jar'`
+63
View File
@@ -0,0 +1,63 @@
name: Report Download Stats
on:
schedule:
- cron: '0/15 * * * *' # run every 15 min - don't forget to adjust the "interval" in the json sent to the metrics endpoint
jobs:
report-download-stats:
runs-on: ubuntu-latest
steps:
- name: Get download count of latest releases
id: get-stats
uses: actions/github-script@v6
with:
script: |
const query = `query($owner:String!, $name:String!) {
repository(owner:$owner, name:$name){
releases(first: 10, orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
isPrerelease
tagName
releaseAssets(first: 20) {
nodes {
name
downloadCount
}
}
}
}
}
}`;
const variables = {
owner: context.repo.owner,
name: context.repo.repo
}
return await github.graphql(query, variables)
- name: Transform Results
id: transform-stats
run: |
TIME=$(($(date +%s) / $INTERVAL * $INTERVAL))
echo ${JSON_DATA} | jq --arg TIME "$TIME" --arg INTERVAL "$INTERVAL" -c '.repository.releases.nodes[] | select(.isPrerelease == false) | .tagName as $tagName | .releaseAssets.nodes[] | {filename: .name, downloads: .downloadCount, release: $tagName, time: ($TIME|tonumber), interval: ($INTERVAL|tonumber)}' > input.json
jq -c 'select(.filename|endswith("-x86_64.AppImage")) | {name: "github.releases.downloads", tags: ["file=AppImage", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("_amd64.deb")) | {name: "github.releases.downloads", tags: ["file=deb", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-x64.msi")) | {name: "github.releases.downloads", tags: ["file=msi", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-x64.exe")) | {name: "github.releases.downloads", tags: ["file=exe", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith("-arm64.dmg")) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=arm64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
jq -c 'select(.filename|endswith(".dmg")) | select(.filename|endswith("-arm64.dmg")|not) | {name: "github.releases.downloads", tags: ["file=dmg", "version=\(.release)", "arch=amd64"], value: .downloads, interval: .interval, time: .time}' input.json >> output.json
RESULT=$(jq -s -c "." output.json)
echo "::set-output name=result::${RESULT}"
env:
INTERVAL: 900
JSON_DATA: ${{ steps.get-stats.outputs.result }}
- name: Upload Results
uses: fjogeleit/http-request-action@v1
with:
url: 'https://graphite-us-central1.grafana.net/metrics'
method: 'POST'
contentType: 'application/json'
bearerToken: ${{ secrets.GRAFANA_GRAPHITE_TOKEN }}
data: ${{ steps.transform-stats.outputs.result }}
continue-on-error: true # currently there seems to be a problem with the metrics endpoint, failing every now and then
+19 -30
View File
@@ -82,13 +82,15 @@ jobs:
--app-version "${{ steps.versions.outputs.semVerNum }}"
--java-options "-Xss5m"
--java-options "-Xmx256m"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dfile.encoding=\"utf-8\""
--java-options "-Dapple.awt.enableTemplateImages=true"
--java-options "-Dsun.java2d.metal=true"
--java-options "-Dcryptomator.appVersion=\"${{ steps.versions.outputs.semVerStr }}\""
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\""
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\""
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\""
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\""
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"dmg-${{ steps.versions.outputs.revNum }}\""
--mac-package-identifier org.cryptomator
@@ -102,6 +104,16 @@ jobs:
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
REVISION_NO: ${{ steps.versions.outputs.revNum }}
- name: Generate license for dmg
run: >
mvn -B 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
@@ -176,33 +188,14 @@ jobs:
Cryptomator-${VERSION_NO}.dmg dmg
env:
VERSION_NO: ${{ steps.versions.outputs.semVerNum }}
- name: Install notarization credentials
if: startsWith(github.ref, 'refs/tags/')
run: |
# create temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
security create-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
security set-keychain-settings -lut 900 ${KEYCHAIN_PATH}
security unlock-keychain -p "${NOTARIZATION_TMP_KEYCHAIN_PW}" ${KEYCHAIN_PATH}
# import credentials from secrets
sudo xcode-select -s /Applications/Xcode_13.0.app
xcrun notarytool store-credentials "${NOTARIZATION_KEYCHAIN_PROFILE}" --apple-id "${NOTARIZATION_APPLE_ID}" --password "${NOTARIZATION_PW}" --team-id "${NOTARIZATION_TEAM_ID}" --keychain "${KEYCHAIN_PATH}"
env:
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }}
NOTARIZATION_PW: ${{ secrets.MACOS_NOTARIZATION_PW }}
NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }}
NOTARIZATION_TMP_KEYCHAIN_PW: ${{ secrets.MACOS_NOTARIZATION_TMP_KEYCHAIN_PW }}
- name: Notarize .dmg
if: startsWith(github.ref, 'refs/tags/')
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/notarization.keychain-db
sudo xcode-select -s /Applications/Xcode_13.0.app
xcrun notarytool submit Cryptomator-*.dmg --keychain-profile "${NOTARIZATION_KEYCHAIN_PROFILE}" --keychain "${KEYCHAIN_PATH}" --wait
xcrun stapler staple Cryptomator-*.dmg
env:
NOTARIZATION_KEYCHAIN_PROFILE: ${{ secrets.MACOS_NOTARIZATION_KEYCHAIN_PROFILE }}
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 }}
- name: Add possible alpha/beta tags to installer name
run: mv Cryptomator-*.dmg Cryptomator-${{ steps.versions.outputs.semVerStr }}.dmg
- name: Create detached GPG signature with key 615D449FE6E6A235
@@ -216,10 +209,6 @@ jobs:
if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/codesign.keychain-db
continue-on-error: true
- name: Clean up notarization credentials
if: ${{ always() }}
run: security delete-keychain $RUNNER_TEMP/notarization.keychain-db
continue-on-error: true
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
+73 -9
View File
@@ -97,6 +97,7 @@ jobs:
--java-options "-Dcryptomator.mountPointsDir=\"~/Cryptomator\""
--java-options "-Dcryptomator.showTrayIcon=true"
--java-options "-Dcryptomator.buildNumber=\"msi-${{ steps.versions.outputs.revNum }}\""
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=\"Cryptomator\""
--resource-dir dist/win/resources
--icon dist/win/resources/Cryptomator.ico
- name: Patch Application Directory
@@ -115,12 +116,17 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com'
folder: appdir/Cryptomator
recursive: true
- name: Generate license
- name: Generate license for MSI
run: >
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/resources"
"-Dlicense.fileTemplate=dist/win/resources/licenseTemplate.ftl"
"-Dlicense.includedScopes=compile"
"-Dlicense.excludedGroups=^org\.cryptomator"
"-Dlicense.failOnMissing=true"
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
shell: pwsh
- name: Create MSI
run: >
${JAVA_HOME}/bin/jpackage
@@ -183,6 +189,19 @@ jobs:
semVerStr: ${{ steps.versions.outputs.semVerStr }}
revNum: ${{ steps.versions.outputs.revNum }}
publish-winget:
name: Publish on winget repo
runs-on: windows-latest
needs: [build-msi]
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
$installerUrl = $github.release.assets | Where-Object -Property name -match 'Cryptomator-*.msi' | Select -ExpandProperty browser_download_url -First 1
.\wingetcreate.exe update Cryptomator.Cryptomator -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.CRYPTOBOT_WINGET_TOKEN }}
shell: pwsh
build-exe:
name: Build .exe installer
runs-on: windows-latest
@@ -201,12 +220,17 @@ jobs:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'
- name: Generate license
- name: Generate license for exe
run: >
mvn -B license:add-third-party
"-Dlicense.thirdPartyFilename=license.rtf"
"-Dlicense.fileTemplate=dist/win/bundle/resources/licenseTemplate.ftl"
"-Dlicense.outputDirectory=dist/win/bundle/resources"
"-Dlicense.includedScopes=compile"
"-Dlicense.excludedGroups=^org\.cryptomator"
"-Dlicense.failOnMissing=true"
"-Dlicense.licenseMergesUrl=file:///${{ github.workspace }}/license/merges"
shell: pwsh
- name: Download WinFsp
run:
curl --output dist/win/bundle/resources/winfsp.msi -L ${{ env.WINFSP_MSI }}
@@ -225,11 +249,11 @@ jobs:
run: >
"${WIX}/bin/light.exe" -b dist/win/ dist/win/bundle/bundleWithWinfsp.wixobj
-ext WixBalExtension
-out installer/unsigned/Cryptomator.exe
-out installer/unsigned/Cryptomator-Installer.exe
- name: Detach burn engine in preparation to sign
run: >
"${WIX}/bin/insignia.exe"
-ib installer/unsigned/Cryptomator.exe
-ib installer/unsigned/Cryptomator-Installer.exe
-o tmp/engine.exe
- name: Codesign burn engine
uses: skymatic/code-sign-action@v1
@@ -243,8 +267,8 @@ jobs:
- name: Reattach signed burn engine to installer
run : >
"${WIX}/bin/insignia.exe"
-ab tmp/engine.exe installer/unsigned/Cryptomator.exe
-o installer/Cryptomator.exe
-ab tmp/engine.exe installer/unsigned/Cryptomator-Installer.exe
-o installer/Cryptomator-Installer.exe
- name: Codesign EXE
uses: skymatic/code-sign-action@v1
with:
@@ -255,7 +279,7 @@ jobs:
timestampUrl: 'http://timestamp.digicert.com'
folder: installer
- name: Add possible alpha/beta tags to installer name
run: mv installer/Cryptomator.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
run: mv installer/Cryptomator-Installer.exe Cryptomator-${{ needs.build-msi.outputs.semVerStr }}-x64.exe
- name: Create detached GPG signature with key 615D449FE6E6A235
run: |
echo "${GPG_PRIVATE_KEY}" | gpg --batch --quiet --import
@@ -279,4 +303,44 @@ jobs:
token: ${{ secrets.CRYPTOBOT_RELEASE_TOKEN }}
files: |
Cryptomator-*.exe
Cryptomator-*.asc
Cryptomator-*.asc
allowlist:
name: Anti Virus Allowlisting
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
needs: [build-msi, build-exe]
steps:
- name: Download .msi
uses: actions/download-artifact@v2
with:
name: msi
path: msi
- name: Download .exe
uses: actions/download-artifact@v2
with:
name: exe
path: exe
- name: Collect files
run: |
mkdir files
cp msi/*.msi files
cp exe/*.exe files
- name: Upload to Kaspersky
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
protocol: ftps
server: allowlist.kaspersky-labs.com
port: 990
username: ${{ secrets.ALLOWLIST_KASPERSKY_USERNAME }}
password: ${{ secrets.ALLOWLIST_KASPERSKY_PASSWORD }}
local-dir: files/
- name: Upload to Avast
uses: SamKirkland/FTP-Deploy-Action@4.3.0
with:
protocol: ftp
server: whitelisting.avast.com
port: 21
username: ${{ secrets.ALLOWLIST_AVAST_USERNAME }}
password: ${{ secrets.ALLOWLIST_AVAST_PASSWORD }}
local-dir: files/
+1 -17
View File
@@ -5,25 +5,9 @@
*.war
*.ear
# Eclipse Settings Files #
.settings
.project
.classpath
# Maven #
target/
pom.xml.versionsBackup
# IntelliJ Settings Files (https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems) #
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/shelf
.idea/dictionaries/**
!.idea/dictionaries/dict_*
.idea/compiler.xml
.idea/jarRepositories.xml
.idea/uiDesigner.xml
.idea/**/libraries/
*.iml
# Java Crash Logs
hs_err_pid*.log
+13
View File
@@ -0,0 +1,13 @@
# see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839-How-to-manage-projects-under-Version-Control-Systems
# Default ignored files
/shelf/
/workspace.xml
/usage.statistics.xml
/dictionaries/
# generated from Maven
/jarRepositories.xml
/modules.xml
/*.iml
/libraries/*.xml
+51
View File
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
</profile>
<profile name="Annotation profile for Cryptomator Desktop App" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<option name="dagger.fastInit" value="enabled" />
<option name="dagger.formatGeneratedSource" value="enabled" />
<processorPath useClasspath="false">
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.41/dagger-compiler-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.41/dagger-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.41/dagger-producers-2.41.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$/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.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$/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.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.41/dagger-spi-2.41.jar" />
<entry name="$MAVEN_REPOSITORY$/com/google/devtools/ksp/symbol-processing-api/1.5.30-1.0.0/symbol-processing-api-1.5.30-1.0.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.32/kotlin-stdlib-jdk8-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.5.32/kotlin-stdlib-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.5.32/kotlin-stdlib-common-1.5.32.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5.32/kotlin-stdlib-jdk7-1.5.32.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/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
<entry name="$MAVEN_REPOSITORY$/net/ltgt/gradle/incap/incap/0.2/incap-0.2.jar" />
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.3.0/kotlinx-metadata-jvm-0.3.0.jar" />
</processorPath>
<module name="cryptomator" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="cryptomator" options="-Adagger.fastInit=enabled -Adagger.formatGeneratedSource=enabled" />
</option>
</component>
</project>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator/mnt&quot; -Dcryptomator.showTrayIcon=true -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Linux Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Djdk.gtk.version=2 -Duser.language=en -Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/.config/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/.config/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/.local/share/Cryptomator-Dev/logs&quot; -Dcryptomator.pluginDir=&quot;~/.local/share/Cryptomator-Dev/plugins&quot; -Dcryptomator.mountPointsDir=&quot;~/.local/share/Cryptomator-Dev/mnt&quot; -Dcryptomator.showTrayIcon=true -Dfuse.experimental=&quot;true&quot; -Xss20m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -2,7 +2,7 @@
<configuration default="false" name="Cryptomator Windows Dev" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<option name="VM_PARAMETERS" value="-Dcryptomator.settingsPath=&quot;~/AppData/Roaming/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/AppData/Roaming/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/AppData/Roaming/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/AppData/Roaming/Cryptomator-Dev/Plugins&quot; -Dcryptomator.keychainPath=&quot;~/AppData/Roaming/Cryptomator-Dev/keychain.json&quot; -Dcryptomator.mountPointsDir=&quot;~/Cryptomator-Dev&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
+1 -1
View File
@@ -5,7 +5,7 @@
</envs>
<option name="MAIN_CLASS_NAME" value="org.cryptomator.launcher.Cryptomator" />
<module name="cryptomator" />
<option name="VM_PARAMETERS" value="-Duser.language=en -Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<option name="VM_PARAMETERS" value="-Dapple.awt.enableTemplateImages=true -Dcryptomator.settingsPath=&quot;~/Library/Application Support/Cryptomator-Dev/settings.json&quot; -Dcryptomator.ipcSocketPath=&quot;~/Library/Application Support/Cryptomator-Dev/ipc.socket&quot; -Dcryptomator.logDir=&quot;~/Library/Logs/Cryptomator-Dev&quot; -Dcryptomator.pluginDir=&quot;~/Library/Application Support/Cryptomator-Dev/Plugins&quot; -Dcryptomator.showTrayIcon=true -Xss2m -Xmx512m -ea" />
<method v="2">
<option name="Make" enabled="true" />
</method>
-3
View File
@@ -36,8 +36,6 @@ Cryptomator is provided free of charge as an open-source project despite the hig
</tbody>
</table>
- [Jameson Lopp](https://www.lopp.net/)
---
## Introduction
@@ -82,7 +80,6 @@ For more information on the security details visit [cryptomator.org](https://doc
* JDK 17 (e.g. temurin)
* Maven 3
* Optional: OS-dependent build tools for native packaging (see [Windows](https://github.com/cryptomator/cryptomator-win), [OS X](https://github.com/cryptomator/cryptomator-osx), [Linux](https://github.com/cryptomator/builder-containers))
### Run Maven
+3
View File
@@ -20,12 +20,14 @@ ${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
--compress=1
# create app dir
envsubst '${SEMVER_STR} ${REVISION_NUM}' < dist/linux/launcher-gtk2.properties > launcher-gtk2.properties
${JAVA_HOME}/bin/jpackage \
--verbose \
--type app-image \
@@ -48,6 +50,7 @@ ${JAVA_HOME}/bin/jpackage \
--java-options "-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\"" \
--java-options "-Dcryptomator.showTrayIcon=false" \
--java-options "-Dcryptomator.buildNumber=\"appimage-${REVISION_NO}\"" \
--add-launcher cryptomator-gtk2=launcher-gtk2.properties \
--resource-dir ../resources
# transform AppDir
+5 -20
View File
@@ -15,26 +15,11 @@ elif command -v pacman &> /dev/null; then # don't forget arch
GTK3_PRESENT=`pacman -Qi gtk3 &> /dev/null; echo $?`
fi
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
GTK_FLAG="-Djdk.gtk.version=2"
fi
# workaround for https://github.com/cryptomator/cryptomator-linux/issues/27
export LD_PRELOAD=lib/app/libjffi.so
# start Cryptomator
./lib/runtime/bin/java \
-p "lib/app/mods" \
-cp "lib/app/*" \
-Dfile.encoding="utf-8" \
-Dcryptomator.logDir="~/.local/share/Cryptomator/logs" \
-Dcryptomator.pluginDir="~/.local/share/Cryptomator/plugins" \
-Dcryptomator.mountPointsDir="~/.local/share/Cryptomator/mnt" \
-Dcryptomator.settingsPath="~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json" \
-Dcryptomator.ipcSocketPath="~/.config/Cryptomator/ipc.socket" \
-Dcryptomator.buildNumber="appimage-${REVISION_NO}" \
-Dcryptomator.appVersion="${SEMVER_STR}" \
$GTK_FLAG \
-Xss5m \
-Xmx256m \
-m org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator
if [ "$GTK2_PRESENT" -eq 0 ] && [ "$GTK3_PRESENT" -ne 0 ]; then
bin/Cryptomator-gtk2
else
bin/Cryptomator
fi
+1 -1
View File
@@ -7,5 +7,5 @@ Terminal=false
Type=Application
Categories=Utility;Security;FileTools;
StartupNotify=true
StartupWMClass=org.cryptomator.launcher.Cryptomator
StartupWMClass=org.cryptomator.launcher.Cryptomator$MainApp
MimeType=application/vnd.cryptomator.encrypted;application/vnd.cryptomator.vault;
@@ -66,6 +66,8 @@
</content_rating>
<releases>
<release date="2022-05-03" version="1.6.10"/>
<release date="2022-04-27" version="1.6.9"/>
<release date="2022-03-30" version="1.6.8"/>
<release date="2021-12-16" version="1.6.5"/>
</releases>
+1
View File
@@ -19,6 +19,7 @@ override_dh_auto_build:
jlink \
--output runtime \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
+12
View File
@@ -0,0 +1,12 @@
java-options=-Xss5m \
-Xmx256m \
-Dfile.encoding=\"utf-8\" \
-Dcryptomator.appVersion=\"${SEMVER_STR}\" \
-Dcryptomator.logDir=\"~/.local/share/Cryptomator/logs\" \
-Dcryptomator.pluginDir=\"~/.local/share/Cryptomator/plugins\" \
-Dcryptomator.settingsPath=\"~/.config/Cryptomator/settings.json:~/.Cryptomator/settings.json\" \
-Dcryptomator.ipcSocketPath=\"~/.config/Cryptomator/ipc.socket\" \
-Dcryptomator.mountPointsDir=\"~/.local/share/Cryptomator/mnt\" \
-Dcryptomator.showTrayIcon=false \
-Dcryptomator.buildNumber=\"appimage-${REVISION_NUM}\" \
-Djdk.gtk.version=2
+2 -1
View File
@@ -2,4 +2,5 @@
Cryptomator.app/
runtime/
dmg/
*.dmg
*.dmg
license.rtf
+49 -28
View File
@@ -14,9 +14,17 @@ while getopts ":s:" o; do
done
shift "$((OPTIND-1))"
# prepare working dir and variables
# prepare working dir
cd $(dirname $0)
rm -rf runtime dmg
rm -rf runtime dmg *.app *.dmg
# set variables
APP_NAME="Cryptomator"
VENDOR="Skymatic GmbH"
COPYRIGHT_YEARS="2016 - 2022"
PACKAGE_IDENTIFIER="org.cryptomator"
MAIN_JAR_GLOB="cryptomator-*.jar"
MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
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'`
@@ -31,13 +39,14 @@ fi
# compile
mvn -B -f../../../pom.xml clean package -DskipTests -Pmac
cp ../../../target/cryptomator-*.jar ../../../target/mods
cp ../../../target/${MAIN_JAR_GLOB} ../../../target/mods
# add runtime
${JAVA_HOME}/bin/jlink \
--output runtime \
--module-path "${JAVA_HOME}/jmods" \
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr \
--strip-native-commands \
--no-header-files \
--no-man-pages \
--strip-debug \
@@ -50,35 +59,47 @@ ${JAVA_HOME}/bin/jpackage \
--runtime-image runtime \
--input ../../../target/libs \
--module-path ../../../target/mods \
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator \
--module ${MODULE_AND_MAIN_CLASS} \
--dest . \
--name Cryptomator \
--vendor "Skymatic GmbH" \
--copyright "(C) 2016 - 2022 Skymatic GmbH" \
--name ${APP_NAME} \
--vendor "${VENDOR}" \
--copyright "(C) ${COPYRIGHT_YEARS} ${VENDOR}" \
--app-version "${VERSION_NO}" \
--java-options "-Xss5m" \
--java-options "-Xmx256m" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--app-version "${VERSION_NO}" \
--java-options "-Dfile.encoding=\"utf-8\"" \
--java-options "-Dapple.awt.enableTemplateImages=true" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/Cryptomator\"" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/Cryptomator/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/Cryptomator/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/Cryptomator/ipc.socket\"" \
--java-options "-Dsun.java2d.metal=true" \
--java-options "-Dcryptomator.appVersion=\"${VERSION_NO}\"" \
--java-options "-Dcryptomator.logDir=\"~/Library/Logs/${APP_NAME}\"" \
--java-options "-Dcryptomator.pluginDir=\"~/Library/Application Support/${APP_NAME}/Plugins\"" \
--java-options "-Dcryptomator.settingsPath=\"~/Library/Application Support/${APP_NAME}/settings.json\"" \
--java-options "-Dcryptomator.ipcSocketPath=\"~/Library/Application Support/${APP_NAME}/ipc.socket\"" \
--java-options "-Dcryptomator.integrationsMac.keychainServiceName=\"${APP_NAME}\"" \
--java-options "-Dcryptomator.showTrayIcon=true" \
--java-options "-Dcryptomator.buildNumber=\"dmg-${REVISION_NO}\"" \
--mac-package-identifier org.cryptomator \
--mac-package-identifier ${PACKAGE_IDENTIFIER} \
--resource-dir ../resources
# transform app dir
cp ../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
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_VERSION###|${REVISION_NO}|g" ${APP_NAME}.app/Contents/Info.plist
# generate license
mvn -B -f../../../pom.xml license:add-third-party \
-Dlicense.thirdPartyFilename=license.rtf \
-Dlicense.outputDirectory=dist/mac/dmg/resources \
-Dlicense.fileTemplate=resources/licenseTemplate.ftl \
-Dlicense.includedScopes=compile \
-Dlicense.excludedGroups=^org\.cryptomator \
-Dlicense.failOnMissing=true \
-Dlicense.licenseMergesUrl=file://$(pwd)/../../../license/merges
# codesign
if [ -n "${CODESIGN_IDENTITY}" ]; then
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
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
if [[ `unzip -l ${JAR_PATH} | grep '.dylib\|.jnilib'` ]]; then
JAR_FILENAME=$(basename ${JAR_PATH})
OUTPUT_PATH=${JAR_PATH%.*}
@@ -93,25 +114,25 @@ if [ -n "${CODESIGN_IDENTITY}" ]; then
rm -r ${OUTPUT_PATH}
fi
done
echo "Codesigning Cryptomator.app..."
codesign --force --deep --entitlements ../Cryptomator.entitlements -o runtime -s ${CODESIGN_IDENTITY} Cryptomator.app
echo "Codesigning ${APP_NAME}.app..."
codesign --force --deep --entitlements ../${APP_NAME}.entitlements -o runtime -s ${CODESIGN_IDENTITY} ${APP_NAME}.app
fi
# prepare dmg contents
mkdir dmg
mv Cryptomator.app dmg
mv ${APP_NAME}.app dmg
cp resources/macFUSE.webloc dmg
# create dmg
create-dmg \
--volname Cryptomator \
--volicon "resources/Cryptomator-Volume.icns" \
--background "resources/Cryptomator-background.tiff" \
--volname ${APP_NAME} \
--volicon "resources/${APP_NAME}-Volume.icns" \
--background "resources/${APP_NAME}-background.tiff" \
--window-pos 400 100 \
--window-size 640 694 \
--icon-size 128 \
--icon "Cryptomator.app" 128 245 \
--hide-extension "Cryptomator.app" \
--icon "${APP_NAME}.app" 128 245 \
--hide-extension "${APP_NAME}.app" \
--icon "macFUSE.webloc" 320 501 \
--hide-extension "macFUSE.webloc" \
--app-drop-link 512 245 \
@@ -119,4 +140,4 @@ create-dmg \
--icon ".background" 128 758 \
--icon ".fseventsd" 320 758 \
--icon ".VolumeIcon.icns" 512 758 \
Cryptomator-${VERSION_NO}.dmg dmg
${APP_NAME}-${VERSION_NO}.dmg dmg
-100
View File
@@ -1,100 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf2512
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
\deftab720
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
\f0\b\fs24 \cf0 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.
\f1\b0 \
\
\f0\b \'a9 2016 \'96 2022 Skymatic GmbH
\f1\b0 \
\
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
\
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
\
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
\
\f0\b Cryptomator uses 49 third-party dependencies under the following licenses:
\f1\b0 \
Apache License v2.0:\
- jffi (com.github.jnr:jffi:1.2.23 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jffi"}}{\fldrslt http://github.com/jnr/jffi}})\
- jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm}})\
- jnr-constants (com.github.jnr:jnr-constants:0.9.15 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-constants"}}{\fldrslt http://github.com/jnr/jnr-constants}})\
- jnr-ffi (com.github.jnr:jnr-ffi:2.1.12 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-ffi"}}{\fldrslt http://github.com/jnr/jnr-ffi}})\
- FindBugs-jsr305 (com.google.code.findbugs:jsr305:3.0.2 - {\field{\*\fldinst{HYPERLINK "http://findbugs.sourceforge.net/"}}{\fldrslt http://findbugs.sourceforge.net/}})\
- Gson (com.google.code.gson:gson:2.8.6 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/gson/gson"}}{\fldrslt https://github.com/google/gson/gson}})\
- Dagger (com.google.dagger:dagger:2.29.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/dagger"}}{\fldrslt https://github.com/google/dagger}})\
- error-prone annotations (com.google.errorprone:error_prone_annotations:2.3.4 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/error_prone_parent/error_prone_annotation}} )\
- Guava InternalFutureFailureAccess and InternalFutures (com.google.guava:failureaccess:1.0.1 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/failureaccess"}}{\fldrslt https://github.com/google/guava/failureaccess}})\
- Guava: Google Core Libraries for Java (com.google.guava:guava:30.0-jre - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/guava"}}{\fldrslt https://github.com/google/guava/guava}})\
- Guava ListenableFuture only (com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava - {\field{\*\fldinst{HYPERLINK "https://github.com/google/guava/listenablefuture"}}{\fldrslt https://github.com/google/guava/listenablefuture}})\
- J2ObjC Annotations (com.google.j2objc:j2objc-annotations:1.3 - {\field{\*\fldinst{HYPERLINK "https://github.com/google/j2objc/"}}{\fldrslt https://github.com/google/j2objc/}})\
- Apache Commons CLI (commons-cli:commons-cli:1.4 - {\field{\*\fldinst{HYPERLINK "http://commons.apache.org/proper/commons-cli/"}}{\fldrslt http://commons.apache.org/proper/commons-cli/}})\
- javax.inject (javax.inject:javax.inject:1 - {\field{\*\fldinst{HYPERLINK "http://code.google.com/p/atinject/"}}{\fldrslt http://code.google.com/p/atinject/}})\
- Java Native Access (net.java.dev.jna:jna:5.6.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
- Apache Commons Lang (org.apache.commons:commons-lang3:3.11 - {\field{\*\fldinst{HYPERLINK "https://commons.apache.org/proper/commons-lang/"}}{\fldrslt https://commons.apache.org/proper/commons-lang/}})\
- Apache HttpCore (org.apache.httpcomponents:httpcore:4.4.13 - {\field{\*\fldinst{HYPERLINK "http://hc.apache.org/httpcomponents-core-ga"}}{\fldrslt http://hc.apache.org/httpcomponents-core-ga}})\
- Jackrabbit WebDAV Library (org.apache.jackrabbit:jackrabbit-webdav:2.21.3 - {\field{\*\fldinst{HYPERLINK "http://jackrabbit.apache.org/jackrabbit-webdav/"}}{\fldrslt http://jackrabbit.apache.org/jackrabbit-webdav/}})\
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
BSD:\
- asm (org.ow2.asm:asm:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-analysis (org.ow2.asm:asm-analysis:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-commons (org.ow2.asm:asm-commons:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-tree (org.ow2.asm:asm-tree:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
- asm-util (org.ow2.asm:asm-util:7.1 - {\field{\*\fldinst{HYPERLINK "http://asm.ow2.org/"}}{\fldrslt http://asm.ow2.org/}})\
Eclipse Public License - Version 1.0:\
- Jetty :: Http Utility (org.eclipse.jetty:jetty-http:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-http"}}{\fldrslt https://eclipse.org/jetty/jetty-http}})\
- Jetty :: IO Utility (org.eclipse.jetty:jetty-io:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-io"}}{\fldrslt https://eclipse.org/jetty/jetty-io}})\
- Jetty :: Security (org.eclipse.jetty:jetty-security:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-security"}}{\fldrslt https://eclipse.org/jetty/jetty-security}})\
- Jetty :: Server Core (org.eclipse.jetty:jetty-server:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-server"}}{\fldrslt https://eclipse.org/jetty/jetty-server}})\
- Jetty :: Servlet Handling (org.eclipse.jetty:jetty-servlet:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-servlet"}}{\fldrslt https://eclipse.org/jetty/jetty-servlet}})\
- Jetty :: Utilities (org.eclipse.jetty:jetty-util:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util"}}{\fldrslt https://eclipse.org/jetty/jetty-util}})\
- Jetty :: Utilities :: Ajax(JSON) (org.eclipse.jetty:jetty-util-ajax:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-util-ajax"}}{\fldrslt https://eclipse.org/jetty/jetty-util-ajax}})\
- Jetty :: Webapp Application Support (org.eclipse.jetty:jetty-webapp:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-webapp"}}{\fldrslt https://eclipse.org/jetty/jetty-webapp}})\
- Jetty :: XML utilities (org.eclipse.jetty:jetty-xml:9.4.35.v20201120 - {\field{\*\fldinst{HYPERLINK "https://eclipse.org/jetty/jetty-xml"}}{\fldrslt https://eclipse.org/jetty/jetty-xml}})\
Eclipse Public License - v 2.0:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
GPLv2:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
GPLv2+CE:\
- Java Servlet API (javax.servlet:javax.servlet-api:3.1.0 - {\field{\*\fldinst{HYPERLINK "http://servlet-spec.java.net"}}{\fldrslt http://servlet-spec.java.net}})\
- javafx-base (org.openjfx:javafx-base:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-base/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-base/}})\
- javafx-controls (org.openjfx:javafx-controls:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-controls/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-controls/}})\
- javafx-fxml (org.openjfx:javafx-fxml:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-fxml/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-fxml/}})\
- javafx-graphics (org.openjfx:javafx-graphics:15 - {\field{\*\fldinst{HYPERLINK "https://openjdk.java.net/projects/openjfx/javafx-graphics/"}}{\fldrslt https://openjdk.java.net/projects/openjfx/javafx-graphics/}})\
LGPL 2.1:\
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - {\field{\*\fldinst{HYPERLINK "http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix"}}{\fldrslt http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix}})\
- Java Native Access (net.java.dev.jna:jna:5.6.0 - https://github.com/java-native-access/jna)\
- Java Native Access Platform (net.java.dev.jna:jna-platform:5.5.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/java-native-access/jna"}}{\fldrslt https://github.com/java-native-access/jna}})\
MIT License:\
- java jwt (com.auth0:java-jwt:3.12.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/auth0/java-jwt"}}{\fldrslt https://github.com/auth0/java-jwt}})\
- jnr-x86asm (com.github.jnr:jnr-x86asm:1.0.2 - {\field{\*\fldinst{HYPERLINK "http://github.com/jnr/jnr-x86asm"}}{\fldrslt http://github.com/jnr/jnr-x86asm}})\
- jnr-fuse (com.github.serceman:jnr-fuse:0.5.4 - no url defined)\
- zxcvbn4j (com.nulab-inc:zxcvbn:1.3.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/nulab/zxcvbn4j"}}{\fldrslt https://github.com/nulab/zxcvbn4j}})\
- Checker Qual (org.checkerframework:checker-qual:3.5.0 - {\field{\*\fldinst{HYPERLINK "https://checkerframework.org"}}{\fldrslt https://checkerframework.org}})\
- SLF4J API Module (org.slf4j:slf4j-api:1.7.30 - {\field{\*\fldinst{HYPERLINK "http://www.slf4j.org"}}{\fldrslt http://www.slf4j.org}})\
The BSD 2-Clause License:\
- EasyBind (com.tobiasdiez:easybind:2.1.0 - {\field{\*\fldinst{HYPERLINK "https://github.com/tobiasdiez/EasyBind"}}{\fldrslt https://github.com/tobiasdiez/EasyBind}})\
\
\f0\b Cryptomator uses other third-party assets under the following licenses:
\f1\b0 \
SIL OFL 1.1 License:\
- Font Awesome 5.12.0 ({\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt https://fontawesome.com/}})\
\
}
+49
View File
@@ -0,0 +1,49 @@
<#function artifactFormat p>
<#if p.name?index_of('Unnamed') &gt; -1>
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.artifactId + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
<#else>
<#return "{\\field{\\*\\fldinst{HYPERLINK \"" + (p.url!"no url defined") + "\"}}{\\fldrslt " + p.name + "}}" + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + ")">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\cocoartf2512
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0
\deftab720
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardeftab720\partightenfactor0
\f0\b\fs24 \cf0 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.
\f1\b0 \
\
\f0\b \'a9 2016 \'96 2022 Skymatic GmbH
\f1\b0 \
\
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\
\
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\
\
You should have received a copy of the GNU General Public License along with this program. If not, see {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/"}}{\fldrslt http://www.gnu.org/licenses/}}.\
\
\f0\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:
\f1\b0 \
<#list licenseMap as e>
<#assign license = e.getKey()/>
<#assign projects = e.getValue()/>
<#if projects?size &gt; 0>
${license}:\
<#list projects as project>
- ${artifactFormat(project)}\
</#list>
</#if>
</#list>
\
\f0\b Cryptomator uses other third-party assets under the following licenses:
\f1\b0 \
SIL OFL 1.1 License:\
- {\field{\*\fldinst{HYPERLINK "https://fontawesome.com/"}}{\fldrslt Font Awesome}} (5.12.0)\
\
}
+22 -1
View File
@@ -1,2 +1,23 @@
@echo off
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1
:: Default values for Cryptomator builds
SET APPNAME="Cryptomator"
SET MAIN_JAR_GLOB="cryptomator-*"
SET UPGRADE_UUID="bda45523-42b1-4cae-9354-a45475ed4775"
SET VENDOR="Skymatic GmbH"
SET FIRST_COPYRIGHT_YEAR=2016
SET ABOUT_URL="https://cryptomator.org"
SET UPDATE_URL="https://cryptomator.org/downloads/"
SET HELP_URL="https://cryptomator.org/contact/"
SET MODULE_AND_MAIN_CLASS="org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator"
powershell -NoLogo -NoExit -ExecutionPolicy Unrestricted -Command .\build.ps1^
-AppName %APPNAME%^
-MainJarGlob "%MAIN_JAR_GLOB%"^
-ModuleAndMainClass "%MODULE_AND_MAIN_CLASS%"^
-UpgradeUUID "%UPGRADE_UUID%"^
-Vendor ""%VENDOR%""^
-CopyrightStartYear %FIRST_COPYRIGHT_YEAR%^
-AboutUrl "%ABOUT_URL%"^
-HelpUrl "%HELP_URL%"^
-UpdateUrl "%UPDATE_URL%"^
-Clean 1
+52 -37
View File
@@ -1,5 +1,15 @@
# check parameters
$clean = $args[0] -eq "fresh"
Param(
[Parameter(Mandatory, HelpMessage="Please provide a name for the app")][string] $AppName,
[Parameter(Mandatory, HelpMessage="Please provide the glob pattern to identify the main jar")][string] $MainJarGlob,
[Parameter(Mandatory, HelpMessage="Please provide the module- and main class path to start the app")][string] $ModuleAndMainClass,
[Parameter(Mandatory, HelpMessage="Please provide the windows upgrade uuid for the installer")][string] $UpgradeUUID,
[Parameter(Mandatory, HelpMessage="Please provide the name of the vendor")][string] $Vendor,
[Parameter(Mandatory, HelpMessage="Please provide the starting year for the copyright notice")][int] $CopyrightStartYear,
[Parameter(Mandatory, HelpMessage="Please provide a help url")][string] $HelpUrl,
[Parameter(Mandatory, HelpMessage="Please provide an update url")][string] $UpdateUrl,
[Parameter(Mandatory, HelpMessage="Please provide an about url")][string] $AboutUrl,
[bool] $clean
)
# check preconditions
if ((Get-Command "git" -ErrorAction SilentlyContinue) -eq $null)
@@ -24,12 +34,11 @@ Write-Output "`$revisionNo=$revisionNo"
Write-Output "`$buildDir=$buildDir"
Write-Output "`$Env:JAVA_HOME=$Env:JAVA_HOME"
$vendor = "Skymatic GmbH"
$copyright = "(C) 2016 - 2022 Skymatic GmbH"
$copyright = "(C) $CopyrightStartYear - $((Get-Date).Year) $Vendor"
# compile
&mvn -B -f $buildDir/../../pom.xml clean package -DskipTests -Pwin
Copy-Item "$buildDir\..\..\target\cryptomator-*.jar" -Destination "$buildDir\..\..\target\mods"
Copy-Item "$buildDir\..\..\target\$MainJarGlob.jar" -Destination "$buildDir\..\..\target\mods"
# add runtime
$runtimeImagePath = '.\runtime'
@@ -42,12 +51,13 @@ if ($clean -and (Test-Path -Path $runtimeImagePath)) {
--output runtime `
--module-path "$Env:JAVA_HOME/jmods" `
--add-modules java.base,java.desktop,java.logging,java.naming,java.net.http,java.scripting,java.sql,java.xml,jdk.unsupported,jdk.crypto.ec,jdk.accessibility,jdk.management.jfr `
--strip-native-commands `
--no-header-files `
--no-man-pages `
--strip-debug `
--compress=1
$appPath = '.\Cryptomator'
$appPath = ".\$AppName"
if ($clean -and (Test-Path -Path $appPath)) {
Remove-Item -Path $appPath -Force -Recurse
}
@@ -59,68 +69,73 @@ if ($clean -and (Test-Path -Path $appPath)) {
--runtime-image runtime `
--input ../../target/libs `
--module-path ../../target/mods `
--module org.cryptomator.desktop/org.cryptomator.launcher.Cryptomator `
--module $ModuleAndMainClass `
--dest . `
--name Cryptomator `
--vendor $vendor `
--name $AppName `
--vendor $Vendor `
--copyright $copyright `
--java-options "-Xss5m" `
--java-options "-Xmx256m" `
--java-options "-Dcryptomator.appVersion=`"$semVerNo`"" `
--app-version "$semVerNo.$revisionNo" `
--java-options "-Dfile.encoding=`"utf-8`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/Cryptomator`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/Cryptomator/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/Cryptomator/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/Cryptomator/ipc.socket`"" `
--java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/Cryptomator/keychain.json`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/Cryptomator`"" `
--java-options "-Dcryptomator.logDir=`"~/AppData/Roaming/$AppName`"" `
--java-options "-Dcryptomator.pluginDir=`"~/AppData/Roaming/$AppName/Plugins`"" `
--java-options "-Dcryptomator.settingsPath=`"~/AppData/Roaming/$AppName/settings.json`"" `
--java-options "-Dcryptomator.ipcSocketPath=`"~/AppData/Roaming/$AppName/ipc.socket`"" `
--java-options "-Dcryptomator.keychainPath=`"~/AppData/Roaming/$AppName/keychain.json`"" `
--java-options "-Dcryptomator.mountPointsDir=`"~/$AppName`"" `
--java-options "-Dcryptomator.integrationsWin.autoStartShellLinkName=`"$AppName`"" `
--java-options "-Dcryptomator.showTrayIcon=true" `
--java-options "-Dcryptomator.buildNumber=`"msi-$revisionNo`"" `
--resource-dir resources `
--icon resources/Cryptomator.ico
--icon resources/$AppName.ico
#Create RTF license for msi
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\resources\"
"-Dlicense.outputDirectory=$buildDir\resources\" `
"-Dlicense.includedScopes=compile" `
"-Dlicense.excludedGroups=^org\.cryptomator" `
"-Dlicense.failOnMissing=true" `
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# patch app dir
Copy-Item "contrib\*" -Destination "Cryptomator"
attrib -r "Cryptomator\Cryptomator.exe"
$aboutUrl="https://cryptomator.org"
$updateUrl="https://cryptomator.org/downloads/"
$helpUrl="https://cryptomator.org/contact/"
Copy-Item "contrib\*" -Destination "$AppName"
attrib -r "$AppName\$AppName.exe"
# create .msi
$Env:JP_WIXWIZARD_RESOURCES = "$buildDir\resources"
& "$Env:JAVA_HOME\bin\jpackage" `
--verbose `
--type msi `
--win-upgrade-uuid bda45523-42b1-4cae-9354-a45475ed4775 `
--app-image Cryptomator `
--win-upgrade-uuid $UpgradeUUID `
--app-image $AppName `
--dest installer `
--name Cryptomator `
--vendor $vendor `
--name $AppName `
--vendor $Vendor `
--copyright $copyright `
--app-version "$semVerNo" `
--win-menu `
--win-dir-chooser `
--win-shortcut-prompt `
--win-update-url $updateUrl `
--win-menu-group Cryptomator `
--win-menu-group $AppName `
--resource-dir resources `
--about-url $aboutUrl `
--license-file resources/license.rtf `
--win-update-url $UpdateUrl `
--about-url $AboutUrl `
--file-associations resources/FAvaultFile.properties
#Create RTF license for bundle
&mvn -B -f $buildDir/../../pom.xml license:add-third-party `
"-Dlicense.thirdPartyFilename=license.rtf" `
"-Dlicense.fileTemplate=$buildDir\bundle\resources\licenseTemplate.ftl" `
"-Dlicense.outputDirectory=$buildDir\bundle\resources\"
"-Dlicense.outputDirectory=$buildDir\bundle\resources\" `
"-Dlicense.includedScopes=compile" `
"-Dlicense.excludedGroups=^org\.cryptomator" `
"-Dlicense.failOnMissing=true" `
"-Dlicense.licenseMergesUrl=file:///$buildDir/../../license/merges"
# download Winfsp
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
@@ -130,14 +145,14 @@ Write-Output "Downloading ${winfspMsiUrl}..."
Invoke-WebRequest $winfspMsiUrl -OutFile ".\bundle\resources\winfsp.msi" # redirects are followed by default
# copy MSI to bundle resources
Copy-Item ".\installer\Cryptomator-*.msi" -Destination ".\bundle\resources\Cryptomator.msi"
Copy-Item ".\installer\$AppName-*.msi" -Destination ".\bundle\resources\$AppName.msi"
# create bundle including winfsp
& "$env:WIX\bin\candle.exe" .\bundle\bundleWithWinfsp.wxs -ext WixBalExtension -out bundle\ `
-dBundleVersion="$semVerNo.$revisionNo" `
-dBundleVendor="$vendor" `
-dBundleVendor="$Vendor" `
-dBundleCopyright="$copyright" `
-dAboutUrl="$aboutUrl" `
-dHelpUrl="$helpUrl" `
-dUpdateUrl="$updateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\CryptomatorBundle.exe
-dAboutUrl="$AboutUrl" `
-dHelpUrl="$HelpUrl" `
-dUpdateUrl="$UpdateUrl"
& "$env:WIX\bin\light.exe" -b . .\bundle\BundlewithWinfsp.wixobj -ext WixBalExtension -out installer\$AppName-Installer.exe
+7 -7
View File
@@ -5,18 +5,18 @@
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par
\b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
\par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
\par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
@@ -26,7 +26,7 @@ You should have received a copy of the GNU General Public License along with thi
<#if projects?size &gt; 0>
\tab ${license}:\par
<#list projects as project>
\tab\tab- ${artifactFormat(project)}\par
\tab\tab - ${artifactFormat(project)}\par
</#list>
</#if>
</#list>
@@ -38,4 +38,4 @@ You should have received a copy of the GNU General Public License along with thi
\b Cryptomator dynamically links to third-party libraries under the following license:\b0\par
\tab Uncategorized License:\par
\tab\tab - WinFsp - Windows File System Proxy, Copyright (C) Bill Zissimopoulos ({{\field{\*\fldinst{HYPERLINK https://github.com/billziss-gh/winfsp }}{\fldrslt{https://github.com/billziss-gh/winfsp\ul0\cf0}}}}\f0\fs16 )\b\par
}
}
+5 -1
View File
@@ -1,3 +1,7 @@
@echo off
:: Default values for Cryptomator builds
SET LOOPBACK_ALIAS="cryptomator-vault"
cd %~dp0
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1
powershell -NoLogo -NonInteractive -ExecutionPolicy Unrestricted -Command .\patchWebDAV.ps1^
-LoopbackAlias %LOOPBACK_ALIAS%
+9 -3
View File
@@ -1,10 +1,16 @@
#Requires -RunAsAdministrator
Param(
[Parameter(Mandatory, HelpMessage="Please provide an alias for 127.0.0.1")][string] $LoopbackAlias
)
# Adds for address 127.0.0.1 the 'cryptomator-vault' alias to the hosts file
# Adds an alias for 127.0.0.1 to the hosts file
function Add-AliasToHost {
param (
[string]$LoopbackAlias
)
$sysdir = [Environment]::SystemDirectory
$hostsFile = "$sysdir\drivers\etc\hosts"
$aliasLine = '127.0.0.1 cryptomator-vault'
$aliasLine = "127.0.0.1 $LoopbackAlias"
foreach ($line in Get-Content $hostsFile) {
if ($line -eq $aliasLine){
@@ -49,7 +55,7 @@ function Edit-ProviderOrder {
}
Add-AliasToHost
Add-AliasToHost $LoopbackAlias
Write-Output 'Ensured alias exists in hosts file'
Set-WebDAVFileSizeLimit
+1 -1
View File
@@ -91,7 +91,7 @@
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
</Control>
<Control Id="DescriptionReason2" Type="Text" X="135" Y="170" Width="220" Height="40" Transparent="yes" NoPrefix="yes" Hidden="yes" >
<Text>Cryptomator was still running during installation.</Text>
<Text>Application to update was still running during installation.</Text>
<Condition Action="show">FOUNDRUNNINGAPP</Condition>
</Control>
</Dialog>
+7 -7
View File
@@ -5,18 +5,18 @@
<#return p.name + " (" + p.groupId + ":" + p.artifactId + ":" + p.version + " - {{\\field{\\*\\fldinst{HYPERLINK " + (p.url!"no url defined") + "}}{\\fldrslt{" + (p.url!"no url defined") + "\\ul0\\cf0}}}}\\f0\\fs16 ) ">
</#if>
</#function>
{\rtf1\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Arial;}}
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1031{\fonttbl{\f0\fnil\fcharset0 Segoe UI;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\vieww12000\viewh15840\viewkind0
\pard\tx283\tx567\tx850\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\b\fs16\lang7 Cryptomator is distributed under the GPLv3 License, found below. Please see the bottom of this document for any other license applicable to code used within Cryptomator.\b0\par
\par
\b\'a9 2016 \endash 2022 Skymatic GmbH\b0\par
\b\'a9 2016 \'96 2022 Skymatic GmbH \b0\par
\par
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\par
\par
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\par
\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
You should have received a copy of the GNU General Public License along with this program. If not, see {{\field{\*\fldinst{HYPERLINK http://www.gnu.org/licenses/ }}{\fldrslt{http://www.gnu.org/licenses/\ul0\cf0}}}}\f0\fs16 .\par
\par
\b Cryptomator uses ${dependencyMap?size} third-party dependencies under the following licenses:\b0\par
@@ -26,12 +26,12 @@ You should have received a copy of the GNU General Public License along with thi
<#if projects?size &gt; 0>
\tab ${license}:\par
<#list projects as project>
\tab\tab- ${artifactFormat(project)}\par
\tab\tab - ${artifactFormat(project)}\par
</#list>
</#if>
</#list>
\par
\b Cryptomator uses other third-party assets under the following licenses:\b0\par
\tab SIL OFL 1.1 License:\par
\tab\tab - Font Awesome 5.12.0 ({{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
\tab\tab - Font Awesome (5.12.0 - {{\field{\*\fldinst{HYPERLINK https://fontawesome.com/ }}{\fldrslt{https://fontawesome.com/\ul0\cf0}}}}\f0\fs16 )\b\par
}
+8 -8
View File
@@ -86,12 +86,12 @@
<!-- Non-Opening ProgID -->
<DirectoryRef Id="INSTALLDIR">
<Component Win64="yes" Id="nonStartingProgID" >
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\Cryptomator-Vault.ico" Name="Cryptomator-Vault.ico"></File>
<ProgId Id="Cryptomator.Encrypted.1" Description="Cryptomator Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="application/vnd.cryptomator.encrypted">
<MIME ContentType="application/vnd.cryptomator.encrypted" Default="yes"></MIME>
<File Id="IconFileForEncryptedData" KeyPath="yes" Source="$(env.JP_WIXWIZARD_RESOURCES)\$(var.IconFileC9rC9s)" Name="$(var.IconFileC9rC9s)"></File>
<ProgId Id="$(var.JpAppName).Encrypted.1" Description="$(var.JpAppName) Encrypted Data" Icon="IconFileForEncryptedData" IconIndex="0">
<Extension Id="c9r" Advertise="no" ContentType="$(var.ProgIdContentType)">
<MIME ContentType="$(var.ProgIdContentType)" Default="yes"></MIME>
</Extension>
<Extension Id="c9s" Advertise="no" ContentType="application/vnd.cryptomator.encrypted"/>
<Extension Id="c9s" Advertise="no" ContentType="$(var.ProgIdContentType)"/>
</ProgId>
</Component>
</DirectoryRef>
@@ -130,12 +130,12 @@
<!-- Running App detection and exit -->
<Property Id="FOUNDRUNNINGAPP" Admin="yes"/>
<util:CloseApplication
Id="CloseCryptomator"
Target="cryptomator.exe"
Target="$(var.CloseApplicationTarget)"
Id="Close$(var.JpAppName)"
CloseMessage="no"
RebootPrompt="no"
PromptToContinue="yes"
Description="A running instance of Cryptomator is found. Please close it to continue."
Description="A running instance of $(var.JpAppName) is found. Please close it to continue."
Property="FOUNDRUNNINGAPP"
>
</util:CloseApplication>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Stub by design -->
<!--
overrides.wxi is a placeholder to set/alter WiX variables referenced from default
main.wxs file.
Put custom overrides.wxi in resource directory to replace this default file.
Override default overrides.wxi if configuring of msi installers through jpackage
command line is not sufficient.
WiX variables referenced from default main.wxs that can be altered in custom overrides.wxi:
- JpProductLanguage
Value of `Language` attribute of `Product` WiX element. Default value is 1033.
- JpInstallerVersion
Value of `InstallerVersion` attribute of `Package` WiX element. Default value is 200.
- JpCompressedMsi
Value of `Compressed` attribute of `Package` WiX element. Default value is `yes`.
- JpAllowDowngrades
Should be defined to enable downgrades and undefined to disable downgrades.
Default value is `yes`.
- JpAllowUpgrades
Should be defined to enable upgrades and undefined to disable upgrades.
Default value is `yes`.
-->
<!-- Non-opening ProgID settings-->
<?define IconFileC9rC9s= "Cryptomator-Vault.ico" ?>
<?define ProgIdContentType= "application/vnd.cryptomator.encrypted" ?>
<!-- Close Application util -->
<?define CloseApplicationTarget= "cryptomator.exe" ?>
<Include/>
+7
View File
@@ -0,0 +1,7 @@
Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0|Apache-2.0
MIT License|The MIT License (MIT)|The MIT License|MIT license
LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1
GPLv2|GNU General Public License Version 2
GPLv2+CE|CDDL + GPLv2 with classpath exception
Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0
Eclipse Public License - Version 2.0|Eclipse Public License - v 2.0
+64 -20
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptomator</artifactId>
<version>1.6.8</version>
<version>1.7.0-SNAPSHOT</version>
<name>Cryptomator Desktop App</name>
<organization>
@@ -27,24 +27,24 @@
<nonModularGroupIds>com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh</nonModularGroupIds>
<!-- cryptomator dependencies -->
<cryptomator.cryptofs.version>2.4.0</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.1.0-beta1</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.0.0</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.0.0</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.0.1</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.3</cryptomator.fuse.version>
<cryptomator.cryptofs.version>2.4.2</cryptomator.cryptofs.version>
<cryptomator.integrations.version>1.1.0</cryptomator.integrations.version>
<cryptomator.integrations.win.version>1.1.1</cryptomator.integrations.win.version>
<cryptomator.integrations.mac.version>1.1.1</cryptomator.integrations.mac.version>
<cryptomator.integrations.linux.version>1.1.0</cryptomator.integrations.linux.version>
<cryptomator.fuse.version>1.3.4</cryptomator.fuse.version>
<cryptomator.dokany.version>1.3.3</cryptomator.dokany.version>
<cryptomator.webdav.version>1.2.7</cryptomator.webdav.version>
<!-- 3rd party dependencies -->
<javafx.version>18</javafx.version>
<javafx.version>18.0.1</javafx.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<jwt.version>3.19.1</jwt.version>
<easybind.version>2.2</easybind.version>
<guava.version>31.1-jre</guava.version>
<dagger.version>2.41</dagger.version>
<gson.version>2.9.0</gson.version>
<zxcvbn.version>1.5.2</zxcvbn.version>
<zxcvbn.version>1.7.0</zxcvbn.version>
<slf4j.version>1.7.36</slf4j.version>
<logback.version>1.2.11</logback.version>
@@ -53,8 +53,9 @@
<mockito.version>4.4.0</mockito.version>
<hamcrest.version>2.2</hamcrest.version>
<!-- build plugin dependencies -->
<dependency-check.version>7.0.2</dependency-check.version>
<!-- build-time dependencies -->
<jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
<dependency-check.version>7.1.0</dependency-check.version>
<jacoco.version>0.8.7</jacoco.version>
</properties>
@@ -224,6 +225,13 @@
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>${jetbrains.annotations.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
@@ -290,6 +298,41 @@
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>compile-light-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/light_theme.css</arg>
<arg>${project.build.outputDirectory}/css/light_theme.bss</arg>
</arguments>
</configuration>
</execution>
<execution>
<id>compile-dark-theme</id>
<phase>compile</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>javafx.graphics/com.sun.javafx.css.parser.Css2Bin</mainClass>
<arguments>
<arg>${project.basedir}/src/main/resources/css/dark_theme.css</arg>
<arg>${project.build.outputDirectory}/css/dark_theme.bss</arg>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@@ -347,23 +390,24 @@
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.basedir}/src/main/resources/license</outputDirectory>
<thirdPartyFilename>THIRD-PARTY.txt</thirdPartyFilename>
<includedScopes>compile</includedScopes>
<excludedGroups>org\.cryptomator</excludedGroups>
<licenseMerges>
<licenseMerge>Apache License v2.0|Apache License, Version 2.0|The Apache Software License, Version 2.0|Apache 2.0|Apache Software License - Version 2.0</licenseMerge>
<licenseMerge>MIT License|The MIT License (MIT)|The MIT License|MIT license</licenseMerge>
<licenseMerge>LGPL 2.1|LGPL, version 2.1|GNU Lesser/Library General Public License version 2|GNU Lesser General Public License Version 2.1</licenseMerge>
<licenseMerge>GPLv2|GNU General Public License Version 2</licenseMerge>
<licenseMerge>GPLv2+CE|CDDL + GPLv2 with classpath exception</licenseMerge>
</licenseMerges>
<fileTemplate>${project.basedir}/src/license/template.ftl</fileTemplate>
<licenseMergesUrl>file:///${project.basedir}/license/merges</licenseMergesUrl>
<fileTemplate>${project.basedir}/src/main/resources/license/template.ftl</fileTemplate>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>license/*</exclude>
</excludes>
</resource>
</resources>
</build>
<profiles>
-1
View File
@@ -1 +0,0 @@
com.github.serceman--jnr-fuse--0.5.4=MIT License
+8 -8
View File
@@ -1,9 +1,9 @@
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.integrations.tray.TrayMenuController;
import org.cryptomator.ui.traymenu.AwtTrayMenuController;
module org.cryptomator.desktop {
requires static org.jetbrains.annotations;
requires org.cryptomator.cryptofs;
requires org.cryptomator.frontend.dokany;
requires org.cryptomator.frontend.fuse;
@@ -29,13 +29,13 @@ module org.cryptomator.desktop {
requires logback.classic;
requires logback.core;
uses AutoStartProvider;
uses KeychainAccessProvider;
uses TrayIntegrationProvider;
uses UiAppearanceProvider;
exports org.cryptomator.ui.traymenu to org.cryptomator.integrations.api;
provides TrayMenuController with AwtTrayMenuController;
opens org.cryptomator.common.settings to com.google.gson;
opens org.cryptomator.launcher to javafx.graphics;
opens org.cryptomator.common to javafx.fxml;
opens org.cryptomator.common.vaults to javafx.fxml;
opens org.cryptomator.ui.addvaultwizard to javafx.fxml;
@@ -0,0 +1,92 @@
package org.cryptomator.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Platform;
import javafx.concurrent.Task;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
//Inspired by: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ThreadPoolExecutor.html#afterExecute(java.lang.Runnable,java.lang.Throwable)
public final class CatchingExecutors {
private static final Logger LOG = LoggerFactory.getLogger(CatchingExecutors.class);
private CatchingExecutors() { /* NO-OP */ }
public static class CatchingScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
public CatchingScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
afterExecuteInternal(runnable, throwable);
}
}
public static class CatchingThreadPoolExecutor extends ThreadPoolExecutor {
public CatchingThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
@Override
protected void afterExecute(Runnable runnable, Throwable throwable) {
super.afterExecute(runnable, throwable);
afterExecuteInternal(runnable, throwable);
}
}
private static void afterExecuteInternal(Runnable runnable, Throwable throwable) {
if (throwable != null) {
callHandler(Thread.currentThread(), throwable);
} else if (runnable instanceof Task<?> t) {
afterExecuteTask(t);
} else if (runnable instanceof Future<?> f && f.isDone()) {
afterExecuteFuture(f);
}
//Errors in this method are delegated to the UncaughtExceptionHandler of the current thread
}
private static void callHandler(Thread thread, Throwable throwable) {
Objects.requireNonNullElseGet(thread.getUncaughtExceptionHandler(), CatchingExecutors::fallbackHandler).uncaughtException(thread, throwable);
}
private static Thread.UncaughtExceptionHandler fallbackHandler() {
return (thread, throwable) -> LOG.error("FALLBACK: Uncaught exception in " + thread.getName(), throwable);
}
private static void afterExecuteTask(Task<?> task) {
var caller = Thread.currentThread();
Platform.runLater(() -> {
if (task.getOnFailed() == null) {
callHandler(caller, task.getException());
}
});
}
private static void afterExecuteFuture(Future<?> future) {
assert future.isDone();
try {
future.get();
} catch (CancellationException ce) {
callHandler(Thread.currentThread(), ce);
} catch (ExecutionException ee) {
callHandler(Thread.currentThread(), ee.getCause());
} catch (InterruptedException ie) {
//Ignore/Reset
Thread.currentThread().interrupt();
}
}
}
@@ -12,9 +12,7 @@ import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.keychain.KeychainModule;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.SettingsProvider;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultComponent;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultListModule;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.frontend.webdav.WebDavServer;
@@ -25,16 +23,13 @@ import javax.inject.Named;
import javax.inject.Singleton;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import java.net.InetSocketAddress;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -93,7 +88,7 @@ public abstract class CommonsModule {
@Singleton
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
ScheduledExecutorService executorService = new CatchingExecutors.CatchingScheduledThreadPoolExecutor(NUM_SCHEDULER_THREADS, r -> {
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
Thread t = new Thread(r);
t.setName(name);
@@ -110,7 +105,7 @@ public abstract class CommonsModule {
@Singleton
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
final AtomicInteger threadNumber = new AtomicInteger(1);
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
ExecutorService executorService = new CatchingExecutors.CatchingThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
Thread t = new Thread(r);
t.setName(name);
@@ -6,6 +6,7 @@ public interface Constants {
String MASTERKEY_BACKUP_SUFFIX = ".bkup";
String VAULTCONFIG_FILENAME = "vault.cryptomator";
String CRYPTOMATOR_FILENAME_EXT = ".cryptomator";
String CRYPTOMATOR_FILENAME_GLOB = "*.cryptomator";
byte[] PEPPER = new byte[0];
}
@@ -42,7 +42,6 @@ public class Environment {
LOG.debug("cryptomator.appVersion: {}", System.getProperty("cryptomator.appVersion"));
LOG.debug("cryptomator.buildNumber: {}", System.getProperty("cryptomator.buildNumber"));
LOG.debug("cryptomator.showTrayIcon: {}", System.getProperty("cryptomator.showTrayIcon"));
LOG.debug("fuse.experimental: {}", Boolean.getBoolean("fuse.experimental"));
}
public boolean useCustomLogbackConfig() {
@@ -0,0 +1,64 @@
package org.cryptomator.common;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
/**
* Enum of common cloud providers and their default local storage location path.
*/
public enum LocationPreset {
DROPBOX("Dropbox", "~/Dropbox"),
ICLOUDDRIVE("iCloud Drive", "~/Library/Mobile Documents/com~apple~CloudDocs", "~/iCloudDrive"),
GDRIVE("Google Drive", "~/Google Drive/My Drive", "~/Google Drive"),
MEGA("MEGA", "~/MEGA"),
ONEDRIVE("OneDrive", "~/OneDrive"),
PCLOUD("pCloud", "~/pCloudDrive"),
LOCAL("local");
private final String name;
private final List<Path> candidates;
LocationPreset(String name, String... candidates) {
this.name = name;
this.candidates = Arrays.stream(candidates).map(UserHome::resolve).map(Path::of).toList();
}
/**
* Checks for this LocationPreset if any of the associated paths exist.
*
* @return the first existing path or null, if none exists.
*/
public Path existingPath() {
return candidates.stream().filter(Files::isDirectory).findFirst().orElse(null);
}
public String getDisplayName() {
return name;
}
@Override
public String toString() {
return getDisplayName();
}
//this contruct is needed, since static members are initialized after every enum member is initialized
//TODO: refactor this to normal class and use this also in different parts of the project
private static class UserHome {
private static final String USER_HOME = System.getProperty("user.home");
private static String resolve(String path) {
if (path.startsWith("~/")) {
return UserHome.USER_HOME + path.substring(1);
} else {
return path;
}
}
}
}
@@ -1,66 +0,0 @@
package org.cryptomator.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@Singleton
public class PluginClassLoader extends URLClassLoader {
private static final Logger LOG = LoggerFactory.getLogger(PluginClassLoader.class);
private static final String NAME = "PluginClassLoader";
private static final String JAR_SUFFIX = ".jar";
@Inject
public PluginClassLoader(Environment env) {
super(NAME, env.getPluginDir().map(PluginClassLoader::findJars).orElse(new URL[0]), PluginClassLoader.class.getClassLoader());
}
private static URL[] findJars(Path path) {
if (!Files.isDirectory(path)) {
return new URL[0];
} else {
try {
var visitor = new JarVisitor();
Files.walkFileTree(path, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, visitor);
return visitor.urls.toArray(URL[]::new);
} catch (IOException e) {
LOG.warn("Failed to scan plugin dir " + path, e);
return new URL[0];
}
}
}
private static final class JarVisitor extends SimpleFileVisitor<Path> {
private final List<URL> urls = new ArrayList<>();
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (attrs.isRegularFile() && file.getFileName().toString().toLowerCase().endsWith(JAR_SUFFIX)) {
try {
urls.add(file.toUri().toURL());
} catch (MalformedURLException e) {
LOG.warn("Failed to create URL for jar file {}", file);
}
}
return FileVisitResult.CONTINUE;
}
}
}
@@ -43,12 +43,6 @@ public class KeychainManager implements KeychainAccessProvider {
return getClass().getName();
}
@Override
public void storePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
@Override
public void storePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
getKeychainOrFail().storePassphrase(key, displayName, passphrase);
@@ -68,14 +62,6 @@ public class KeychainManager implements KeychainAccessProvider {
setPassphraseStored(key, false);
}
@Override
public void changePassphrase(String key, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) {
getKeychainOrFail().changePassphrase(key, passphrase);
setPassphraseStored(key, true);
}
}
@Override
public void changePassphrase(String key, String displayName, CharSequence passphrase) throws KeychainAccessException {
if (isPassphraseStored(key)) {
@@ -2,42 +2,30 @@ package org.cryptomator.common.keychain;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.integrations.keychain.KeychainAccessProvider;
import javax.inject.Singleton;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectExpression;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.List;
@Module
public class KeychainModule {
@Provides
@Singleton
static Set<ServiceLoader.Provider<KeychainAccessProvider>> provideAvailableKeychainAccessProviderFactories(PluginClassLoader classLoader) {
return ServiceLoader.load(KeychainAccessProvider.class, classLoader).stream().collect(Collectors.toUnmodifiableSet());
static List<KeychainAccessProvider> provideSupportedKeychainAccessProviders() {
return KeychainAccessProvider.get().toList();
}
@Provides
@Singleton
static Set<KeychainAccessProvider> provideSupportedKeychainAccessProviders(Set<ServiceLoader.Provider<KeychainAccessProvider>> availableFactories) {
return availableFactories.stream() //
.map(ServiceLoader.Provider::get) //
.filter(KeychainAccessProvider::isSupported) //
.collect(Collectors.toUnmodifiableSet());
}
@Provides
@Singleton
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, Set<KeychainAccessProvider> providers) {
static ObjectExpression<KeychainAccessProvider> provideKeychainAccessProvider(Settings settings, List<KeychainAccessProvider> providers) {
return Bindings.createObjectBinding(() -> {
var selectedProviderClass = settings.keychainProvider().get();
var selectedProvider = providers.stream().filter(provider -> provider.getClass().getName().equals(selectedProviderClass)).findAny();
var fallbackProvider = providers.stream().findAny().orElse(null);
var fallbackProvider = providers.stream().findFirst().orElse(null);
return selectedProvider.orElse(fallbackProvider);
}, settings.keychainProvider());
}
@@ -36,7 +36,7 @@ public class Settings {
public static final int DEFAULT_NUM_TRAY_NOTIFICATIONS = 3;
public static final WebDavUrlScheme DEFAULT_GVFS_SCHEME = WebDavUrlScheme.DAV;
public static final boolean DEFAULT_DEBUG_MODE = false;
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = SystemUtils.IS_OS_WINDOWS ? VolumeImpl.DOKANY : VolumeImpl.FUSE;
public static final VolumeImpl DEFAULT_PREFERRED_VOLUME_IMPL = VolumeImpl.FUSE;
public static final UiTheme DEFAULT_THEME = UiTheme.LIGHT;
@Deprecated // to be changed to "whatever is available" eventually
public 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";
@@ -44,6 +44,7 @@ public class Settings {
public static final String DEFAULT_LICENSE_KEY = "";
public static final boolean DEFAULT_SHOW_MINIMIZE_BUTTON = false;
public static final String DEFAULT_DISPLAY_CONFIGURATION = "";
public static final String DEFAULT_LANGUAGE = null;
private final ObservableList<VaultSettings> directories = FXCollections.observableArrayList(VaultSettings::observables);
@@ -66,6 +67,7 @@ public class Settings {
private final IntegerProperty windowWidth = new SimpleIntegerProperty();
private final IntegerProperty windowHeight = new SimpleIntegerProperty();
private final ObjectProperty<String> displayConfiguration = new SimpleObjectProperty<>(DEFAULT_DISPLAY_CONFIGURATION);
private final StringProperty language = new SimpleStringProperty(DEFAULT_LANGUAGE);
private Consumer<Settings> saveCmd;
@@ -96,6 +98,7 @@ public class Settings {
windowWidth.addListener(this::somethingChanged);
windowHeight.addListener(this::somethingChanged);
displayConfiguration.addListener(this::somethingChanged);
language.addListener(this::somethingChanged);
}
void setSaveCmd(Consumer<Settings> saveCmd) {
@@ -191,4 +194,8 @@ public class Settings {
public ObjectProperty<String> displayConfigurationProperty() {
return displayConfiguration;
}
public StringProperty languageProperty() {
return language;
}
}
@@ -57,6 +57,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
out.name("windowWidth").value((value.windowWidthProperty().get()));
out.name("windowHeight").value((value.windowHeightProperty().get()));
out.name("displayConfiguration").value((value.displayConfigurationProperty().get()));
out.name("language").value((value.languageProperty().get()));
out.endObject();
}
@@ -97,6 +98,7 @@ public class SettingsJsonAdapter extends TypeAdapter<Settings> {
case "windowWidth" -> settings.windowWidthProperty().set(in.nextInt());
case "windowHeight" -> settings.windowHeightProperty().set(in.nextInt());
case "displayConfiguration" -> settings.displayConfigurationProperty().set(in.nextString());
case "language" -> settings.languageProperty().set(in.nextString());
default -> {
LOG.warn("Unsupported vault setting found in JSON: " + name);
@@ -3,9 +3,9 @@ package org.cryptomator.common.settings;
import org.apache.commons.lang3.SystemUtils;
public enum UiTheme {
LIGHT("preferences.general.theme.light"), //
DARK("preferences.general.theme.dark"), //
AUTOMATIC("preferences.general.theme.automatic");
LIGHT("preferences.interface.theme.light"), //
DARK("preferences.interface.theme.dark"), //
AUTOMATIC("preferences.interface.theme.automatic");
public static UiTheme[] applicableValues() {
if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_WINDOWS) {
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface DefaultMountFlags {
@interface DefaultMountFlags {
}
@@ -10,6 +10,7 @@ package org.cryptomator.common.vaults;
import com.google.common.base.Strings;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.Constants;
import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.vaults.Volume.VolumeException;
@@ -125,6 +126,7 @@ public class Vault {
.withKeyLoader(keyLoader) //
.withFlags(flags) //
.withMaxCleartextNameLength(vaultSettings.maxCleartextFilenameLength().get()) //
.withVaultConfigFilename(Constants.VAULTCONFIG_FILENAME) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
@@ -19,22 +19,13 @@ public interface VaultComponent {
Vault vault();
@Subcomponent.Builder
interface Builder {
@Subcomponent.Factory
interface Factory {
@BindsInstance
Builder vaultSettings(VaultSettings vaultSettings);
VaultComponent create(@BindsInstance VaultSettings vaultSettings, //
@BindsInstance VaultConfigCache configCache, //
@BindsInstance VaultState.Value vaultState, //
@BindsInstance @Nullable @Named("lastKnownException") Exception initialErrorCause);
@BindsInstance
Builder vaultConfigCache(VaultConfigCache configCache);
@BindsInstance
Builder initialVaultState(VaultState.Value vaultState);
@BindsInstance
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
VaultComponent build();
}
}
}
@@ -38,15 +38,15 @@ public class VaultListManager {
private static final Logger LOG = LoggerFactory.getLogger(VaultListManager.class);
private final AutoLocker autoLocker;
private final VaultComponent.Builder vaultComponentBuilder;
private final VaultComponent.Factory vaultComponentFactory;
private final ObservableList<Vault> vaultList;
private final String defaultVaultName;
@Inject
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Builder vaultComponentBuilder, ResourceBundle resourceBundle, Settings settings) {
public VaultListManager(ObservableList<Vault> vaultList, AutoLocker autoLocker, VaultComponent.Factory vaultComponentFactory, ResourceBundle resourceBundle, Settings settings) {
this.vaultList = vaultList;
this.autoLocker = autoLocker;
this.vaultComponentBuilder = vaultComponentBuilder;
this.vaultComponentFactory = vaultComponentFactory;
this.defaultVaultName = resourceBundle.getString("defaults.vault.vaultName");
addAll(settings.getDirectories());
@@ -93,21 +93,17 @@ public class VaultListManager {
}
private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
var wrapper = new VaultConfigCache(vaultSettings);
try {
VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
VaultConfigCache wrapper = new VaultConfigCache(vaultSettings);
compBuilder.vaultConfigCache(wrapper); //first set the wrapper in the builder, THEN try to load config
var vaultState = determineVaultState(vaultSettings.path().get());
if (vaultState == LOCKED) { //for legacy reasons: pre v8 vault do not have a config, but they are in the NEEDS_MIGRATION state
wrapper.reloadConfig();
}
compBuilder.initialVaultState(vaultState);
return vaultComponentFactory.create(vaultSettings, wrapper, vaultState, null).vault();
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
compBuilder.initialVaultState(ERROR);
compBuilder.initialErrorCause(e);
return vaultComponentFactory.create(vaultSettings, wrapper, ERROR, e).vault();
}
return compBuilder.build().vault();
}
public static VaultState.Value redetermineVaultState(Vault vault) {
@@ -83,7 +83,7 @@ public class VaultState extends ObservableValueBase<VaultState.Value> implements
if (success) {
fireValueChangedEvent();
} else {
LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
LOG.debug("Failed transiting into state {}: Expected state was not {}.", fromState, toState);
}
return success;
}
@@ -0,0 +1,13 @@
package org.cryptomator.launcher;
import java.nio.file.Path;
import java.util.Collection;
public record AppLaunchEvent(AppLaunchEvent.EventType type, Collection<Path> pathsToOpen) {
public enum EventType {
REVEAL_APP,
OPEN_FILE
}
}
@@ -13,47 +13,60 @@ import org.cryptomator.common.ShutdownHook;
import org.cryptomator.ipc.IpcCommunicator;
import org.cryptomator.logging.DebugMode;
import org.cryptomator.logging.LoggerConfiguration;
import org.cryptomator.ui.launcher.UiLauncher;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import javafx.application.Application;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
@Singleton
public class Cryptomator {
private static final long STARTUP_TIME = System.currentTimeMillis();
// DaggerCryptomatorComponent gets generated by Dagger.
// Run Maven and include target/generated-sources/annotations in your IDE.
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.create();
private static final CryptomatorComponent CRYPTOMATOR_COMPONENT = DaggerCryptomatorComponent.factory().create(STARTUP_TIME);
private static final Logger LOG = LoggerFactory.getLogger(Cryptomator.class);
private final LoggerConfiguration logConfig;
private final DebugMode debugMode;
private final SupportedLanguages supportedLanguages;
private final Environment env;
private final Lazy<IpcMessageHandler> ipcMessageHandler;
private final CountDownLatch shutdownLatch;
private final ShutdownHook shutdownHook;
private final Lazy<UiLauncher> uiLauncher;
@Inject
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, Lazy<UiLauncher> uiLauncher) {
Cryptomator(LoggerConfiguration logConfig, DebugMode debugMode, SupportedLanguages supportedLanguages, Environment env, Lazy<IpcMessageHandler> ipcMessageHandler, ShutdownHook shutdownHook) {
this.logConfig = logConfig;
this.debugMode = debugMode;
this.supportedLanguages = supportedLanguages;
this.env = env;
this.ipcMessageHandler = ipcMessageHandler;
this.shutdownLatch = shutdownLatch;
this.shutdownHook = shutdownHook;
this.uiLauncher = uiLauncher;
}
public static void main(String[] args) {
var printVersion = Optional.ofNullable(args) //
.stream() //Streams either one element (the args-array) or zero elements
.flatMap(Arrays::stream) //
.anyMatch(arg -> "-v".equals(arg) || "--version".equals(arg));
if (printVersion) {
var appVer = System.getProperty("cryptomator.appVersion", "SNAPSHOT");
var buildNumber = System.getProperty("cryptomator.buildNumber", "SNAPSHOT");
//Reduce noise for parsers by using System.out directly
System.out.printf("Cryptomator version %s (build %s)%n", appVer, buildNumber);
return;
}
int exitCode = CRYPTOMATOR_COMPONENT.application().run(args);
LOG.info("Exit {}", exitCode);
System.exit(exitCode); // end remaining non-daemon threads.
@@ -67,8 +80,10 @@ public class Cryptomator {
*/
private int run(String[] args) {
logConfig.init();
LOG.debug("Dagger graph initialized after {}ms", System.currentTimeMillis() - STARTUP_TIME);
LOG.info("Starting Cryptomator {} on {} {} ({})", env.getAppVersion().orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH);
debugMode.initialize();
supportedLanguages.applyPreferred();
/*
* Attempts to create an IPC connection to a running Cryptomator instance and sends it the given args.
@@ -96,21 +111,38 @@ public class Cryptomator {
}
/**
* Launches the JavaFX application and waits until shutdown is requested.
* Launches the JavaFX application, blocking the main thread until shuts down.
*
* @return Nonzero exit code in case of an error.
* @implNote This method blocks until {@link #shutdownLatch} reached zero.
*/
private int runGuiApplication() {
try {
uiLauncher.get().launch();
shutdownLatch.await();
Application.launch(MainApp.class);
LOG.info("UI shut down");
return 0;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (Exception e) {
LOG.error("Terminating due to error", e);
return 1;
}
}
public static class MainApp extends Application {
@Override
public void start(Stage primaryStage) {
LOG.info("JavaFX runtime started after {}ms", System.currentTimeMillis() - STARTUP_TIME);
FxApplicationComponent component = CRYPTOMATOR_COMPONENT.fxAppComponentBuilder() //
.fxApplication(this) //
.primaryStage(primaryStage) //
.build();
component.application().start();
}
@Override
public void stop() {
LOG.info("JavaFX application stopped.");
}
}
}
@@ -1,16 +1,25 @@
package org.cryptomator.launcher;
import dagger.BindsInstance;
import dagger.Component;
import org.cryptomator.common.CommonsModule;
import org.cryptomator.logging.LoggerModule;
import org.cryptomator.ui.launcher.UiLauncherModule;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
import javax.inject.Singleton;
@Singleton
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class, UiLauncherModule.class})
@Component(modules = {CryptomatorModule.class, CommonsModule.class, LoggerModule.class})
public interface CryptomatorComponent {
Cryptomator application();
FxApplicationComponent.Builder fxAppComponentBuilder();
@Component.Factory
interface Factory {
CryptomatorComponent create(@BindsInstance @Named("startupTime") long startupTime);
}
}
@@ -2,20 +2,50 @@ package org.cryptomator.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.ResourceBundle;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Module
@Module(subcomponents = {FxApplicationComponent.class})
class CryptomatorModule {
@Provides
@Singleton
@Named("shutdownLatch")
static CountDownLatch provideShutdownLatch() {
return new CountDownLatch(1);
static ResourceBundle provideLocalization() {
return ResourceBundle.getBundle("i18n.strings");
}
@Provides
@Singleton
@Named("launchEventQueue")
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
return new ArrayBlockingQueue<>(10);
}
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider() {
return UiAppearanceProvider.get();
}
@Provides
@Singleton
static Optional<AutoStartProvider> provideAutostartProvider() {
return AutoStartProvider.get();
}
@Provides
@Singleton
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider() {
return TrayIntegrationProvider.get();
}
}
@@ -6,7 +6,6 @@
*******************************************************************************/
package org.cryptomator.launcher;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,12 +19,10 @@ import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.stream.Collectors;
@Singleton
class FileOpenRequestHandler {
@@ -1,7 +1,6 @@
package org.cryptomator.launcher;
import org.cryptomator.ipc.IpcMessageListener;
import org.cryptomator.ui.launcher.AppLaunchEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -0,0 +1,39 @@
package org.cryptomator.launcher;
import org.cryptomator.common.settings.Settings;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Locale;
@Singleton
public class SupportedLanguages {
private static final Logger LOG = LoggerFactory.getLogger(SupportedLanguages.class);
// these are BCP 47 language codes, not ISO. Note the "-" instead of the "_":
public static final List<String> LANGUAGAE_TAGS = List.of("en", "ar", "bn", "bs", "ca", "cs", "de", "el", "es", "fil", "fr", "gl", "he", //
"hi", "hr", "hu", "id", "it", "ja", "ko", "lv", "mk", "nb", "nl", "nn", "no", "pa", "pl", "pt", "pt-BR", "ro", "ru", "sk", "sr", //
"sr-Latn", "sv", "sw", "ta", "te", "th", "tr", "uk", "zh", "zh-HK", "zh-TW");
@Nullable
private final String preferredLanguage;
@Inject
public SupportedLanguages(Settings settings) {
this.preferredLanguage = settings.languageProperty().get();
}
public void applyPreferred() {
if (preferredLanguage == null) {
LOG.debug("Using system locale");
return;
}
var preferredLocale = Locale.forLanguageTag(preferredLanguage);
LOG.debug("Applying preferred locale {}", preferredLocale.getDisplayName(Locale.ENGLISH));
Locale.setDefault(preferredLocale);
}
}
@@ -14,7 +14,7 @@ import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.PasswordStrengthUtil;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.mainwindow.MainWindow;
import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
import javax.inject.Named;
@@ -43,12 +43,12 @@ public abstract class AddVaultModule {
@Provides
@AddVaultWizardWindow
@AddVaultWizardScoped
static Stage provideStage(StageFactory factory, @MainWindow Stage owner, ResourceBundle resourceBundle) {
static Stage provideStage(StageFactory factory, @PrimaryStage Stage primaryStage, ResourceBundle resourceBundle) {
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
stage.setResizable(false);
stage.initModality(Modality.WINDOW_MODAL);
stage.initOwner(owner);
stage.initOwner(primaryStage);
return stage;
}
@@ -2,25 +2,24 @@ package org.cryptomator.ui.addvaultwizard;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import javax.inject.Inject;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.Optional;
@AddVaultWizardScoped
public class AddVaultSuccessController implements FxController {
private final FxApplication fxApplication;
private final FxApplicationWindows appWindows;
private final Stage window;
private final ReadOnlyObjectProperty<Vault> vault;
@Inject
AddVaultSuccessController(FxApplication fxApplication, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
this.fxApplication = fxApplication;
AddVaultSuccessController(FxApplicationWindows appWindows, @AddVaultWizardWindow Stage window, @AddVaultWizardWindow ObjectProperty<Vault> vault) {
this.appWindows = appWindows;
this.window = window;
this.vault = vault;
}
@@ -28,7 +27,7 @@ public class AddVaultSuccessController implements FxController {
@FXML
public void unlockAndClose() {
close();
fxApplication.startUnlockWorkflow(vault.get(), Optional.of(window));
appWindows.startUnlockWorkflow(vault.get(), window);
}
@FXML
@@ -2,12 +2,14 @@ package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -20,12 +22,11 @@ import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@AddVaultWizardScoped
public class ChooseExistingVaultController implements FxController {
@@ -34,30 +35,32 @@ public class ChooseExistingVaultController implements FxController {
private final Stage window;
private final Lazy<Scene> welcomeScene;
private final Lazy<Scene> successScene;
private final ErrorComponent.Builder errorComponent;
private final FxApplicationWindows appWindows;
private final ObjectProperty<Path> vaultPath;
private final ObjectProperty<Vault> vault;
private final VaultListManager vaultListManager;
private final ResourceBundle resourceBundle;
private final Settings settings;
private Image screenshot;
@Inject
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle) {
ChooseExistingVaultController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_WELCOME) Lazy<Scene> welcomeScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, VaultListManager vaultListManager, ResourceBundle resourceBundle, Settings settings) {
this.window = window;
this.welcomeScene = welcomeScene;
this.successScene = successScene;
this.errorComponent = errorComponent;
this.appWindows = appWindows;
this.vaultPath = vaultPath;
this.vault = vault;
this.vaultListManager = vaultListManager;
this.resourceBundle = resourceBundle;
this.settings = settings;
}
@FXML
public void initialize() {
if (SystemUtils.IS_OS_MAC) {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac.png").toString());
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-mac"+(UiTheme.LIGHT == settings.theme().get()? "":"-dark")+".png").toString());
} else {
this.screenshot = new Image(getClass().getResource("/img/select-masterkey-win.png").toString());
}
@@ -72,7 +75,7 @@ public class ChooseExistingVaultController implements FxController {
public void chooseFileAndNext() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Vault", "*.cryptomator"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
vaultPath.setValue(masterkeyFile.toPath().toAbsolutePath().getParent());
@@ -82,7 +85,7 @@ public class ChooseExistingVaultController implements FxController {
window.setScene(successScene.get());
} catch (IOException e) {
LOG.error("Failed to open existing vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
appWindows.showErrorWindow(e, window, window.getScene());
}
}
}
@@ -46,7 +46,7 @@ public class CreateNewVaultLocationController implements FxController {
private final Stage window;
private final Lazy<Scene> chooseNameScene;
private final Lazy<Scene> choosePasswordScene;
private final LocationPresets locationPresets;
private final ObservedLocationPresets locationPresets;
private final ObjectProperty<Path> vaultPath;
private final StringProperty vaultName;
private final ResourceBundle resourceBundle;
@@ -71,7 +71,7 @@ public class CreateNewVaultLocationController implements FxController {
public FontAwesome5IconView badLocation;
@Inject
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, LocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy<Scene> chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy<Scene> choosePasswordScene, ObservedLocationPresets locationPresets, ObjectProperty<Path> vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window;
this.chooseNameScene = chooseNameScene;
this.choosePasswordScene = choosePasswordScene;
@@ -197,7 +197,7 @@ public class CreateNewVaultLocationController implements FxController {
return validVaultPath.get();
}
public LocationPresets getLocationPresets() {
public ObservedLocationPresets getObservedLocationPresets() {
return locationPresets;
}
@@ -10,12 +10,12 @@ import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.common.Tasks;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.masterkeyfile.MasterkeyFileLoadingStrategy;
import org.cryptomator.ui.recoverykey.RecoveryKeyFactory;
import org.slf4j.Logger;
@@ -60,7 +60,7 @@ public class CreateNewVaultPasswordController implements FxController {
private final Lazy<Scene> chooseLocationScene;
private final Lazy<Scene> recoveryKeyScene;
private final Lazy<Scene> successScene;
private final ErrorComponent.Builder errorComponent;
private final FxApplicationWindows appWindows;
private final ExecutorService executor;
private final RecoveryKeyFactory recoveryKeyFactory;
private final StringProperty vaultNameProperty;
@@ -83,12 +83,12 @@ public class CreateNewVaultPasswordController implements FxController {
public NewPasswordController newPasswordSceneController;
@Inject
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, ErrorComponent.Builder errorComponent, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
CreateNewVaultPasswordController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_LOCATION) Lazy<Scene> chooseLocationScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_RECOVERYKEY) Lazy<Scene> recoveryKeyScene, @FxmlScene(FxmlFile.ADDVAULT_SUCCESS) Lazy<Scene> successScene, FxApplicationWindows appWindows, ExecutorService executor, RecoveryKeyFactory recoveryKeyFactory, @Named("vaultName") StringProperty vaultName, ObjectProperty<Path> vaultPath, @AddVaultWizardWindow ObjectProperty<Vault> vault, @Named("recoveryKey") StringProperty recoveryKey, VaultListManager vaultListManager, ResourceBundle resourceBundle, ReadmeGenerator readmeGenerator, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.chooseLocationScene = chooseLocationScene;
this.recoveryKeyScene = recoveryKeyScene;
this.successScene = successScene;
this.errorComponent = errorComponent;
this.appWindows = appWindows;
this.executor = executor;
this.recoveryKeyFactory = recoveryKeyFactory;
this.vaultNameProperty = vaultName;
@@ -121,16 +121,6 @@ public class CreateNewVaultPasswordController implements FxController {
@FXML
public void next() {
Path pathToVault = vaultPathProperty.get();
try {
Files.createDirectory(pathToVault);
} catch (IOException e) {
LOG.error("Failed to create vault directory.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
return;
}
if (showRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
showRecoveryKeyScene();
} else if (skipRecoveryKey.equals(recoveryKeyChoice.getSelectedToggle())) {
@@ -144,15 +134,15 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get();
processing.set(true);
Tasks.create(() -> {
initializeVault(pathToVault);
createVault(pathToVault);
return recoveryKeyFactory.createRecoveryKey(pathToVault, newPasswordSceneController.passwordField.getCharacters());
}).onSuccess(recoveryKey -> {
initializationSucceeded(pathToVault);
creationSucceeded(pathToVault);
recoveryKeyProperty.set(recoveryKey);
window.setScene(recoveryKeyScene.get());
}).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
LOG.error("Failed to create vault.", e);
appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> {
processing.set(false);
}).runOnce(executor);
@@ -162,19 +152,22 @@ public class CreateNewVaultPasswordController implements FxController {
Path pathToVault = vaultPathProperty.get();
processing.set(true);
Tasks.create(() -> {
initializeVault(pathToVault);
createVault(pathToVault);
}).onSuccess(() -> {
initializationSucceeded(pathToVault);
creationSucceeded(pathToVault);
window.setScene(successScene.get());
}).onError(IOException.class, e -> {
LOG.error("Failed to initialize vault.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
LOG.error("Failed to create vault.", e);
appWindows.showErrorWindow(e, window, window.getScene());
}).andFinally(() -> {
processing.set(false);
}).runOnce(executor);
}
private void initializeVault(Path path) throws IOException {
private void createVault(Path path) throws IOException {
// 0. create directory
Files.createDirectory(path);
// 1. write masterkey:
Path masterkeyFilePath = path.resolve(MASTERKEY_FILENAME);
try (Masterkey masterkey = Masterkey.generate(csprng)) {
@@ -193,7 +186,7 @@ public class CreateNewVaultPasswordController implements FxController {
ch.write(US_ASCII.encode(readmeGenerator.createVaultAccessLocationReadmeRtf()));
}
} catch (CryptoException e) {
throw new IOException("Failed initialize vault.", e);
throw new IOException("Vault initialization failed", e);
}
}
@@ -206,7 +199,7 @@ public class CreateNewVaultPasswordController implements FxController {
LOG.info("Created vault at {}", path);
}
private void initializationSucceeded(Path pathToVault) {
private void creationSucceeded(Path pathToVault) {
try {
Vault newVault = vaultListManager.add(pathToVault);
vaultProperty.set(newVault);
@@ -1,23 +1,15 @@
package org.cryptomator.ui.addvaultwizard;
import org.cryptomator.common.LocationPreset;
import javax.inject.Inject;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@AddVaultWizardScoped
public class LocationPresets {
private static final String USER_HOME = System.getProperty("user.home");
private static final String[] ICLOUDDRIVE_LOCATIONS = {"~/Library/Mobile Documents/iCloud~com~setolabs~Cryptomator/Documents", "~/iCloudDrive/iCloud~com~setolabs~Cryptomator"};
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive/My Drive", "~/Google Drive"};
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
public class ObservedLocationPresets {
private final ReadOnlyObjectProperty<Path> iclouddriveLocation;
private final ReadOnlyObjectProperty<Path> dropboxLocation;
@@ -33,13 +25,13 @@ public class LocationPresets {
private final BooleanBinding foundPcloud;
@Inject
public LocationPresets() {
this.iclouddriveLocation = new SimpleObjectProperty<>(existingWritablePath(ICLOUDDRIVE_LOCATIONS));
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS));
this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS));
this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS));
public ObservedLocationPresets() {
this.iclouddriveLocation = new SimpleObjectProperty<>(LocationPreset.ICLOUDDRIVE.existingPath());
this.dropboxLocation = new SimpleObjectProperty<>(LocationPreset.DROPBOX.existingPath());
this.gdriveLocation = new SimpleObjectProperty<>(LocationPreset.GDRIVE.existingPath());
this.onedriveLocation = new SimpleObjectProperty<>(LocationPreset.ONEDRIVE.existingPath());
this.megaLocation = new SimpleObjectProperty<>(LocationPreset.MEGA.existingPath());
this.pcloudLocation = new SimpleObjectProperty<>(LocationPreset.PCLOUD.existingPath());
this.foundIclouddrive = iclouddriveLocation.isNotNull();
this.foundDropbox = dropboxLocation.isNotNull();
this.foundGdrive = gdriveLocation.isNotNull();
@@ -48,24 +40,6 @@ public class LocationPresets {
this.foundPcloud = pcloudLocation.isNotNull();
}
private static Path existingWritablePath(String... candidates) {
for (String candidate : candidates) {
Path path = Paths.get(resolveHomePath(candidate));
if (Files.isDirectory(path)) {
return path;
}
}
return null;
}
private static String resolveHomePath(String path) {
if (path.startsWith("~/")) {
return USER_HOME + path.substring(1);
} else {
return path;
}
}
/* Observables */
public ReadOnlyObjectProperty<Path> iclouddriveLocationProperty() {
@@ -8,10 +8,10 @@ import org.cryptomator.cryptolib.api.InvalidPassphraseException;
import org.cryptomator.cryptolib.common.MasterkeyFileAccess;
import org.cryptomator.integrations.keychain.KeychainAccessException;
import org.cryptomator.ui.common.Animations;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.NewPasswordController;
import org.cryptomator.ui.controls.NiceSecurePasswordField;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +26,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.security.SecureRandom;
import static org.cryptomator.common.Constants.MASTERKEY_BACKUP_SUFFIX;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -38,9 +37,8 @@ public class ChangePasswordController implements FxController {
private final Stage window;
private final Vault vault;
private final ErrorComponent.Builder errorComponent;
private final FxApplicationWindows appWindows;
private final KeychainManager keychain;
private final SecureRandom csprng;
private final MasterkeyFileAccess masterkeyFileAccess;
public NiceSecurePasswordField oldPasswordField;
@@ -49,12 +47,11 @@ public class ChangePasswordController implements FxController {
public NewPasswordController newPasswordController;
@Inject
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, ErrorComponent.Builder errorComponent, KeychainManager keychain, SecureRandom csprng, MasterkeyFileAccess masterkeyFileAccess) {
public ChangePasswordController(@ChangePasswordWindow Stage window, @ChangePasswordWindow Vault vault, FxApplicationWindows appWindows, KeychainManager keychain, MasterkeyFileAccess masterkeyFileAccess) {
this.window = window;
this.vault = vault;
this.errorComponent = errorComponent;
this.appWindows = appWindows;
this.keychain = keychain;
this.csprng = csprng;
this.masterkeyFileAccess = masterkeyFileAccess;
}
@@ -95,7 +92,7 @@ public class ChangePasswordController implements FxController {
oldPasswordField.requestFocus();
} catch (IOException | CryptoException e) {
LOG.error("Password change failed. Unable to perform operation.", e);
errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
appWindows.showErrorWindow(e, window, window.getScene());
}
}
@@ -4,7 +4,6 @@ import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.Nullable;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;
@@ -16,34 +15,17 @@ public interface ErrorComponent {
@FxmlScene(FxmlFile.ERROR)
Scene scene();
default void showErrorScene() {
if (Platform.isFxApplicationThread()) {
show();
} else {
Platform.runLater(this::show);
}
}
private void show() {
default Stage show() {
Stage stage = window();
stage.setScene(scene());
stage.show();
return stage;
}
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder cause(Throwable cause);
@BindsInstance
Builder window(Stage window);
@BindsInstance
Builder returnToScene(@Nullable Scene previousScene);
ErrorComponent build();
@Subcomponent.Factory
interface Factory {
ErrorComponent create(@BindsInstance Throwable cause, @BindsInstance Stage window, @BindsInstance @Nullable Scene previousScene);
}
}
@@ -1,23 +1,24 @@
package org.cryptomator.ui.common;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import javax.inject.Inject;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.function.Consumer;
@FxApplicationScoped
public class StageFactory {
private final Consumer<Stage> initializer;
public StageFactory(Consumer<Stage> initializer) {
@Inject
public StageFactory(StageInitializer initializer) {
this.initializer = initializer;
}
public Stage create() {
return create(StageStyle.DECORATED);
}
public Stage create(StageStyle stageStyle) {
Stage stage = new Stage(stageStyle);
Stage stage = new Stage(StageStyle.DECORATED);
initializer.accept(stage);
return stage;
}
@@ -0,0 +1,32 @@
package org.cryptomator.ui.common;
import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.fxapp.FxApplicationScoped;
import javax.inject.Inject;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.util.List;
import java.util.function.Consumer;
/**
* Performs common setup for all stages
*/
@FxApplicationScoped
public class StageInitializer implements Consumer<Stage> {
private final List<Image> windowIcons;
@Inject
public StageInitializer() {
this.windowIcons = SystemUtils.IS_OS_MAC ? List.of() : List.of( //
new Image(StageInitializer.class.getResource("/img/window_icon_32.png").toString()), //
new Image(StageInitializer.class.getResource("/img/window_icon_512.png").toString()) //
);
}
@Override
public void accept(Stage stage) {
stage.getIcons().setAll(windowIcons);
}
}
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -53,7 +54,9 @@ public class VaultService {
*
* @param vault The vault to lock
* @param forced Whether to attempt a forced lock
* @deprecated use {@link org.cryptomator.ui.fxapp.FxApplicationWindows#startLockWorkflow(Vault, Stage)}
*/
@Deprecated
public void lock(Vault vault, boolean forced) {
executorService.execute(createLockTask(vault, forced));
}
@@ -90,7 +93,7 @@ public class VaultService {
* @return Meta-Task that waits until all vaults are locked or fails after the first failure of a subtask
*/
public Task<Collection<Vault>> createLockAllTask(Collection<Vault> vaults, boolean forced) {
List<Task<Vault>> lockTasks = vaults.stream().map(v -> new LockVaultTask(v, forced)).collect(Collectors.toUnmodifiableList());
List<Task<Vault>> lockTasks = vaults.stream().<Task<Vault>>map(v -> new LockVaultTask(v, forced)).toList();
lockTasks.forEach(executorService::execute);
Task<Collection<Vault>> task = new WaitForTasksTask(lockTasks);
String vaultNames = vaults.stream().map(Vault::getDisplayName).collect(Collectors.joining(", "));
@@ -1,14 +1,14 @@
package org.cryptomator.ui.launcher;
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.launcher.AppLaunchEvent;
import org.cryptomator.ui.common.VaultService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.application.Platform;
import java.io.IOException;
import java.nio.file.Path;
@@ -17,22 +17,25 @@ import java.util.concurrent.ExecutorService;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_EXT;
@Singleton
// TODO: use message bus
@FxApplicationScoped
class AppLaunchEventHandler {
private static final Logger LOG = LoggerFactory.getLogger(AppLaunchEventHandler.class);
private final BlockingQueue<AppLaunchEvent> launchEventQueue;
private final ExecutorService executorService;
private final FxApplicationStarter fxApplicationStarter;
private final FxApplicationWindows appWindows;
private final VaultListManager vaultListManager;
private final VaultService vaultService;
@Inject
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationStarter fxApplicationStarter, VaultListManager vaultListManager) {
public AppLaunchEventHandler(@Named("launchEventQueue") BlockingQueue<AppLaunchEvent> launchEventQueue, ExecutorService executorService, FxApplicationWindows appWindows, VaultListManager vaultListManager, VaultService vaultService) {
this.launchEventQueue = launchEventQueue;
this.executorService = executorService;
this.fxApplicationStarter = fxApplicationStarter;
this.appWindows = appWindows;
this.vaultListManager = vaultListManager;
this.vaultService = vaultService;
}
public void startHandlingLaunchEvents() {
@@ -52,14 +55,12 @@ class AppLaunchEventHandler {
}
private void handleLaunchEvent(AppLaunchEvent event) {
switch (event.getType()) {
case REVEAL_APP -> fxApplicationStarter.get().thenAccept(FxApplication::showMainWindow);
case OPEN_FILE -> fxApplicationStarter.get().thenRun(() -> {
Platform.runLater(() -> {
event.getPathsToOpen().forEach(this::addOrRevealVault);
});
switch (event.type()) {
case REVEAL_APP -> appWindows.showMainWindow();
case OPEN_FILE -> Platform.runLater(() -> {
event.pathsToOpen().forEach(this::addOrRevealVault);
});
default -> LOG.warn("Unsupported event type: {}", event.getType());
default -> LOG.warn("Unsupported event type: {}", event.type());
}
}
@@ -75,7 +76,7 @@ class AppLaunchEventHandler {
}
if (v.isUnlocked()) {
fxApplicationStarter.get().thenAccept(app -> app.getVaultService().reveal(v));
vaultService.reveal(v);
}
LOG.debug("Added vault {}", potentialVaultPath);
} catch (IOException e) {
@@ -0,0 +1,26 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Inject;
import javafx.collections.ObservableList;
@FxApplicationScoped
public class AutoUnlocker {
private final ObservableList<Vault> vaults;
private final FxApplicationWindows appWindows;
@Inject
public AutoUnlocker(ObservableList<Vault> vaults, FxApplicationWindows appWindows) {
this.vaults = vaults;
this.appWindows = appWindows;
}
public void unlock() {
vaults.stream().filter(Vault::isLocked).filter(v -> v.getVaultSettings().unlockAfterStartup().get()).forEach(v -> {
appWindows.startUnlockWorkflow(v, null);
});
}
}
@@ -0,0 +1,20 @@
package org.cryptomator.ui.fxapp;
import javafx.application.Platform;
import java.awt.desktop.QuitResponse;
record ExitingQuitResponse(QuitResponse delegate) implements QuitResponse {
@Override
public void performQuit() {
Platform.exit();
// TODO wait a moment for javafx to terminate?
delegate.performQuit();
}
@Override
public void cancelQuit() {
delegate.cancelQuit();
}
}
@@ -1,213 +1,74 @@
package org.cryptomator.ui.fxapp;
import dagger.Lazy;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.stage.Stage;
import javafx.stage.Window;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Provider;
import java.awt.desktop.QuitResponse;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import javafx.application.Platform;
@FxApplicationScoped
public class FxApplication extends Application {
public class FxApplication {
private static final Logger LOG = LoggerFactory.getLogger(FxApplication.class);
private final long startupTime;
private final Settings settings;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> preferencesWindow;
private final Lazy<QuitComponent> quitWindow;
private final Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider;
private final Provider<LockComponent.Builder> lockWorkflowBuilderProvider;
private final ErrorComponent.Builder errorWindowBuilder;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final VaultService vaultService;
private final LicenseHolder licenseHolder;
private final ObservableList<Window> visibleWindows;
private final BooleanBinding hasVisibleWindows;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
private final AppLaunchEventHandler launchEventHandler;
private final Lazy<TrayMenuComponent> trayMenu;
private final FxApplicationWindows appWindows;
private final FxApplicationStyle applicationStyle;
private final FxApplicationTerminator applicationTerminator;
private final AutoUnlocker autoUnlocker;
@Inject
FxApplication(Settings settings, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Provider<UnlockComponent.Builder> unlockWorkflowBuilderProvider, Provider<LockComponent.Builder> lockWorkflowBuilderProvider, Lazy<QuitComponent> quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional<TrayIntegrationProvider> trayIntegration, Optional<UiAppearanceProvider> appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
FxApplication(@Named("startupTime") long startupTime, Settings settings, AppLaunchEventHandler launchEventHandler, Lazy<TrayMenuComponent> trayMenu, FxApplicationWindows appWindows, FxApplicationStyle applicationStyle, FxApplicationTerminator applicationTerminator, AutoUnlocker autoUnlocker) {
this.startupTime = startupTime;
this.settings = settings;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
this.quitWindow = quitWindow;
this.errorWindowBuilder = errorWindowBuilder;
this.trayIntegration = trayIntegration;
this.appearanceProvider = appearanceProvider;
this.vaultService = vaultService;
this.licenseHolder = licenseHolder;
this.visibleWindows = Stage.getWindows().filtered(Window::isShowing);
this.hasVisibleWindows = Bindings.isNotEmpty(visibleWindows);
this.launchEventHandler = launchEventHandler;
this.trayMenu = trayMenu;
this.appWindows = appWindows;
this.applicationStyle = applicationStyle;
this.applicationTerminator = applicationTerminator;
this.autoUnlocker = autoUnlocker;
}
public void start() {
LOG.trace("FxApplication.start()");
Platform.setImplicitExit(false);
applicationStyle.initialize();
appWindows.initialize();
applicationTerminator.initialize();
hasVisibleWindows.addListener(this::hasVisibleStagesChanged);
settings.theme().addListener(this::appThemeChanged);
loadSelectedStyleSheet(settings.theme().get());
}
@Override
public void start(Stage stage) {
throw new UnsupportedOperationException("Use start() instead.");
}
private void hasVisibleStagesChanged(@SuppressWarnings("unused") ObservableValue<? extends Boolean> observableValue, @SuppressWarnings("unused") boolean oldValue, boolean newValue) {
LOG.debug("has visible stages: {}", newValue);
if (newValue) {
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
// init system tray
final boolean hasTrayIcon;
if (settings.showTrayIcon().get() && trayMenu.get().isSupported()) {
trayMenu.get().initializeTrayIcon();
Platform.setImplicitExit(false); // don't quit when closing all windows
hasTrayIcon = true;
} else {
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
hasTrayIcon = false;
}
}
public void showPreferencesWindow(SelectedPreferencesTab selectedTab) {
Platform.runLater(() -> {
preferencesWindow.get().showPreferencesWindow(selectedTab);
LOG.debug("Showing Preferences");
});
}
public CompletionStage<Stage> showMainWindow() {
CompletableFuture<Stage> future = new CompletableFuture<>();
Platform.runLater(() -> {
var win = mainWindow.get().showMainWindow();
LOG.debug("Showing MainWindow");
future.complete(win);
});
return future;
}
public void startUnlockWorkflow(Vault vault, Optional<Stage> owner) {
Platform.runLater(() -> {
if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
} else {
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
// show main window
appWindows.showMainWindow().thenAccept(stage -> {
if (settings.startHidden().get()) {
if (hasTrayIcon) {
stage.hide();
} else {
stage.setIconified(true);
}
}
LOG.debug("Main window initialized after {}ms", System.currentTimeMillis() - startupTime);
}).exceptionally(error -> {
LOG.error("Failed to show main window", error);
return null;
});
}
public void startLockWorkflow(Vault vault, Optional<Stage> owner) {
Platform.runLater(() -> {
if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
} else {
showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
}
});
}
public void showQuitWindow(QuitResponse response) {
Platform.runLater(() -> {
quitWindow.get().showQuitWindow(response);
LOG.debug("Showing QuitWindow");
});
}
public VaultService getVaultService() {
return vaultService;
}
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
try {
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to disable automatic theme switching.");
}
}
loadSelectedStyleSheet(newValue);
}
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
case AUTOMATIC -> {
appearanceProvider.ifPresent(appearanceProvider -> {
try {
appearanceProvider.addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic theme switching.");
}
});
applySystemTheme();
}
}
}
private void systemInterfaceThemeChanged(Theme theme) {
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
}
}
private void applySystemTheme() {
if (appearanceProvider.isPresent()) {
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
} else {
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
applyLightTheme();
}
}
private void applyLightTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/light_theme.css").toString());
appearanceProvider.ifPresent(appearanceProvider -> {
appearanceProvider.adjustToTheme(Theme.LIGHT);
});
}
private void applyDarkTheme() {
Application.setUserAgentStylesheet(getClass().getResource("/css/dark_theme.css").toString());
appearanceProvider.ifPresent(appearanceProvider -> {
appearanceProvider.adjustToTheme(Theme.DARK);
});
launchEventHandler.startHandlingLaunchEvents();
autoUnlocker.unlock();
}
}
@@ -5,8 +5,12 @@
*******************************************************************************/
package org.cryptomator.ui.fxapp;
import dagger.BindsInstance;
import dagger.Subcomponent;
import javafx.application.Application;
import javafx.stage.Stage;
@FxApplicationScoped
@Subcomponent(modules = FxApplicationModule.class)
public interface FxApplicationComponent {
@@ -16,6 +20,12 @@ public interface FxApplicationComponent {
@Subcomponent.Builder
interface Builder {
@BindsInstance
Builder fxApplication(Application application);
@BindsInstance
Builder primaryStage(@PrimaryStage Stage primaryStage);
FxApplicationComponent build();
}
@@ -5,7 +5,6 @@
*******************************************************************************/
package org.cryptomator.ui.fxapp;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import org.apache.commons.lang3.SystemUtils;
@@ -15,67 +14,46 @@ import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import javax.inject.Named;
import javafx.application.Application;
import javafx.collections.ObservableSet;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Collections;
import java.util.List;
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {TrayMenuComponent.class, MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, LockComponent.class, QuitComponent.class, ErrorComponent.class})
abstract class FxApplicationModule {
@Provides
@Named("windowIcons")
@FxApplicationScoped
static List<Image> provideWindowIcons() {
if (SystemUtils.IS_OS_MAC) {
return Collections.emptyList();
}
try {
return List.of( //
createImageFromResource("/img/window_icon_32.png"), //
createImageFromResource("/img/window_icon_512.png") //
);
} catch (IOException e) {
throw new UncheckedIOException("Failed to load embedded resource.", e);
}
}
@Provides
@FxApplicationScoped
static StageFactory provideStageFactory(@Named("windowIcons") List<Image> windowIcons) {
return new StageFactory(stage -> {
stage.getIcons().addAll(windowIcons);
});
}
private static Image createImageFromResource(String resourceName) throws IOException {
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
return new Image(in);
}
}
@Binds
abstract Application bindApplication(FxApplication application);
@Provides
@FxApplicationScoped
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
static MainWindowComponent provideMainWindowComponent(MainWindowComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
static PreferencesComponent providePreferencesComponent(PreferencesComponent.Builder builder) {
return builder.build();
}
@Provides
@FxApplicationScoped
static QuitComponent provideQuitComponent(QuitComponent.Builder builder) {
return builder.build();
}
@@ -0,0 +1,108 @@
package org.cryptomator.ui.fxapp;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import java.util.Optional;
@FxApplicationScoped
public class FxApplicationStyle {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStyle.class);
private final Settings settings;
private final Optional<UiAppearanceProvider> appearanceProvider;
private final LicenseHolder licenseHolder;
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject
public FxApplicationStyle(Settings settings, Optional<UiAppearanceProvider> appearanceProvider, LicenseHolder licenseHolder){
this.settings = settings;
this.appearanceProvider = appearanceProvider;
this.licenseHolder = licenseHolder;
}
public void initialize() {
settings.theme().addListener(this::appThemeChanged);
loadSelectedStyleSheet(settings.theme().get());
}
private void appThemeChanged(@SuppressWarnings("unused") ObservableValue<? extends UiTheme> observable, @SuppressWarnings("unused") UiTheme oldValue, UiTheme newValue) {
if (appearanceProvider.isPresent() && oldValue == UiTheme.AUTOMATIC && newValue != UiTheme.AUTOMATIC) {
try {
appearanceProvider.get().removeListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to disable automatic theme switching.");
}
}
loadSelectedStyleSheet(newValue);
}
private void loadSelectedStyleSheet(UiTheme desiredTheme) {
UiTheme theme = licenseHolder.isValidLicense() ? desiredTheme : UiTheme.LIGHT;
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
case AUTOMATIC -> {
appearanceProvider.ifPresent(provider -> {
try {
provider.addListener(systemInterfaceThemeListener);
} catch (UiAppearanceException e) {
LOG.error("Failed to enable automatic theme switching.");
}
});
applySystemTheme();
}
}
}
private void systemInterfaceThemeChanged(Theme theme) {
switch (theme) {
case LIGHT -> applyLightTheme();
case DARK -> applyDarkTheme();
}
}
private void applySystemTheme() {
if (appearanceProvider.isPresent()) {
systemInterfaceThemeChanged(appearanceProvider.get().getSystemTheme());
} else {
LOG.warn("No UiAppearanceProvider present, assuming LIGHT theme...");
applyLightTheme();
}
}
private void applyLightTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/light_theme.bss")) //
.orElse(getClass().getResource("/css/light_theme.css"));
if (stylesheet == null) {
LOG.warn("Failed to load light_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.LIGHT));
}
}
private void applyDarkTheme() {
var stylesheet = Optional //
.ofNullable(getClass().getResource("/css/dark_theme.bss")) //
.orElse(getClass().getResource("/css/dark_theme.css"));
if (stylesheet == null) {
LOG.warn("Failed to load dark_theme stylesheet");
} else {
Application.setUserAgentStylesheet(stylesheet.toString());
appearanceProvider.ifPresent(provider -> provider.adjustToTheme(Theme.DARK));
}
}
}
@@ -0,0 +1,134 @@
package org.cryptomator.ui.fxapp;
import com.google.common.base.Preconditions;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import java.awt.Desktop;
import java.awt.desktop.QuitResponse;
import java.awt.desktop.QuitStrategy;
import java.util.EnumSet;
import java.util.EventObject;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@FxApplicationScoped
public class FxApplicationTerminator {
private static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationTerminator.class);
private final ObservableList<Vault> vaults;
private final ShutdownHook shutdownHook;
private final FxApplicationWindows appWindows;
private final AtomicBoolean allowQuitWithoutPrompt = new AtomicBoolean();
@Inject
public FxApplicationTerminator(ObservableList<Vault> vaults, ShutdownHook shutdownHook, FxApplicationWindows appWindows) {
this.vaults = vaults;
this.shutdownHook = shutdownHook;
this.appWindows = appWindows;
}
public void initialize() {
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
Desktop desktop = Desktop.getDesktop();
// register quit handler
if (desktop.isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
desktop.setQuitHandler(this::handleQuitRequest);
}
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
if (desktop.isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
desktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
}
// allow sudden termination?
vaultListChanged(vaults);
vaults.addListener(this::vaultListChanged);
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
}
/**
* Gracefully terminates the application.
*/
public void terminate() {
handleQuitRequest(null, new NoopQuitResponse());
}
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
boolean allowSuddenTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
boolean stateChanged = allowQuitWithoutPrompt.compareAndSet(!allowSuddenTermination, allowSuddenTermination);
Desktop desktop = Desktop.getDesktop();
if (stateChanged && desktop.isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
if (allowSuddenTermination) {
LOG.debug("Enabling sudden termination");
desktop.enableSuddenTermination();
} else {
LOG.debug("Disabling sudden termination");
desktop.disableSuddenTermination();
}
}
}
/**
* Asks the app to quit. If confirmed, the JavaFX application will exit before giving a {@code response}.
*
* @param e ignored
* @param response a quit response that will be {@link ExitingQuitResponse decorated in order to exit the JavaFX application}.
*/
private void handleQuitRequest(@SuppressWarnings("unused") @Nullable EventObject e, QuitResponse response) {
var exitingResponse = new ExitingQuitResponse(response);
if (allowQuitWithoutPrompt.get()) {
exitingResponse.performQuit();
} else {
appWindows.showQuitWindow(exitingResponse);
}
}
private void forceUnmountRemainingVaults() {
for (Vault vault : vaults) {
if (vault.isUnlocked()) {
try {
vault.lock(true);
} catch (Volume.VolumeException e) {
LOG.error("Failed to unmount vault " + vault.getPath(), e);
} catch (LockNotCompletedException e) {
LOG.error("Failed to lock vault " + vault.getPath(), e);
}
}
}
}
/**
* A dummy QuitResponse that ignores the response.
*
* To be used with {@link #handleQuitRequest(EventObject, QuitResponse)} if the invoking method is not interested in the response.
*/
private static class NoopQuitResponse implements QuitResponse {
@Override
public void performQuit() {
// no-op
}
@Override
public void cancelQuit() {
// no-op
}
}
}
@@ -0,0 +1,155 @@
package org.cryptomator.ui.fxapp;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
import org.cryptomator.ui.preferences.PreferencesComponent;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.quit.QuitComponent;
import org.cryptomator.ui.unlock.UnlockComponent;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.Window;
import java.awt.Desktop;
import java.awt.desktop.AppReopenedListener;
import java.awt.desktop.QuitResponse;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
@FxApplicationScoped
public class FxApplicationWindows {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationWindows.class);
private final Stage primaryStage;
private final Optional<TrayIntegrationProvider> trayIntegration;
private final Lazy<MainWindowComponent> mainWindow;
private final Lazy<PreferencesComponent> preferencesWindow;
private final Lazy<QuitComponent> quitWindow;
private final UnlockComponent.Factory unlockWorkflowFactory;
private final LockComponent.Factory lockWorkflowFactory;
private final ErrorComponent.Factory errorWindowFactory;
private final ExecutorService executor;
private final FilteredList<Window> visibleWindows;
@Inject
public FxApplicationWindows(@PrimaryStage Stage primaryStage, Optional<TrayIntegrationProvider> trayIntegration, Lazy<MainWindowComponent> mainWindow, Lazy<PreferencesComponent> preferencesWindow, Lazy<QuitComponent> quitWindow, UnlockComponent.Factory unlockWorkflowFactory, LockComponent.Factory lockWorkflowFactory, ErrorComponent.Factory errorWindowFactory, ExecutorService executor) {
this.primaryStage = primaryStage;
this.trayIntegration = trayIntegration;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
this.quitWindow = quitWindow;
this.unlockWorkflowFactory = unlockWorkflowFactory;
this.lockWorkflowFactory = lockWorkflowFactory;
this.errorWindowFactory = errorWindowFactory;
this.executor = executor;
this.visibleWindows = Window.getWindows().filtered(Window::isShowing);
}
public void initialize() {
Preconditions.checkState(Desktop.isDesktopSupported(), "java.awt.Desktop not supported");
Desktop desktop = Desktop.getDesktop();
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) {
desktop.setPreferencesHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ANY));
}
// register preferences shortcut
if (desktop.isSupported(Desktop.Action.APP_ABOUT)) {
desktop.setAboutHandler(evt -> showPreferencesWindow(SelectedPreferencesTab.ABOUT));
}
// register app reopen listener
if (desktop.isSupported(Desktop.Action.APP_EVENT_REOPENED)) {
desktop.addAppEventListener((AppReopenedListener) e -> showMainWindow());
}
// observe visible windows
if (trayIntegration.isPresent()) {
visibleWindows.addListener(this::visibleWindowsChanged);
}
}
private void visibleWindowsChanged(ListChangeListener.Change<? extends Window> change) {
int visibleWindows = change.getList().size();
LOG.debug("visible windows: {}", visibleWindows);
if (visibleWindows > 0) {
trayIntegration.ifPresent(TrayIntegrationProvider::restoredFromTray);
} else {
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
}
}
public CompletionStage<Stage> showMainWindow() {
return CompletableFuture.supplyAsync(mainWindow.get()::showMainWindow, Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showPreferencesWindow(SelectedPreferencesTab selectedTab) {
return CompletableFuture.supplyAsync(() -> preferencesWindow.get().showPreferencesWindow(selectedTab), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Stage> showQuitWindow(QuitResponse response) {
return CompletableFuture.supplyAsync(() -> quitWindow.get().showQuitWindow(response), Platform::runLater).whenComplete(this::reportErrors);
}
public CompletionStage<Void> startUnlockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING), "Vault not locked.");
LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
return unlockWorkflowFactory.create(vault, owner).unlockWorkflow();
}, Platform::runLater) //
.thenCompose(unlockWorkflow -> CompletableFuture.runAsync(unlockWorkflow, executor)) //
.exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null;
});
}
public CompletionStage<Void> startLockWorkflow(Vault vault, @Nullable Stage owner) {
return CompletableFuture.supplyAsync(() -> {
Preconditions.checkState(vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING), "Vault not unlocked.");
LOG.debug("Start lock workflow for {}", vault.getDisplayName());
return lockWorkflowFactory.create(vault, owner).lockWorkflow();
}, Platform::runLater) //
.thenCompose(lockWorkflow -> CompletableFuture.runAsync(lockWorkflow, executor)) //
.exceptionally(e -> {
showErrorWindow(e, owner == null ? primaryStage : owner, null);
return null;
});
}
/**
* Displays the generic error scene in the given window.
*
* @param cause The exception to show
* @param window What window to display the scene in
* @param previousScene To what scene to return to when pressing "back". Back button will be hidden, if <code>null</code>
* @return A
*/
public CompletionStage<Stage> showErrorWindow(Throwable cause, Stage window, @Nullable Scene previousScene) {
return CompletableFuture.supplyAsync(() -> errorWindowFactory.create(cause, window, previousScene).show(), Platform::runLater).whenComplete(this::reportErrors);
}
private void reportErrors(@Nullable Stage stage, @Nullable Throwable error) {
if (error != null) {
LOG.error("Failed to display stage", error);
}
}
}
@@ -0,0 +1,14 @@
package org.cryptomator.ui.fxapp;
import javax.inject.Qualifier;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface PrimaryStage {
}
@@ -43,7 +43,9 @@ public abstract class UpdateCheckerModule {
@FxApplicationScoped
static Optional<HttpClient> provideHttpClient() {
try {
return Optional.of(HttpClient.newHttpClient());
return Optional.of(HttpClient.newBuilder() //
.followRedirects(HttpClient.Redirect.NORMAL) // from version 1.6.11 onwards, Cryptomator can follow redirects, in case this URL ever changes
.build());
} catch (UncheckedIOException e) {
LOG.error("HttpClient for update check cannot be created.", e);
return Optional.empty();
@@ -1,9 +1,8 @@
package org.cryptomator.ui.health;
import com.google.common.base.Preconditions;
import dagger.Lazy;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,9 +14,7 @@ import javafx.beans.property.ObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.stage.Stage;
@@ -37,7 +34,7 @@ public class CheckListController implements FxController {
private final ObjectProperty<Check> selectedCheck;
private final BooleanBinding mainRunStarted; //TODO: rerunning not considered for now
private final BooleanBinding somethingsRunning;
private final Lazy<ErrorComponent.Builder> errorComponentBuilder;
private final FxApplicationWindows appWindows;
private final IntegerBinding chosenTaskCount;
private final BooleanBinding anyCheckSelected;
private final CheckListCellFactory listCellFactory;
@@ -46,7 +43,7 @@ public class CheckListController implements FxController {
public ListView<Check> checksListView;
@Inject
public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, Lazy<ErrorComponent.Builder> errorComponentBuilder, CheckListCellFactory listCellFactory) {
public CheckListController(@HealthCheckWindow Stage window, List<Check> checks, CheckExecutor checkExecutor, ReportWriter reportWriteTask, ObjectProperty<Check> selectedCheck, FxApplicationWindows appWindows, CheckListCellFactory listCellFactory) {
this.window = window;
this.checks = FXCollections.observableList(checks, Check::observables);
this.checkExecutor = checkExecutor;
@@ -54,7 +51,7 @@ public class CheckListController implements FxController {
this.chosenChecks = this.checks.filtered(Check::isChosenForExecution);
this.reportWriter = reportWriteTask;
this.selectedCheck = selectedCheck;
this.errorComponentBuilder = errorComponentBuilder;
this.appWindows = appWindows;
this.chosenTaskCount = Bindings.size(this.chosenChecks);
this.mainRunStarted = Bindings.isEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.RUNNABLE));
this.somethingsRunning = Bindings.isNotEmpty(this.checks.filtered(c -> c.getState() == Check.CheckState.SCHEDULED || c.getState() == Check.CheckState.RUNNING));
@@ -104,7 +101,7 @@ public class CheckListController implements FxController {
reportWriter.writeReport(chosenChecks);
} catch (IOException e) {
LOG.error("Failed to write health check report.", e);
errorComponentBuilder.get().cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
appWindows.showErrorWindow(e, window, window.getScene());
}
}
@@ -7,10 +7,10 @@ import org.cryptomator.cryptofs.VaultConfig;
import org.cryptomator.cryptofs.VaultConfigLoadException;
import org.cryptomator.cryptofs.VaultKeyInvalidException;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.keyloading.KeyLoadingStrategy;
import org.cryptomator.ui.unlock.UnlockCancelledException;
import org.slf4j.Logger;
@@ -40,10 +40,10 @@ public class StartController implements FxController {
private final AtomicReference<Masterkey> masterkeyRef;
private final AtomicReference<VaultConfig> vaultConfigRef;
private final Lazy<Scene> checkScene;
private final Lazy<ErrorComponent.Builder> errorComponent;
private final FxApplicationWindows appWindows;
@Inject
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, Lazy<ErrorComponent.Builder> errorComponent, @Named("unlockWindow") Stage unlockWindow) {
public StartController(@HealthCheckWindow Stage window, @HealthCheckWindow Vault vault, @HealthCheckWindow KeyLoadingStrategy keyLoadingStrategy, ExecutorService executor, AtomicReference<Masterkey> masterkeyRef, AtomicReference<VaultConfig> vaultConfigRef, @FxmlScene(FxmlFile.HEALTH_CHECK_LIST) Lazy<Scene> checkScene, FxApplicationWindows appWindows, @Named("unlockWindow") Stage unlockWindow) {
this.window = window;
this.unlockWindow = unlockWindow;
this.vaultConfig = vault.getVaultConfigCache();
@@ -52,7 +52,7 @@ public class StartController implements FxController {
this.masterkeyRef = masterkeyRef;
this.vaultConfigRef = vaultConfigRef;
this.checkScene = checkScene;
this.errorComponent = errorComponent;
this.appWindows = appWindows;
}
@FXML
@@ -106,10 +106,10 @@ public class StartController implements FxController {
// ok
} else if (e instanceof VaultKeyInvalidException) {
LOG.error("Invalid key"); //TODO: specific error screen
errorComponent.get().window(window).cause(e).build().showErrorScene();
appWindows.showErrorWindow(e, window, null);
} else {
LOG.error("Failed to load key.", e);
errorComponent.get().window(window).cause(e).build().showErrorScene();
appWindows.showErrorWindow(e, window, null);
}
}
@@ -15,6 +15,8 @@ import java.nio.file.Path;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@ChooseMasterkeyFileScoped
public class ChooseMasterkeyFileController implements FxController {
@@ -46,7 +48,7 @@ public class ChooseMasterkeyFileController implements FxController {
LOG.trace("proceed()");
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("unlock.chooseMasterkey.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("unlock.chooseMasterkey.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
LOG.debug("Chose masterkey file: {}", masterkeyFile);
@@ -1,28 +0,0 @@
package org.cryptomator.ui.launcher;
import java.nio.file.Path;
import java.util.Collection;
public class AppLaunchEvent {
private final EventType type;
private final Collection<Path> pathsToOpen;
public enum EventType {
REVEAL_APP,
OPEN_FILE
}
public AppLaunchEvent(EventType type, Collection<Path> pathsToOpen) {
this.type = type;
this.pathsToOpen = pathsToOpen;
}
public EventType getType() {
return type;
}
public Collection<Path> getPathsToOpen() {
return pathsToOpen;
}
}
@@ -1,146 +0,0 @@
package org.cryptomator.ui.launcher;
import org.cryptomator.common.ShutdownHook;
import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javafx.application.Platform;
import javafx.beans.Observable;
import javafx.collections.ObservableList;
import java.awt.Desktop;
import java.awt.EventQueue;
import java.awt.desktop.AboutEvent;
import java.awt.desktop.QuitResponse;
import java.awt.desktop.QuitStrategy;
import java.util.EnumSet;
import java.util.EventObject;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.cryptomator.common.vaults.VaultState.Value.*;
@Singleton
public class AppLifecycleListener {
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
public static final Set<VaultState.Value> STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private final FxApplicationStarter fxApplicationStarter;
private final CountDownLatch shutdownLatch;
private final ObservableList<Vault> vaults;
private final AtomicBoolean allowQuitWithoutPrompt;
@Inject
AppLifecycleListener(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList<Vault> vaults) {
this.fxApplicationStarter = fxApplicationStarter;
this.shutdownLatch = shutdownLatch;
this.vaults = vaults;
this.allowQuitWithoutPrompt = new AtomicBoolean(true);
vaults.addListener(this::vaultListChanged);
// register preferences shortcut
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
Desktop.getDesktop().setPreferencesHandler(this::showPreferencesWindow);
}
// register preferences shortcut
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_ABOUT)) {
Desktop.getDesktop().setAboutHandler(this::showAboutWindow);
}
// register quit handler
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
Desktop.getDesktop().setQuitHandler(this::handleQuitRequest);
}
// set quit strategy (cmd+q would call `System.exit(0)` otherwise)
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_STRATEGY)) {
Desktop.getDesktop().setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
}
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
}
/**
* Gracefully terminates the application.
*/
public void quit() {
handleQuitRequest(null, new QuitResponse() {
@Override
public void performQuit() {
// no-op
}
@Override
public void cancelQuit() {
// no-op
}
});
}
private void handleQuitRequest(@SuppressWarnings("unused") EventObject e, QuitResponse response) {
QuitResponse decoratedQuitResponse = decorateQuitResponse(response);
if (allowQuitWithoutPrompt.get()) {
decoratedQuitResponse.performQuit();
} else {
fxApplicationStarter.get().thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
}
}
private QuitResponse decorateQuitResponse(QuitResponse originalQuitResponse) {
return new QuitResponse() {
@Override
public void performQuit() {
Platform.exit(); // will be no-op, if JavaFX never started.
shutdownLatch.countDown(); // main thread is waiting for this latch
originalQuitResponse.performQuit();
}
@Override
public void cancelQuit() {
originalQuitResponse.cancelQuit();
}
};
}
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
assert Platform.isFxApplicationThread();
boolean allVaultsAllowTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
boolean suddenTerminationChanged = allowQuitWithoutPrompt.compareAndSet(!allVaultsAllowTermination, allVaultsAllowTermination);
if (suddenTerminationChanged) {
LOG.debug("Allow quitting without prompt: {}", allVaultsAllowTermination);
}
}
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
}
private void showAboutWindow(@SuppressWarnings("unused") AboutEvent aboutEvent) {
fxApplicationStarter.get().thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ABOUT));
}
private void forceUnmountRemainingVaults() {
for (Vault vault : vaults) {
if (vault.isUnlocked()) {
try {
vault.lock(true);
} catch (Volume.VolumeException e) {
LOG.error("Failed to unmount vault " + vault.getPath(), e);
} catch (LockNotCompletedException e) {
LOG.error("Failed to lock vault " + vault.getPath(), e);
}
}
}
}
}
@@ -1,54 +0,0 @@
package org.cryptomator.ui.launcher;
import dagger.Lazy;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.application.Platform;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
@Singleton
public class FxApplicationStarter {
private static final Logger LOG = LoggerFactory.getLogger(FxApplicationStarter.class);
private final Lazy<FxApplicationComponent> fxAppComponent;
private final ExecutorService executor;
private final AtomicBoolean started;
private final CompletableFuture<FxApplication> future;
@Inject
public FxApplicationStarter(Lazy<FxApplicationComponent> fxAppComponent, ExecutorService executor) {
this.fxAppComponent = fxAppComponent;
this.executor = executor;
this.started = new AtomicBoolean();
this.future = new CompletableFuture<>();
}
public CompletionStage<FxApplication> get() {
if (!started.getAndSet(true)) {
start();
}
return future;
}
private void start() {
executor.submit(() -> {
LOG.debug("Starting JavaFX runtime...");
Platform.startup(() -> {
assert Platform.isFxApplicationThread();
LOG.info("JavaFX Runtime started.");
FxApplication app = fxAppComponent.get().application();
app.start();
future.complete(app);
});
});
}
}
@@ -1,90 +0,0 @@
package org.cryptomator.ui.launcher;
import dagger.Lazy;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import javafx.collections.ObservableList;
import java.awt.Desktop;
import java.awt.SystemTray;
import java.awt.desktop.AppReopenedListener;
import java.util.Collection;
import java.util.Optional;
@Singleton
public class UiLauncher {
private static final Logger LOG = LoggerFactory.getLogger(UiLauncher.class);
private final Settings settings;
private final ObservableList<Vault> vaults;
private final Lazy<TrayMenuComponent> trayMenu;
private final FxApplicationStarter fxApplicationStarter;
private final AppLaunchEventHandler launchEventHandler;
private final Optional<TrayIntegrationProvider> trayIntegration;
@Inject
public UiLauncher(Settings settings, ObservableList<Vault> vaults, Lazy<TrayMenuComponent> trayMenu, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<TrayIntegrationProvider> trayIntegration) {
this.settings = settings;
this.vaults = vaults;
this.trayMenu = trayMenu;
this.fxApplicationStarter = fxApplicationStarter;
this.launchEventHandler = launchEventHandler;
this.trayIntegration = trayIntegration;
}
public void launch() {
boolean hidden = settings.startHidden().get();
if (SystemTray.isSupported() && settings.showTrayIcon().get()) {
trayMenu.get().initializeTrayIcon();
launch(true, hidden);
} else {
launch(false, hidden);
}
}
private void launch(boolean withTrayIcon, boolean hidden) {
// start hidden, minimized or normal?
if (withTrayIcon && hidden) {
LOG.debug("Hiding application...");
trayIntegration.ifPresent(TrayIntegrationProvider::minimizedToTray);
} else if (!withTrayIcon && hidden) {
LOG.debug("Minimizing application...");
showMainWindowAsync(true);
} else {
LOG.debug("Showing application...");
showMainWindowAsync(false);
}
// register app reopen listener
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(false));
// auto unlock
Collection<Vault> vaultsToAutoUnlock = vaults.filtered(this::shouldAttemptAutoUnlock);
if (!vaultsToAutoUnlock.isEmpty()) {
fxApplicationStarter.get().thenAccept(app -> {
for (Vault vault : vaultsToAutoUnlock) {
app.startUnlockWorkflow(vault, Optional.empty());
}
});
}
launchEventHandler.startHandlingLaunchEvents();
}
private boolean shouldAttemptAutoUnlock(Vault vault) {
return vault.isLocked() && vault.getVaultSettings().unlockAfterStartup().get();
}
private void showMainWindowAsync(boolean minimize) {
fxApplicationStarter.get().thenCompose(FxApplication::showMainWindow).thenAccept(win -> win.setIconified(minimize));
}
}
@@ -1,67 +0,0 @@
package org.cryptomator.ui.launcher;
import dagger.Module;
import dagger.Provides;
import org.cryptomator.common.PluginClassLoader;
import org.cryptomator.integrations.autostart.AutoStartProvider;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
import org.cryptomator.ui.fxapp.FxApplicationComponent;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.ServiceLoader;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Module(subcomponents = {TrayMenuComponent.class, FxApplicationComponent.class})
public abstract class UiLauncherModule {
@Provides
@Singleton
static TrayMenuComponent provideTrayMenuComponent(TrayMenuComponent.Builder builder) {
return builder.build();
}
@Provides
@Singleton
static FxApplicationComponent provideFxApplicationComponent(FxApplicationComponent.Builder builder) {
return builder.build();
}
@Provides
@Singleton
static Optional<UiAppearanceProvider> provideAppearanceProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(UiAppearanceProvider.class, classLoader).findFirst();
}
@Provides
@Singleton
static Optional<AutoStartProvider> provideAutostartProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(AutoStartProvider.class, classLoader).findFirst();
}
@Provides
@Singleton
static Optional<TrayIntegrationProvider> provideTrayIntegrationProvider(PluginClassLoader classLoader) {
return ServiceLoader.load(TrayIntegrationProvider.class, classLoader).findFirst();
}
@Provides
@Singleton
static ResourceBundle provideLocalization() {
return ResourceBundle.getBundle("i18n.strings");
}
@Provides
@Singleton
@Named("launchEventQueue")
static BlockingQueue<AppLaunchEvent> provideFileOpenRequests() {
return new ArrayBlockingQueue<>(10);
}
}
@@ -2,11 +2,11 @@ package org.cryptomator.ui.lock;
import dagger.BindsInstance;
import dagger.Subcomponent;
import org.cryptomator.common.Nullable;
import org.cryptomator.common.vaults.Vault;
import javax.inject.Named;
import javafx.stage.Stage;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@@ -25,15 +25,9 @@ public interface LockComponent {
return workflow;
}
@Subcomponent.Builder
interface Builder {
@BindsInstance
LockComponent.Builder vault(@LockWindow Vault vault);
@BindsInstance
LockComponent.Builder owner(@Named("lockWindowOwner") Optional<Stage> owner);
LockComponent build();
@Subcomponent.Factory
interface Factory {
LockComponent create(@BindsInstance @LockWindow Vault vault, @BindsInstance @Named("lockWindowOwner") @Nullable Stage owner);
}
}
@@ -12,6 +12,7 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.jetbrains.annotations.Nullable;
import javax.inject.Named;
import javax.inject.Provider;
@@ -19,7 +20,6 @@ import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.util.Map;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
@@ -43,12 +43,12 @@ abstract class LockModule {
@Provides
@LockWindow
@LockScoped
static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Named("lockWindowOwner") Optional<Stage> owner) {
static Stage provideWindow(StageFactory factory, @LockWindow Vault vault, @Nullable @Named("lockWindowOwner") Stage owner) {
Stage stage = factory.create();
stage.setTitle(vault.getDisplayName());
stage.setResizable(false);
if (owner.isPresent()) {
stage.initOwner(owner.get());
if (owner != null) {
stage.initOwner(owner);
stage.initModality(Modality.WINDOW_MODAL);
} else {
stage.initModality(Modality.APPLICATION_MODAL);
@@ -5,9 +5,9 @@ import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,16 +40,16 @@ public class LockWorkflow extends Task<Void> {
private final AtomicReference<CompletableFuture<Boolean>> forceRetryDecision;
private final Lazy<Scene> lockForcedScene;
private final Lazy<Scene> lockFailedScene;
private final ErrorComponent.Builder errorComponent;
private final FxApplicationWindows appWindows;
@Inject
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference<CompletableFuture<Boolean>> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene, ErrorComponent.Builder errorComponent) {
public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, AtomicReference<CompletableFuture<Boolean>> forceRetryDecision, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy<Scene> lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy<Scene> lockFailedScene, FxApplicationWindows appWindows) {
this.lockWindow = lockWindow;
this.vault = vault;
this.forceRetryDecision = forceRetryDecision;
this.lockForcedScene = lockForcedScene;
this.lockFailedScene = lockFailedScene;
this.errorComponent = errorComponent;
this.appWindows = appWindows;
}
@Override
@@ -109,7 +109,7 @@ public class LockWorkflow extends Task<Void> {
lockWindow.setScene(lockFailedScene.get());
lockWindow.show();
} else {
errorComponent.cause(throwable).window(lockWindow).build().showErrorScene();
appWindows.showErrorWindow(throwable, lockWindow, null);
}
}
@@ -9,6 +9,6 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface MainWindow {
@interface MainWindow {
}
@@ -13,6 +13,8 @@ import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlLoaderFactory;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.StageFactory;
import org.cryptomator.ui.common.StageInitializer;
import org.cryptomator.ui.fxapp.PrimaryStage;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.migration.MigrationComponent;
import org.cryptomator.ui.removevault.RemoveVaultComponent;
@@ -34,6 +36,18 @@ import java.util.ResourceBundle;
@Module(subcomponents = {AddVaultWizardComponent.class, HealthCheckComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, VaultStatisticsComponent.class, WrongFileAlertComponent.class, ErrorComponent.class})
abstract class MainWindowModule {
@Provides
@MainWindow
@MainWindowScoped
static Stage provideMainWindow(@PrimaryStage Stage stage, StageInitializer initializer) {
initializer.accept(stage);
stage.setTitle("Cryptomator");
stage.initStyle(StageStyle.UNDECORATED);
stage.setMinWidth(650);
stage.setMinHeight(440);
return stage;
}
@Provides
@MainWindowScoped
static ObjectProperty<Vault> provideSelectedVault() {
@@ -47,22 +61,11 @@ abstract class MainWindowModule {
return new FxmlLoaderFactory(factories, sceneFactory, resourceBundle);
}
@Provides
@MainWindow
@MainWindowScoped
static Stage provideStage(StageFactory factory) {
Stage stage = factory.create(StageStyle.UNDECORATED);
stage.setMinWidth(650);
stage.setMinHeight(440);
stage.setTitle("Cryptomator");
return stage;
}
@Provides
@MainWindowScoped
@Named("errorWindow")
static Stage provideErrorStage(@MainWindow Stage window, StageFactory factory, ResourceBundle resourceBundle) {
Stage stage = factory.create(StageStyle.DECORATED);
Stage stage = factory.create();
stage.setTitle(resourceBundle.getString("main.vaultDetail.error.windowTitle"));
stage.initModality(Modality.APPLICATION_MODAL);
stage.initOwner(window);
@@ -3,9 +3,9 @@ package org.cryptomator.ui.mainwindow;
import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.fxapp.FxApplicationTerminator;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.fxapp.UpdateChecker;
import org.cryptomator.ui.launcher.AppLifecycleListener;
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
import org.cryptomator.ui.traymenu.TrayMenuComponent;
import org.slf4j.Logger;
@@ -25,9 +25,9 @@ public class MainWindowTitleController implements FxController {
private static final Logger LOG = LoggerFactory.getLogger(MainWindowTitleController.class);
private final AppLifecycleListener appLifecycle;
private final Stage window;
private final FxApplication application;
private final FxApplicationTerminator terminator;
private final FxApplicationWindows appWindows;
private final boolean trayMenuInitialized;
private final UpdateChecker updateChecker;
private final BooleanBinding updateAvailable;
@@ -40,10 +40,10 @@ public class MainWindowTitleController implements FxController {
private double yOffset;
@Inject
MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
this.appLifecycle = appLifecycle;
MainWindowTitleController(@MainWindow Stage window, FxApplicationTerminator terminator, FxApplicationWindows appWindows, TrayMenuComponent trayMenu, UpdateChecker updateChecker, LicenseHolder licenseHolder, Settings settings) {
this.window = window;
this.application = application;
this.terminator = terminator;
this.appWindows = appWindows;
this.trayMenuInitialized = trayMenu.isInitialized();
this.updateChecker = updateChecker;
this.updateAvailable = updateChecker.latestVersionProperty().isNotNull();
@@ -96,7 +96,7 @@ public class MainWindowTitleController implements FxController {
if (trayMenuInitialized) {
window.close();
} else {
appLifecycle.quit();
terminator.terminate();
}
}
@@ -107,17 +107,17 @@ public class MainWindowTitleController implements FxController {
@FXML
public void showPreferences() {
application.showPreferencesWindow(SelectedPreferencesTab.ANY);
appWindows.showPreferencesWindow(SelectedPreferencesTab.ANY);
}
@FXML
public void showGeneralPreferences() {
application.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
appWindows.showPreferencesWindow(SelectedPreferencesTab.GENERAL);
}
@FXML
public void showDonationKeyPreferences() {
application.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
public void showContributePreferences() {
appWindows.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
}
/* Getter/Setter */
@@ -1,7 +1,6 @@
package org.cryptomator.ui.mainwindow;
import com.tobiasdiez.easybind.EasyBind;
import com.tobiasdiez.easybind.Subscription;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.ui.common.Animations;
@@ -9,9 +8,9 @@ import org.cryptomator.ui.common.AutoAnimator;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.controls.FontAwesome5Icon;
import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.cryptomator.ui.fxapp.FxApplication;
import javax.inject.Inject;
import javafx.application.Application;
import javafx.beans.binding.Binding;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ObjectProperty;
@@ -22,7 +21,7 @@ import javafx.fxml.FXML;
public class VaultDetailController implements FxController {
private final ReadOnlyObjectProperty<Vault> vault;
private final FxApplication application;
private final Application application;
private final Binding<FontAwesome5Icon> glyph;
private final BooleanBinding anyVaultSelected;
@@ -33,7 +32,7 @@ public class VaultDetailController implements FxController {
@Inject
VaultDetailController(ObjectProperty<Vault> vault, FxApplication application) {
VaultDetailController(ObjectProperty<Vault> vault, Application application) {
this.vault = vault;
this.application = application;
this.glyph = EasyBind.select(vault) //
@@ -4,8 +4,7 @@ import com.tobiasdiez.easybind.EasyBind;
import org.cryptomator.common.keychain.KeychainManager;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.fxapp.FxApplication;
import org.cryptomator.ui.health.HealthCheckComponent;
import org.cryptomator.ui.fxapp.FxApplicationWindows;
import org.cryptomator.ui.vaultoptions.SelectedVaultOptionsTab;
import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
@@ -14,25 +13,23 @@ import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.stage.Stage;
import java.util.Optional;
@MainWindowScoped
public class VaultDetailLockedController implements FxController {
private final ReadOnlyObjectProperty<Vault> vault;
private final FxApplication application;
private final VaultOptionsComponent.Builder vaultOptionsWindow;
private final FxApplicationWindows appWindows;
private final VaultOptionsComponent.Factory vaultOptionsWindow;
private final KeychainManager keychain;
private final Stage mainWindow;
private final BooleanExpression passwordSaved;
@Inject
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplication application, VaultOptionsComponent.Builder vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
VaultDetailLockedController(ObjectProperty<Vault> vault, FxApplicationWindows appWindows, VaultOptionsComponent.Factory vaultOptionsWindow, KeychainManager keychain, @MainWindow Stage mainWindow) {
this.vault = vault;
this.application = application;
this.appWindows = appWindows;
this.vaultOptionsWindow = vaultOptionsWindow;
this.keychain = keychain;
this.mainWindow = mainWindow;
@@ -45,17 +42,17 @@ public class VaultDetailLockedController implements FxController {
@FXML
public void unlock() {
application.startUnlockWorkflow(vault.get(), Optional.of(mainWindow));
appWindows.startUnlockWorkflow(vault.get(), mainWindow);
}
@FXML
public void showVaultOptions() {
vaultOptionsWindow.vault(vault.get()).build().showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.ANY);
}
@FXML
public void showKeyVaultOptions() {
vaultOptionsWindow.vault(vault.get()).build().showVaultOptionsWindow(SelectedVaultOptionsTab.KEY);
vaultOptionsWindow.create(vault.get()).showVaultOptionsWindow(SelectedVaultOptionsTab.KEY);
}
/* Getter/Setter */
@@ -13,6 +13,8 @@ import javafx.stage.Stage;
import java.io.File;
import java.util.ResourceBundle;
import static org.cryptomator.common.Constants.CRYPTOMATOR_FILENAME_GLOB;
@MainWindowScoped
public class VaultDetailMissingVaultController implements FxController {
@@ -45,7 +47,7 @@ public class VaultDetailMissingVaultController implements FxController {
// copied from ChooseExistingVaultController class
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle(resourceBundle.getString("addvaultwizard.existing.filePickerTitle"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(resourceBundle.getString("addvaultwizard.existing.filePickerMimeDesc"), CRYPTOMATOR_FILENAME_GLOB));
File masterkeyFile = fileChooser.showOpenDialog(window);
if (masterkeyFile != null) {
vault.get().getVaultSettings().path().setValue(masterkeyFile.toPath().toAbsolutePath().getParent());

Some files were not shown because too many files have changed in this diff Show More