diff --git a/app/src/main/java/io/xpipe/app/core/App.java b/app/src/main/java/io/xpipe/app/core/App.java index 0f85ac789..1cd1257dc 100644 --- a/app/src/main/java/io/xpipe/app/core/App.java +++ b/app/src/main/java/io/xpipe/app/core/App.java @@ -2,22 +2,16 @@ package io.xpipe.app.core; import io.xpipe.app.comp.AppLayoutComp; import io.xpipe.app.fxcomps.util.PlatformThread; -import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.update.XPipeDistributionType; import io.xpipe.app.util.LicenseProvider; -import io.xpipe.core.process.OsType; - import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.stage.Stage; - import lombok.Getter; import lombok.SneakyThrows; -import java.awt.*; - @Getter public class App extends Application { @@ -35,25 +29,6 @@ public class App extends Application { APP = this; stage = primaryStage; stage.opacityProperty().bind(AppPrefs.get().windowOpacity()); - - if (OsType.getLocal().equals(OsType.MACOS)) { - Desktop.getDesktop().setPreferencesHandler(e -> { - AppLayoutModel.get().selectSettings(); - }); - } - - if (OsType.getLocal().equals(OsType.LINUX)) { - try { - Toolkit xToolkit = Toolkit.getDefaultToolkit(); - java.lang.reflect.Field awtAppClassNameField = - xToolkit.getClass().getDeclaredField("awtAppClassName"); - awtAppClassNameField.setAccessible(true); - awtAppClassNameField.set(xToolkit, "XPipe"); - } catch (Exception e) { - ErrorEvent.fromThrowable(e).omit().handle(); - } - } - AppWindowHelper.addIcons(stage); } diff --git a/app/src/main/java/io/xpipe/app/core/AppIntegration.java b/app/src/main/java/io/xpipe/app/core/AppIntegration.java new file mode 100644 index 000000000..ab5b79ed0 --- /dev/null +++ b/app/src/main/java/io/xpipe/app/core/AppIntegration.java @@ -0,0 +1,89 @@ +package io.xpipe.app.core; + +import io.xpipe.app.Main; +import io.xpipe.app.core.mode.OperationMode; +import io.xpipe.app.issue.ErrorEvent; +import io.xpipe.app.launcher.LauncherInput; +import io.xpipe.app.prefs.AppPrefs; +import io.xpipe.core.process.OsType; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.desktop.*; +import java.util.List; + +public class AppIntegration { + + + public static void setupDesktopIntegrations() { + try { + Desktop.getDesktop().addAppEventListener(new SystemSleepListener() { + @Override + public void systemAboutToSleep(SystemSleepEvent e) { + if (AppPrefs.get() != null && AppPrefs.get().lockVaultOnHibernation().get()) { + OperationMode.close(); + } + } + + @Override + public void systemAwoke(SystemSleepEvent e) { + + } + }); + + // This will initialize the toolkit on macos and create the dock icon + // macOS does not like applications that run fully in the background, so always do it + if (OsType.getLocal().equals(OsType.MACOS)) { + Desktop.getDesktop().setPreferencesHandler(e -> { + AppLayoutModel.get().selectSettings(); + }); + + // URL open operations have to be handled in a special way on macOS! + Desktop.getDesktop().setOpenURIHandler(e -> { + LauncherInput.handle(List.of(e.getURI().toString())); + }); + + // Do it this way to prevent IDE inspections from complaining + var c = Class.forName( + ModuleLayer.boot().findModule("java.desktop").orElseThrow(), "com.apple.eawt.Application"); + var m = c.getDeclaredMethod("addAppEventListener", SystemEventListener.class); + m.invoke(c.getMethod("getApplication").invoke(null), new AppReopenedListener() { + @Override + public void appReopened(AppReopenedEvent e) { + OperationMode.switchToAsync(OperationMode.GUI); + } + }); + + // Set dock icon explicitly on mac + // This is necessary in case XPipe was started through a script as it will have no icon otherwise + if (AppProperties.get().isDeveloperMode() && AppLogs.get().isWriteToSysout()) { + try { + var iconUrl = Main.class.getResourceAsStream("resources/img/logo/padded/logo_128x128.png"); + if (iconUrl != null) { + var awtIcon = ImageIO.read(iconUrl); + Taskbar.getTaskbar().setIconImage(awtIcon); + } + } catch (Exception ex) { + ErrorEvent.fromThrowable(ex).omitted(true).build().handle(); + } + } + } + + if (OsType.getLocal().equals(OsType.LINUX)) { + try { + Toolkit xToolkit = Toolkit.getDefaultToolkit(); + java.lang.reflect.Field awtAppClassNameField = + xToolkit.getClass().getDeclaredField("awtAppClassName"); + awtAppClassNameField.setAccessible(true); + awtAppClassNameField.set(xToolkit, "XPipe"); + } catch (Exception e) { + ErrorEvent.fromThrowable(e).omit().handle(); + } + } + + } catch (Throwable ex) { + ErrorEvent.fromThrowable(ex).term().handle(); + } + } + +} diff --git a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java index 38e737981..a89e9d847 100644 --- a/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java +++ b/app/src/main/java/io/xpipe/app/core/mode/OperationMode.java @@ -1,37 +1,24 @@ package io.xpipe.app.core.mode; -import io.xpipe.app.Main; -import io.xpipe.app.core.App; -import io.xpipe.app.core.AppLogs; -import io.xpipe.app.core.AppProperties; -import io.xpipe.app.core.AppState; +import io.xpipe.app.core.*; import io.xpipe.app.core.check.AppDebugModeCheck; import io.xpipe.app.core.check.AppTempCheck; import io.xpipe.app.core.check.AppUserDirectoryCheck; import io.xpipe.app.issue.*; import io.xpipe.app.launcher.LauncherCommand; -import io.xpipe.app.launcher.LauncherInput; import io.xpipe.app.util.LocalShell; import io.xpipe.app.util.PlatformState; import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.XPipeSession; -import io.xpipe.core.process.OsType; import io.xpipe.core.util.FailableRunnable; import io.xpipe.core.util.XPipeDaemonMode; import io.xpipe.core.util.XPipeInstallation; - import javafx.application.Platform; - import lombok.Getter; -import java.awt.*; -import java.awt.desktop.AppReopenedEvent; -import java.awt.desktop.AppReopenedListener; -import java.awt.desktop.SystemEventListener; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; -import javax.imageio.ImageIO; public abstract class OperationMode { @@ -132,47 +119,7 @@ public abstract class OperationMode { setup(args); LauncherCommand.runLauncher(usedArgs); inStartup = false; - postInit(args); - } - - public static void postInit(String[] args) { - try { - // This will initialize the toolkit on macos and create the dock icon - // macOS does not like applications that run fully in the background, so always do it - if (OsType.getLocal().equals(OsType.MACOS)) { - // URL open operations have to be handled in a special way on macOS! - Desktop.getDesktop().setOpenURIHandler(e -> { - LauncherInput.handle(List.of(e.getURI().toString())); - }); - - // Do it this way to prevent IDE inspections from complaining - var c = Class.forName( - ModuleLayer.boot().findModule("java.desktop").orElseThrow(), "com.apple.eawt.Application"); - var m = c.getDeclaredMethod("addAppEventListener", SystemEventListener.class); - m.invoke(c.getMethod("getApplication").invoke(null), new AppReopenedListener() { - @Override - public void appReopened(AppReopenedEvent e) { - OperationMode.switchToAsync(OperationMode.GUI); - } - }); - - // Set dock icon explicitly on mac - // This is necessary in case XPipe was started through a script as it will have no icon otherwise - if (AppProperties.get().isDeveloperMode() && AppLogs.get().isWriteToSysout()) { - try { - var iconUrl = Main.class.getResourceAsStream("resources/img/logo/padded/logo_128x128.png"); - if (iconUrl != null) { - var awtIcon = ImageIO.read(iconUrl); - Taskbar.getTaskbar().setIconImage(awtIcon); - } - } catch (Exception ex) { - ErrorEvent.fromThrowable(ex).omitted(true).build().handle(); - } - } - } - } catch (Throwable ex) { - ErrorEvent.fromThrowable(ex).term().handle(); - } + AppIntegration.setupDesktopIntegrations(); } public static void switchToAsync(OperationMode newMode) { diff --git a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java index de2e3827a..fea57559d 100644 --- a/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java +++ b/app/src/main/java/io/xpipe/app/prefs/AppPrefs.java @@ -96,6 +96,8 @@ public class AppPrefs { map(new SimpleBooleanProperty(false), "condenseConnectionDisplay", Boolean.class); final BooleanProperty showChildCategoriesInParentCategory = map(new SimpleBooleanProperty(true), "showChildrenConnectionsInParentCategory", Boolean.class); + final BooleanProperty lockVaultOnHibernation = + map(new SimpleBooleanProperty(false), "lockVaultOnHibernation", Boolean.class); final BooleanProperty openConnectionSearchWindowOnConnectionCreation = map(new SimpleBooleanProperty(true), "openConnectionSearchWindowOnConnectionCreation", Boolean.class); final ObjectProperty storageDirectory = @@ -263,6 +265,10 @@ public class AppPrefs { return disableTerminalRemotePasswordPreparation; } + public ObservableBooleanValue lockVaultOnHibernation() { + return lockVaultOnHibernation; + } + public ObservableValue alwaysConfirmElevation() { return alwaysConfirmElevation; } diff --git a/app/src/main/java/io/xpipe/app/prefs/VaultCategory.java b/app/src/main/java/io/xpipe/app/prefs/VaultCategory.java index 1e4b2b114..eff309a36 100644 --- a/app/src/main/java/io/xpipe/app/prefs/VaultCategory.java +++ b/app/src/main/java/io/xpipe/app/prefs/VaultCategory.java @@ -52,7 +52,11 @@ public class VaultCategory extends AppPrefsCategory { }, prefs.getLockCrypt()), LockChangeAlert::show), - prefs.getLockCrypt())); + prefs.getLockCrypt()) + .nameAndDescription("lockVaultOnHibernation") + .addToggle(prefs.lockVaultOnHibernation) + .hide(prefs.getLockCrypt().isNull().or(prefs.getLockCrypt().isEmpty())) + ); return builder.buildComp(); } } diff --git a/lang/app/strings/translations_da.properties b/lang/app/strings/translations_da.properties index 583584449..b2087b594 100644 --- a/lang/app/strings/translations_da.properties +++ b/lang/app/strings/translations_da.properties @@ -443,3 +443,5 @@ retryAll=Prøv igen alle replace=Erstat replaceAll=Udskift alle copyPassword=copyPassword +lockVaultOnHibernation=Lås hvælving på computer i dvale +lockVaultOnHibernationDescription=Når denne funktion er aktiveret, låses boksen automatisk, når computeren sættes i dvale. Når du vågner, skal du indtaste din vault-passphrase igen. diff --git a/lang/app/strings/translations_de.properties b/lang/app/strings/translations_de.properties index d47a375ca..2fd814ec4 100644 --- a/lang/app/strings/translations_de.properties +++ b/lang/app/strings/translations_de.properties @@ -444,3 +444,5 @@ retryAll=Alle Versuche wiederholen replace=Ersetzen replaceAll=Ersetze alles copyPassword=copyPassword +lockVaultOnHibernation=Tresor im Ruhezustand des Computers sperren +lockVaultOnHibernationDescription=Wenn diese Funktion aktiviert ist, wird der Tresor automatisch gesperrt, sobald dein Computer in den Ruhezustand versetzt wird. Nach dem Aufwachen musst du deine Tresor-Passphrase erneut eingeben. diff --git a/lang/app/strings/translations_en.properties b/lang/app/strings/translations_en.properties index 033ca36da..0b0793df8 100644 --- a/lang/app/strings/translations_en.properties +++ b/lang/app/strings/translations_en.properties @@ -447,3 +447,7 @@ retryAll=Retry all replace=Replace replaceAll=Replace all copyPassword=copyPassword +#force +#context: when computer sleeps +lockVaultOnHibernation=Lock vault on computer hibernation +lockVaultOnHibernationDescription=When enabled, the vault will automatically be locked once your computer is put into hibernation/to sleep. Upon wake up, you will have to enter your vault passphrase again. diff --git a/lang/app/strings/translations_es.properties b/lang/app/strings/translations_es.properties index 5d5a6e8be..3b568fcc4 100644 --- a/lang/app/strings/translations_es.properties +++ b/lang/app/strings/translations_es.properties @@ -431,3 +431,5 @@ retryAll=Reintentar todo replace=Sustituye replaceAll=Sustituir todo copyPassword=copiarContraseña +lockVaultOnHibernation=Bloquear la bóveda al hibernar el ordenador +lockVaultOnHibernationDescription=Si está activada, el almacén se bloqueará automáticamente cuando tu ordenador entre en hibernación/reposo. Al despertarte, tendrás que volver a introducir la contraseña de tu bóveda. diff --git a/lang/app/strings/translations_fr.properties b/lang/app/strings/translations_fr.properties index bb7f8150b..febafc94b 100644 --- a/lang/app/strings/translations_fr.properties +++ b/lang/app/strings/translations_fr.properties @@ -431,3 +431,5 @@ retryAll=Réessayer tout replace=Remplacer replaceAll=Remplacer tout copyPassword=copierMotdepasse +lockVaultOnHibernation=Verrouille le coffre-fort lors de l'hibernation de l'ordinateur +lockVaultOnHibernationDescription=Lorsque cette option est activée, le coffre-fort sera automatiquement verrouillé une fois que ton ordinateur sera mis en hibernation/en veille. Au réveil, tu devras saisir à nouveau la phrase de passe de ton coffre-fort. diff --git a/lang/app/strings/translations_it.properties b/lang/app/strings/translations_it.properties index e78775b4b..f93b41b76 100644 --- a/lang/app/strings/translations_it.properties +++ b/lang/app/strings/translations_it.properties @@ -431,3 +431,5 @@ retryAll=Riprova tutti replace=Sostituire replaceAll=Sostituisci tutto copyPassword=copiaPassword +lockVaultOnHibernation=Blocca il caveau durante l'ibernazione del computer +lockVaultOnHibernationDescription=Se abilitato, il vault si blocca automaticamente quando il computer viene messo in ibernazione o a riposo. Al risveglio, dovrai inserire nuovamente la passphrase del vault. diff --git a/lang/app/strings/translations_ja.properties b/lang/app/strings/translations_ja.properties index 5948c3081..63819d75e 100644 --- a/lang/app/strings/translations_ja.properties +++ b/lang/app/strings/translations_ja.properties @@ -431,3 +431,5 @@ retryAll=すべて再試行する replace=置き換える replaceAll=すべて置き換える copyPassword=コピーパスワード +lockVaultOnHibernation=コンピュータのハイバネーション時に保管庫をロックする +lockVaultOnHibernationDescription=有効にすると、コンピュータが休止状態/スリープ状態になると、保管庫は自動的にロックされる。スリープ解除後、保管庫のパスフレーズを再度入力する必要がある。 diff --git a/lang/app/strings/translations_nl.properties b/lang/app/strings/translations_nl.properties index 36f3e941f..5fe5d7956 100644 --- a/lang/app/strings/translations_nl.properties +++ b/lang/app/strings/translations_nl.properties @@ -431,3 +431,5 @@ retryAll=Alles opnieuw proberen replace=Vervangen replaceAll=Alles vervangen copyPassword=kopieerwachtwoord +lockVaultOnHibernation=Kluis op computer in slaapstand +lockVaultOnHibernationDescription=Als deze optie is ingeschakeld, wordt de kluis automatisch vergrendeld zodra je computer in de slaapstand wordt gezet. Als je wakker wordt, moet je je wachtwoordzin voor de kluis opnieuw invoeren. diff --git a/lang/app/strings/translations_pt.properties b/lang/app/strings/translations_pt.properties index f7df04a5e..382a0c227 100644 --- a/lang/app/strings/translations_pt.properties +++ b/lang/app/strings/translations_pt.properties @@ -431,3 +431,5 @@ retryAll=Repetir tudo replace=Substitui replaceAll=Substitui tudo copyPassword=copia a palavra-passe +lockVaultOnHibernation=Bloqueia o cofre na hibernação do computador +lockVaultOnHibernationDescription=Quando ativado, a abóbada é automaticamente bloqueada quando o computador é colocado em hibernação/sono. Quando acordares, terás de introduzir novamente a frase-chave do cofre. diff --git a/lang/app/strings/translations_ru.properties b/lang/app/strings/translations_ru.properties index 5d4d571cd..70d12dc5a 100644 --- a/lang/app/strings/translations_ru.properties +++ b/lang/app/strings/translations_ru.properties @@ -431,3 +431,5 @@ retryAll=Повторите все попытки replace=Замените replaceAll=Заменить все copyPassword=copyPassword +lockVaultOnHibernation=Блокировка хранилища при спящем режиме компьютера +lockVaultOnHibernationDescription=Если эта функция включена, хранилище будет автоматически блокироваться, как только компьютер перейдет в спящий режим. После пробуждения тебе придется снова ввести кодовую фразу хранилища. diff --git a/lang/app/strings/translations_tr.properties b/lang/app/strings/translations_tr.properties index c9432eba4..8ed264c62 100644 --- a/lang/app/strings/translations_tr.properties +++ b/lang/app/strings/translations_tr.properties @@ -432,3 +432,5 @@ retryAll=Tümünü yeniden dene replace=Değiştirin replaceAll=Tümünü değiştirin copyPassword=copyPassword +lockVaultOnHibernation=Bilgisayar hazırda bekletme modunda kasayı kilitleme +lockVaultOnHibernationDescription=Etkinleştirildiğinde, bilgisayarınız hazırda bekletme/uyku moduna geçtiğinde kasa otomatik olarak kilitlenecektir. Uyandığınızda, kasa parolanızı tekrar girmeniz gerekecektir. diff --git a/lang/app/strings/translations_zh.properties b/lang/app/strings/translations_zh.properties index 15068e1fb..974f5a8e2 100644 --- a/lang/app/strings/translations_zh.properties +++ b/lang/app/strings/translations_zh.properties @@ -431,3 +431,5 @@ retryAll=全部重试 replace=替换 replaceAll=全部替换 copyPassword=复制密码 +lockVaultOnHibernation=电脑休眠时锁定保险库 +lockVaultOnHibernationDescription=启用后,一旦电脑进入休眠/睡眠状态,保管库就会自动锁定。唤醒后,您必须再次输入保险库密码。