mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-22 22:45:21 +01:00
Play silent sound to avoid standby mode
Apple disabled options do avoid App Nap in recent OSX versions. Playing an inaudible sound marks the application for preventing to get set to standby mode as well as App Nap mode. The alternative to that "hack" would be to add OSX native code, but even then it is likely only possible to prevent App Nap but not sleep mode. If Bisq is in App Nap mode offers cannot be taken as well the user loses network connection and offers. See: https://github.com/bisq-network/bisq/issues/1701
This commit is contained in:
parent
1504e4992a
commit
416d9ba8f3
9 changed files with 157 additions and 4 deletions
|
@ -1263,6 +1263,7 @@ message PreferencesPayload {
|
|||
bool use_trade_notifications = 42;
|
||||
bool use_market_notifications = 43;
|
||||
bool use_price_notifications = 44;
|
||||
bool use_standby_mode = 45;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -19,6 +19,7 @@ package bisq.core;
|
|||
|
||||
import bisq.core.alert.AlertModule;
|
||||
import bisq.core.app.AppOptionKeys;
|
||||
import bisq.core.app.AvoidStandbyMode;
|
||||
import bisq.core.app.BisqEnvironment;
|
||||
import bisq.core.app.BisqFacade;
|
||||
import bisq.core.app.BisqSetup;
|
||||
|
@ -93,6 +94,7 @@ public class CoreModule extends AppModule {
|
|||
bind(Preferences.class).in(Singleton.class);
|
||||
bind(BridgeAddressProvider.class).to(Preferences.class).in(Singleton.class);
|
||||
bind(CorruptedDatabaseFilesHandler.class).in(Singleton.class);
|
||||
bind(AvoidStandbyMode.class).in(Singleton.class);
|
||||
|
||||
bind(SeedNodeAddressLookup.class).in(Singleton.class);
|
||||
bind(SeedNodeRepository.class).to(DefaultSeedNodeRepository.class).in(Singleton.class);
|
||||
|
|
118
core/src/main/java/bisq/core/app/AvoidStandbyMode.java
Normal file
118
core/src/main/java/bisq/core/app/AvoidStandbyMode.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq 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 Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.app;
|
||||
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
@Slf4j
|
||||
public class AvoidStandbyMode {
|
||||
private final Preferences preferences;
|
||||
private volatile boolean isStopped;
|
||||
|
||||
@Inject
|
||||
public AvoidStandbyMode(Preferences preferences) {
|
||||
this.preferences = preferences;
|
||||
|
||||
preferences.getUseStandbyModeProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
isStopped = true;
|
||||
log.info("AvoidStandbyMode stopped");
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void init() {
|
||||
isStopped = preferences.isUseStandbyMode();
|
||||
if (!isStopped) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
private void start() {
|
||||
isStopped = false;
|
||||
log.info("AvoidStandbyMode started");
|
||||
Thread thread = new Thread(this::play);
|
||||
thread.setName("AvoidStandbyMode-thread");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
||||
private void play() {
|
||||
if (!isStopped) {
|
||||
OutputStream outputStream = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = getClass().getClassLoader().getResourceAsStream("silent.aiff");
|
||||
File soundFile = File.createTempFile("Bisq-", "-PreventAppNap-soundFile");
|
||||
soundFile.deleteOnExit();
|
||||
outputStream = new FileOutputStream(soundFile);
|
||||
IOUtils.copy(inputStream, outputStream);
|
||||
outputStream.close();
|
||||
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
|
||||
byte tempBuffer[] = new byte[10000];
|
||||
AudioFormat audioFormat = audioInputStream.getFormat();
|
||||
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
|
||||
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
|
||||
sourceDataLine.open(audioFormat);
|
||||
sourceDataLine.start();
|
||||
int cnt;
|
||||
while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1 && !isStopped) {
|
||||
if (cnt > 0) {
|
||||
sourceDataLine.write(tempBuffer, 0, cnt);
|
||||
}
|
||||
}
|
||||
sourceDataLine.drain();
|
||||
sourceDataLine.close();
|
||||
play();
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (inputStream != null)
|
||||
inputStream.close();
|
||||
if (outputStream != null)
|
||||
outputStream.close();
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -142,6 +142,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
private final String btcNodesFromOptions;
|
||||
private final String useTorFlagFromOptions;
|
||||
private final String referralIdFromOptions;
|
||||
@Getter
|
||||
private final BooleanProperty useStandbyModeProperty = new SimpleBooleanProperty(prefPayload.isUseStandbyMode());
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -168,6 +170,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
GlobalSettings.setUseAnimations(prefPayload.isUseAnimations());
|
||||
persist();
|
||||
});
|
||||
|
||||
useStandbyModeProperty.addListener((ov) -> {
|
||||
prefPayload.setUseStandbyMode(useStandbyModeProperty.get());
|
||||
persist();
|
||||
});
|
||||
|
||||
fiatCurrenciesAsObservable.addListener((javafx.beans.Observable ov) -> {
|
||||
prefPayload.getFiatCurrencies().clear();
|
||||
prefPayload.getFiatCurrencies().addAll(fiatCurrenciesAsObservable);
|
||||
|
@ -254,6 +262,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
|
||||
// set all properties
|
||||
useAnimationsProperty.set(prefPayload.isUseAnimations());
|
||||
useStandbyModeProperty.set(prefPayload.isUseStandbyMode());
|
||||
useCustomWithdrawalTxFeeProperty.set(prefPayload.isUseCustomWithdrawalTxFee());
|
||||
withdrawalTxFeeInBytesProperty.set(prefPayload.getWithdrawalTxFeeInBytes());
|
||||
|
||||
|
@ -583,6 +592,9 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
persist();
|
||||
}
|
||||
|
||||
public void setUseStandbyMode(boolean useStandbyMode) {
|
||||
this.useStandbyModeProperty.set(useStandbyMode);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -782,5 +794,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
List<String> getBridgeAddresses();
|
||||
|
||||
long getWithdrawalTxFeeInBytes();
|
||||
|
||||
void setUseStandbyMode(boolean useStandbyMode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
boolean useTradeNotifications = true;
|
||||
boolean useMarketNotifications = true;
|
||||
boolean usePriceNotifications = true;
|
||||
boolean useStandbyMode = false;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -162,7 +163,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
.setUseSoundForMobileNotifications(useSoundForMobileNotifications)
|
||||
.setUseTradeNotifications(useTradeNotifications)
|
||||
.setUseMarketNotifications(useMarketNotifications)
|
||||
.setUsePriceNotifications(usePriceNotifications);
|
||||
.setUsePriceNotifications(usePriceNotifications)
|
||||
.setUseStandbyMode(useStandbyMode);
|
||||
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
|
||||
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((PB.TradeCurrency) e.toProtoMessage()));
|
||||
Optional.ofNullable(offerBookChartScreenCurrencyCode).ifPresent(builder::setOfferBookChartScreenCurrencyCode);
|
||||
|
@ -235,6 +237,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
proto.getUseSoundForMobileNotifications(),
|
||||
proto.getUseTradeNotifications(),
|
||||
proto.getUseMarketNotifications(),
|
||||
proto.getUsePriceNotifications());
|
||||
proto.getUsePriceNotifications(),
|
||||
proto.getUseStandbyMode());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -825,6 +825,7 @@ setting.preferences.general=General preferences
|
|||
setting.preferences.explorer=Bitcoin block explorer:
|
||||
setting.preferences.deviation=Max. deviation from market price:
|
||||
setting.preferences.autoSelectArbitrators=Auto select arbitrators:
|
||||
setting.preferences.avoidStandbyMode=Avoid standby mode:
|
||||
setting.preferences.deviationToLarge=Values higher than {0}% are not allowed.
|
||||
setting.preferences.txFee=Withdrawal transaction fee (satoshis/byte):
|
||||
setting.preferences.useCustomValue=Use custom value
|
||||
|
|
BIN
core/src/main/resources/silent.aiff
Normal file
BIN
core/src/main/resources/silent.aiff
Normal file
Binary file not shown.
|
@ -34,6 +34,7 @@ import bisq.desktop.util.ImageUtil;
|
|||
|
||||
import bisq.core.alert.AlertManager;
|
||||
import bisq.core.app.AppOptionKeys;
|
||||
import bisq.core.app.AvoidStandbyMode;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.btc.wallet.WalletsManager;
|
||||
import bisq.core.filter.FilterManager;
|
||||
|
@ -122,6 +123,8 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
|
|||
scene = createAndConfigScene(mainView, injector);
|
||||
setupStage(scene);
|
||||
|
||||
injector.getInstance(AvoidStandbyMode.class).init();
|
||||
|
||||
UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES);
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error during app init", throwable);
|
||||
|
|
|
@ -94,7 +94,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
|
||||
// private ComboBox<BaseCurrencyNetwork> selectBaseCurrencyNetworkComboBox;
|
||||
|
||||
private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox, showOwnOffersInOfferBook, sortMarketCurrenciesNumericallyCheckBox, useCustomFeeCheckbox;
|
||||
private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox, avoidStandbyModeCheckBox,
|
||||
showOwnOffersInOfferBook, sortMarketCurrenciesNumericallyCheckBox, useCustomFeeCheckbox;
|
||||
private int gridRow = 0;
|
||||
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, referralIdInputTextField;
|
||||
private ChangeListener<Boolean> transactionFeeFocusedListener;
|
||||
|
@ -177,7 +178,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void initializeGeneralOptions() {
|
||||
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 8, Res.get("setting.preferences.general"));
|
||||
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 9, Res.get("setting.preferences.general"));
|
||||
GridPane.setColumnSpan(titledGroupBg, 4);
|
||||
|
||||
// selectBaseCurrencyNetwork
|
||||
|
@ -296,6 +297,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
if (!newValue.equals(oldValue))
|
||||
referralIdService.setReferralId(newValue);
|
||||
};
|
||||
|
||||
// AvoidStandbyMode
|
||||
avoidStandbyModeCheckBox = addLabelCheckBox(root, ++gridRow,
|
||||
Res.get("setting.preferences.avoidStandbyMode"), "").second;
|
||||
}
|
||||
|
||||
private void initializeDisplayCurrencies() {
|
||||
|
@ -646,6 +651,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
|
||||
autoSelectArbitratorsCheckBox.setSelected(preferences.isAutoSelectArbitrators());
|
||||
autoSelectArbitratorsCheckBox.setOnAction(e -> preferences.setAutoSelectArbitrators(autoSelectArbitratorsCheckBox.isSelected()));
|
||||
|
||||
// We use opposite property (useStandbyMode) in preferences to have the default value (false) set as we want it,
|
||||
// so users who update gets set avoidStandbyMode=true (useStandbyMode=false)
|
||||
avoidStandbyModeCheckBox.setSelected(!preferences.isUseStandbyMode());
|
||||
avoidStandbyModeCheckBox.setOnAction(e -> preferences.setUseStandbyMode(!avoidStandbyModeCheckBox.isSelected()));
|
||||
}
|
||||
|
||||
/* private void onSelectNetwork() {
|
||||
|
@ -695,5 +705,6 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
showOwnOffersInOfferBook.setOnAction(null);
|
||||
autoSelectArbitratorsCheckBox.setOnAction(null);
|
||||
resetDontShowAgainButton.setOnAction(null);
|
||||
avoidStandbyModeCheckBox.setOnAction(null);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue