diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCapabilities.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCapabilities.java
index 71241f6c3e..abd6a50e31 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCapabilities.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCapabilities.java
@@ -23,6 +23,7 @@ public enum SonyHeadphonesCapabilities {
PowerOffFromPhone,
AmbientSoundControl,
WindNoiseReduction,
+ SpeakToChat,
AncOptimizer,
AudioSettingsOnlyOnSbcCodec,
AudioUpsampling,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java
index 229c38dcef..3746fe2ee3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java
@@ -178,6 +178,10 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC
settings.add(R.xml.devicesettings_sony_headphones_ambient_sound_control);
}
+ if (supports(SonyHeadphonesCapabilities.SpeakToChat)) {
+ settings.add(R.xml.devicesettings_sony_headphones_speak_to_chat_simple);
+ }
+
if (supports(SonyHeadphonesCapabilities.AncOptimizer)) {
settings.add(R.xml.devicesettings_sony_headphones_anc_optimizer);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java
index 58b63dc540..86afc0d1b4 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java
@@ -51,6 +51,7 @@ public class SonyWH1000XM4Coordinator extends SonyHeadphonesCoordinator {
SonyHeadphonesCapabilities.BatterySingle,
SonyHeadphonesCapabilities.AmbientSoundControl,
SonyHeadphonesCapabilities.WindNoiseReduction,
+ SonyHeadphonesCapabilities.SpeakToChat,
SonyHeadphonesCapabilities.AncOptimizer,
SonyHeadphonesCapabilities.EqualizerWithCustomBands,
SonyHeadphonesCapabilities.AudioUpsampling,
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/SpeakToChat.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/SpeakToChat.java
new file mode 100644
index 0000000000..5b5e5f6489
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/SpeakToChat.java
@@ -0,0 +1,53 @@
+/* Copyright (C) 2023 José Rebelo
+
+ This file is part of Gadgetbridge.
+
+ Gadgetbridge 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.
+
+ Gadgetbridge 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 this program. If not, see . */
+package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs;
+
+import android.content.SharedPreferences;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
+
+public class SpeakToChat {
+ private final boolean enabled;
+
+ public SpeakToChat(final boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String toString() {
+ return String.format(Locale.getDefault(), "SpeakToChat{enabled=%s}", enabled);
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public Map toPreferences() {
+ return new HashMap() {{
+ put(DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT, enabled);
+ }};
+ }
+
+ public static SpeakToChat fromPreferences(final SharedPreferences prefs) {
+ final boolean enabled = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT, false);
+
+ return new SpeakToChat(enabled);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java
index 93d177b74a..def7bdf58a 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java
@@ -40,6 +40,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.Equali
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
+import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChat;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
@@ -223,8 +224,8 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol {
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY:
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE:
case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_TIMEOUT:
- LOG.warn("Speak-to-chat is not implemented ('{}')", config);
- return super.encodeSendConfiguration(config);
+ configRequest = protocolImpl.setSpeakToChat(SpeakToChat.fromPreferences(prefs));
+ break;
default:
LOG.warn("Unknown config '{}'", config);
return super.encodeSendConfiguration(config);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java
index b73f3f8346..83a20769eb 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java
@@ -30,6 +30,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.Equali
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
+import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChat;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
@@ -58,6 +59,10 @@ public abstract class AbstractSonyProtocolImpl {
public abstract Request setAmbientSoundControl(final AmbientSoundControl config);
+ public abstract Request setSpeakToChat(final SpeakToChat config);
+
+ public abstract Request getSpeakToChat();
+
public abstract Request getNoiseCancellingOptimizerState();
public abstract Request getAudioCodec();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java
index a403157333..2cad4083e3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java
@@ -50,6 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.Equali
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
+import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChat;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
@@ -139,6 +140,30 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return new Request(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array());
}
+ @Override
+ public Request setSpeakToChat(final SpeakToChat config) {
+ return new Request(
+ PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getMessageType(),
+ new byte[]{
+ PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_SET.getCode(),
+ (byte) 0x05,
+ (byte) 0x01,
+ (byte) (config.isEnabled() ? 0x01 : 0x00)
+ }
+ );
+ }
+
+ @Override
+ public Request getSpeakToChat() {
+ return new Request(
+ PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getMessageType(),
+ new byte[]{
+ PayloadTypeV1.AUTOMATIC_POWER_OFF_BUTTON_MODE_GET.getCode(),
+ (byte) 0x05
+ }
+ );
+ }
+
@Override
public Request getNoiseCancellingOptimizerState() {
return new Request(
@@ -542,6 +567,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
put(SonyHeadphonesCapabilities.EqualizerWithCustomBands, getEqualizer());
put(SonyHeadphonesCapabilities.SoundPosition, getSoundPosition());
put(SonyHeadphonesCapabilities.SurroundMode, getSurroundMode());
+ put(SonyHeadphonesCapabilities.SpeakToChat, getSpeakToChat());
put(SonyHeadphonesCapabilities.PauseWhenTakenOff, getPauseWhenTakenOff());
put(SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, getAmbientSoundControlButtonMode());
put(SonyHeadphonesCapabilities.QuickAccess, getQuickAccess());
@@ -707,6 +733,26 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return Collections.singletonList(event);
}
+ public List extends GBDeviceEvent> handleSpeakToChat(final byte[] payload) {
+ if (payload.length != 4) {
+ LOG.warn("Unexpected payload length {}", payload.length);
+ return Collections.emptyList();
+ }
+
+ final Boolean enabled = booleanFromByte(payload[3]);
+ if (enabled == null) {
+ LOG.warn("Unknown speak to chat code {}", String.format("%02x", payload[3]));
+ return Collections.emptyList();
+ }
+
+ LOG.debug("Speak to chat: {}", enabled);
+
+ final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences()
+ .withPreferences(new SpeakToChat(enabled).toPreferences());
+
+ return Collections.singletonList(event);
+ }
+
public List extends GBDeviceEvent> handleButtonModes(final byte[] payload) {
if (payload.length != 5) {
LOG.warn("Unexpected payload length {}", payload.length);
@@ -907,6 +953,8 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl {
return handleAutomaticPowerOff(payload);
case 0x03:
return handlePauseWhenTakenOff(payload);
+ case 0x05:
+ return handleSpeakToChat(payload);
case 0x06:
return handleButtonModes(payload);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java
index d95c71a096..f0790cdf86 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2.java
@@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.Equali
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.QuickAccess;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SoundPosition;
+import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SpeakToChat;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.SurroundMode;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.TouchSensor;
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.VoiceNotifications;
@@ -104,6 +105,18 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 {
return new Request(PayloadTypeV1.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array());
}
+ @Override
+ public Request setSpeakToChat(SpeakToChat config) {
+ LOG.warn("Speak-to-chat not implemented for V2");
+ return null;
+ }
+
+ @Override
+ public Request getSpeakToChat() {
+ LOG.warn("Speak-to-chat not implemented for V2");
+ return null;
+ }
+
@Override
public Request getNoiseCancellingOptimizerState() {
LOG.warn("Noise cancelling optimizer not implemented for V2");