mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-08-17 04:36:02 +00:00
- minimizes to tray when vaults are still unlocked (i.e. webdav shares still mounted)
This commit is contained in:
@@ -13,12 +13,14 @@ import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import org.cryptomator.ui.settings.Settings;
|
||||
import org.cryptomator.ui.util.TrayIconUtil;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
|
||||
public class MainApplication extends Application {
|
||||
@@ -27,30 +29,41 @@ public class MainApplication extends Application {
|
||||
private static final CleanShutdownPerformer CLEAN_SHUTDOWN_PERFORMER = new CleanShutdownPerformer();
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
Application.launch(args);
|
||||
Runtime.getRuntime().addShutdownHook(CLEAN_SHUTDOWN_PERFORMER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final Stage primaryStage) throws IOException {
|
||||
final ResourceBundle localizations = ResourceBundle.getBundle("localization");
|
||||
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"), localizations);
|
||||
final ResourceBundle rb = ResourceBundle.getBundle("localization");
|
||||
final FXMLLoader loader = new FXMLLoader(getClass().getResource("/main.fxml"), rb);
|
||||
final Parent root = loader.load();
|
||||
final MainController ctrl = loader.getController();
|
||||
ctrl.setStage(primaryStage);
|
||||
final Scene scene = new Scene(root);
|
||||
primaryStage.setTitle("Cryptomator");
|
||||
primaryStage.setTitle(rb.getString("app.name"));
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.sizeToScene();
|
||||
primaryStage.setResizable(false);
|
||||
primaryStage.show();
|
||||
TrayIconUtil.init(primaryStage, rb, () -> {
|
||||
quit();
|
||||
});
|
||||
}
|
||||
|
||||
private void quit() {
|
||||
Platform.runLater(() -> {
|
||||
CLEAN_SHUTDOWN_PERFORMER.run();
|
||||
Settings.save();
|
||||
Platform.exit();
|
||||
System.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
public void stop() {
|
||||
CLEAN_SHUTDOWN_PERFORMER.run();
|
||||
Settings.save();
|
||||
super.stop();
|
||||
}
|
||||
|
||||
public static void addShutdownTask(Runnable r) {
|
||||
|
||||
@@ -11,8 +11,11 @@ package org.cryptomator.ui;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Collection;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -82,7 +85,7 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
stage.setTitle(selectedDir.getName());
|
||||
showDirectory(selectedDir);
|
||||
}
|
||||
|
||||
|
||||
private void showDirectory(Directory directory) {
|
||||
try {
|
||||
if (directory.isUnlocked()) {
|
||||
@@ -133,6 +136,7 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
@Override
|
||||
public void didUnlock(UnlockController ctrl) {
|
||||
showUnlockedView(ctrl.getDirectory());
|
||||
Platform.setImplicitExit(false);
|
||||
}
|
||||
|
||||
private void showUnlockedView(Directory directory) {
|
||||
@@ -144,6 +148,19 @@ public class MainController implements Initializable, InitializationListener, Un
|
||||
@Override
|
||||
public void didLock(UnlockedController ctrl) {
|
||||
showUnlockView(ctrl.getDirectory());
|
||||
if (getUnlockedDirectories().isEmpty()) {
|
||||
Platform.setImplicitExit(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convenience */
|
||||
|
||||
public Collection<Directory> getDirectories() {
|
||||
return directoryList.getItems();
|
||||
}
|
||||
|
||||
public Collection<Directory> getUnlockedDirectories() {
|
||||
return getDirectories().stream().filter(d -> d.isUnlocked()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/* public Getter/Setter */
|
||||
|
||||
@@ -113,6 +113,23 @@ public class Directory implements Serializable {
|
||||
return server;
|
||||
}
|
||||
|
||||
/* hashcode/equals */
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return path.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof Directory) {
|
||||
final Directory other = (Directory) obj;
|
||||
return this.path.equals(other.path);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* graceful shutdown */
|
||||
|
||||
private class ShutdownTask implements Runnable {
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
package org.cryptomator.ui.util;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.TrayIcon.MessageType;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
public final class TrayIconUtil {
|
||||
|
||||
private static TrayIconUtil INSTANCE;
|
||||
|
||||
private final Stage mainApplicationWindow;
|
||||
private final ResourceBundle rb;
|
||||
private final Runnable exitCommand;
|
||||
|
||||
/**
|
||||
* This will add an icon to the system tray and modify the application shutdown procedure. Depending on
|
||||
* {@link Platform#isImplicitExit()} the application may still be running, allowing shutdown using the tray menu.
|
||||
*/
|
||||
public synchronized static void init(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) {
|
||||
if (INSTANCE == null && SystemTray.isSupported()) {
|
||||
INSTANCE = new TrayIconUtil(mainApplicationWindow, rb, exitCommand);
|
||||
}
|
||||
}
|
||||
|
||||
private TrayIconUtil(Stage mainApplicationWindow, ResourceBundle rb, Runnable exitCommand) {
|
||||
this.mainApplicationWindow = mainApplicationWindow;
|
||||
this.rb = rb;
|
||||
this.exitCommand = exitCommand;
|
||||
|
||||
initTrayIcon();
|
||||
}
|
||||
|
||||
private void initTrayIcon() {
|
||||
final TrayIcon trayIcon = createTrayIcon();
|
||||
try {
|
||||
SystemTray.getSystemTray().add(trayIcon);
|
||||
mainApplicationWindow.setOnCloseRequest((e) -> {
|
||||
if (Platform.isImplicitExit()) {
|
||||
exitCommand.run();
|
||||
} else {
|
||||
mainApplicationWindow.close();
|
||||
this.showTrayNotification(trayIcon);
|
||||
}
|
||||
});
|
||||
} catch (SecurityException | AWTException ex) {
|
||||
// not working? then just go ahead and close the app
|
||||
mainApplicationWindow.setOnCloseRequest((ev) -> {
|
||||
exitCommand.run();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private TrayIcon createTrayIcon() {
|
||||
final PopupMenu popup = new PopupMenu();
|
||||
|
||||
final MenuItem showItem = new MenuItem(rb.getString("tray.menu.open"));
|
||||
showItem.addActionListener(this::restoreFromTray);
|
||||
popup.add(showItem);
|
||||
|
||||
final MenuItem exitItem = new MenuItem(rb.getString("tray.menu.quit"));
|
||||
exitItem.addActionListener(this::quitFromTray);
|
||||
popup.add(exitItem);
|
||||
|
||||
final Image image = Toolkit.getDefaultToolkit().getImage(TrayIconUtil.class.getResource("/tray_icon.png"));
|
||||
return new TrayIcon(image, rb.getString("app.name"), popup);
|
||||
}
|
||||
|
||||
private void showTrayNotification(TrayIcon trayIcon) {
|
||||
final Runnable notificationCmd;
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
final String title = rb.getString("tray.infoMsg.title");
|
||||
final String msg = rb.getString("tray.infoMsg.msg.osx");
|
||||
final String notificationCenterAppleScript = String.format("display notification \"%s\" with title \"%s\"", msg, title);
|
||||
notificationCmd = () -> {
|
||||
try {
|
||||
Runtime.getRuntime().exec(new String[] {"/usr/bin/osascript", "-e", notificationCenterAppleScript});
|
||||
} catch (IOException e) {
|
||||
// ignore, user will notice the tray icon anyway.
|
||||
}
|
||||
};
|
||||
} else {
|
||||
final String title = rb.getString("tray.infoMsg.title");
|
||||
final String msg = rb.getString("tray.infoMsg.msg");
|
||||
notificationCmd = () -> {
|
||||
trayIcon.displayMessage(title, msg, MessageType.INFO);
|
||||
};
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
notificationCmd.run();
|
||||
});
|
||||
}
|
||||
|
||||
private void restoreFromTray(ActionEvent event) {
|
||||
Platform.runLater(() -> {
|
||||
mainApplicationWindow.show();
|
||||
mainApplicationWindow.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
private void quitFromTray(ActionEvent event) {
|
||||
exitCommand.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
# Sebastian Stenzel - initial API and implementation
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
app.name=Cryptomator
|
||||
|
||||
|
||||
# welcome.fxml
|
||||
welcome.welcomeLabel=Welcome to Cryptomator
|
||||
@@ -34,4 +36,12 @@ unlock.messageLabel.startServerFailed=Starting WebDAV server failed.
|
||||
|
||||
# unlocked.fxml
|
||||
unlocked.messageLabel.runningOnPort=Vault is accessible via WebDAV on local port %d.
|
||||
unlocked.button.lock=Lock vault
|
||||
unlocked.button.lock=Lock vault
|
||||
|
||||
|
||||
# tray icon
|
||||
tray.menu.open=Open
|
||||
tray.menu.quit=Quit
|
||||
tray.infoMsg.title=Still running
|
||||
tray.infoMsg.msg=Cryptomator is still alive. Quit it from the tray icon.
|
||||
tray.infoMsg.msg.osx=Cryptomator is still alive. Quit it from the menu bar icon.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Reference in New Issue
Block a user