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 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 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");