From 0e85d47cc87c3ed2303f502dafe5b519c6763cc1 Mon Sep 17 00:00:00 2001 From: duhow Date: Sun, 19 Apr 2026 17:17:16 +0200 Subject: [PATCH] Sony: WH-1000XM3 add support for LDAC and NC/AMBIENT button switch (#5990) Implements missing features from [WH-1000XM3](https://gadgetbridge.org/gadgets/headphones/sony/#device__sony_wh-1000xm3): - Sound Quality Mode (SBC Codec / HD codecs) - Added as a switch (boolean) in UI - Note that LDAC option can appear in Andoid Bluetooth Device settings as well as unselected. Some Android devices (eg. Samsung) will also allow changing the BT Codec in Developer Settings, but if this is not enabled, the change won't apply. - Function of NC/AMBIENT button - Allows using either Google Assistant or Alexa Assistant. - This model does not support "system assistant", used for other non-Google/Alexa assistants. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/5990 Co-authored-by: duhow Co-committed-by: duhow --- .../DeviceSettingsPreferenceConst.java | 2 + .../DeviceSpecificSettingsFragment.java | 2 + .../SonyHeadphonesCapabilities.java | 2 + .../headphones/SonyHeadphonesCoordinator.java | 2 + .../SonyWH1000XM3Coordinator.java | 2 + .../sony/headphones/prefs/AudioLDAC.java | 46 ++++++++++ .../prefs/ButtonFunctionNcAmbient.java | 83 +++++++++++++++++++ .../headphones/SonyHeadphonesProtocol.java | 35 ++++++++ .../impl/AbstractSonyProtocolImpl.java | 8 ++ .../protocol/impl/v1/SonyProtocolImplV1.java | 41 +++++++++ .../protocol/impl/v2/SonyProtocolImplV2.java | 27 ++++++ app/src/main/res/values/arrays.xml | 12 +++ app/src/main/res/values/strings.xml | 5 ++ ...evicesettings_sony_headphones_audio_hd.xml | 9 ++ ..._headphones_button_function_nc_ambient.xml | 12 +++ .../impl/v1/SonyProtocolImplV1Test.java | 25 ++++++ .../impl/v2/SonyProtocolImplV2Test.java | 18 ++++ 17 files changed, 331 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/AudioLDAC.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java create mode 100644 app/src/main/res/xml/devicesettings_sony_headphones_audio_hd.xml create mode 100644 app/src/main/res/xml/devicesettings_sony_headphones_button_function_nc_ambient.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index 96e9073bd0..3376b414d9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -552,6 +552,8 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_SONY_SURROUND_MODE = "pref_sony_surround_mode"; public static final String PREF_SONY_EQUALIZER = "pref_sony_equalizer"; public static final String PREF_SONY_EQUALIZER_MODE = "pref_sony_equalizer_mode"; + public static final String PREF_SONY_AUDIO_HD = "pref_sony_audio_hd"; + public static final String PREF_SONY_BUTTON_FUNCTION_NC_AMBIENT = "pref_sony_button_function_nc_ambient"; public static final String PREF_SONY_AUDIO_UPSAMPLING = "pref_sony_audio_upsampling"; public static final String PREF_SONY_EQUALIZER_BAND_400 = "pref_sony_equalizer_band_400"; public static final String PREF_SONY_EQUALIZER_BAND_1000 = "pref_sony_equalizer_band_1000"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index 2ac30b8959..3cf1f7ec39 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -840,6 +840,8 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i addPreferenceHandlerFor(PREF_SONY_EQUALIZER_BAND_6300); addPreferenceHandlerFor(PREF_SONY_EQUALIZER_BAND_16000); addPreferenceHandlerFor(PREF_SONY_EQUALIZER_BASS); + addPreferenceHandlerFor(PREF_SONY_AUDIO_HD); + addPreferenceHandlerFor(PREF_SONY_BUTTON_FUNCTION_NC_AMBIENT); addPreferenceHandlerFor(PREF_SONY_AUDIO_UPSAMPLING); addPreferenceHandlerFor(PREF_SONY_TOUCH_SENSOR); addPreferenceHandlerFor(PREF_SONY_PAUSE_WHEN_TAKEN_OFF); 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 3a10b11196..73f384cd96 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 { AmbientSoundControlButtonMode, AncOptimizer, AudioSettingsOnlyOnSbcCodec, + AudioLDAC, AudioUpsampling, AutomaticPowerOffByTime, AutomaticPowerOffWhenTakenOff, @@ -31,6 +32,7 @@ public enum SonyHeadphonesCapabilities { BatteryDual2, BatterySingle, ButtonModesLeftRight, + ButtonFunctionNcAmbient, EqualizerSimple, EqualizerWithCustomBands, NoNoiseCancelling, 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 f79db1e357..fcbf149260 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 @@ -146,6 +146,7 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC addSettingsUnderHeader(deviceSpecificSettings, device, R.xml.devicesettings_header_other, new LinkedHashMap<>() {{ put(SonyHeadphonesCapabilities.AudioSettingsOnlyOnSbcCodec, R.xml.devicesettings_sony_warning_wh1000xm3); + put(SonyHeadphonesCapabilities.AudioLDAC, R.xml.devicesettings_sony_headphones_audio_hd); put(SonyHeadphonesCapabilities.EqualizerSimple, R.xml.devicesettings_sony_headphones_equalizer); put(SonyHeadphonesCapabilities.EqualizerWithCustomBands, R.xml.devicesettings_sony_headphones_equalizer_with_custom_bands); put(SonyHeadphonesCapabilities.SoundPosition, R.xml.devicesettings_sony_headphones_sound_position); @@ -162,6 +163,7 @@ public abstract class SonyHeadphonesCoordinator extends AbstractBLClassicDeviceC put(SonyHeadphonesCapabilities.ButtonModesLeftRight, R.xml.devicesettings_sony_headphones_button_modes_left_right); put(SonyHeadphonesCapabilities.AmbientSoundControlButtonMode, R.xml.devicesettings_sony_headphones_ambient_sound_control_button_modes); put(SonyHeadphonesCapabilities.QuickAccess, R.xml.devicesettings_sony_headphones_quick_access); + put(SonyHeadphonesCapabilities.ButtonFunctionNcAmbient, R.xml.devicesettings_sony_headphones_button_function_nc_ambient); put(SonyHeadphonesCapabilities.TouchSensorSingle, R.xml.devicesettings_sony_headphones_touch_sensor_single); put(SonyHeadphonesCapabilities.PauseWhenTakenOff, R.xml.devicesettings_sony_headphones_pause_when_taken_off); put(SonyHeadphonesCapabilities.AutomaticPowerOffWhenTakenOff, R.xml.devicesettings_automatic_power_off_when_taken_off); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java index ad80a93f45..d8f9fe482f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java @@ -47,6 +47,8 @@ public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator { SonyHeadphonesCapabilities.WindNoiseReduction, SonyHeadphonesCapabilities.AncOptimizer, SonyHeadphonesCapabilities.AudioSettingsOnlyOnSbcCodec, + SonyHeadphonesCapabilities.AudioLDAC, + SonyHeadphonesCapabilities.ButtonFunctionNcAmbient, SonyHeadphonesCapabilities.EqualizerWithCustomBands, SonyHeadphonesCapabilities.SoundPosition, SonyHeadphonesCapabilities.SurroundMode, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/AudioLDAC.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/AudioLDAC.java new file mode 100644 index 0000000000..5c620b9218 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/AudioLDAC.java @@ -0,0 +1,46 @@ +/* Copyright (C) 2026 + + 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.Map; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; + +public class AudioLDAC { + private final boolean enabled; + + public AudioLDAC(final boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + public Map toPreferences() { + return new HashMap() {{ + put(DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_HD, enabled); + }}; + } + + public static AudioLDAC fromPreferences(final SharedPreferences prefs) { + return new AudioLDAC(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_HD, false)); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java new file mode 100644 index 0000000000..556b9fc8d6 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/prefs/ButtonFunctionNcAmbient.java @@ -0,0 +1,83 @@ +/* Copyright (C) 2026 + + 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.Map; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; + +public class ButtonFunctionNcAmbient { + public enum Mode { + SWITCH_AMBIENT_SOUND("switch_ambient_sound", 0x00), + GOOGLE_ASSISTANT("google_assistant", 0x01), + ALEXA_ASSISTANT("alexa_assistant", 0x02); + + private final String prefValue; + private final byte code; + + Mode(final String prefValue, final int code) { + this.prefValue = prefValue; + this.code = (byte) code; + } + + public String getPrefValue() { + return prefValue; + } + + public byte getCode() { + return code; + } + + public static Mode fromPrefValue(final String prefValue) { + for (final Mode mode : values()) { + if (mode.prefValue.equals(prefValue)) { + return mode; + } + } + + return SWITCH_AMBIENT_SOUND; + } + } + + private final Mode mode; + + public ButtonFunctionNcAmbient(final Mode mode) { + this.mode = mode; + } + + public Mode getMode() { + return mode; + } + + public Map toPreferences() { + return new HashMap() {{ + put(DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_FUNCTION_NC_AMBIENT, mode.getPrefValue()); + }}; + } + + public static ButtonFunctionNcAmbient fromPreferences(final SharedPreferences prefs) { + final String pref = prefs.getString( + DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_FUNCTION_NC_AMBIENT, + Mode.SWITCH_AMBIENT_SOUND.getPrefValue() + ); + + return new ButtonFunctionNcAmbient(Mode.fromPrefValue(pref)); + } +} 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 6fcebafd06..b484b8bcda 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 @@ -22,6 +22,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Queue; @@ -35,8 +36,10 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePref import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AdaptiveVolumeControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; @@ -228,6 +231,16 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { case DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS: configRequest = protocolImpl.setEqualizerCustomBands(EqualizerCustomBands.fromPreferences(prefs)); break; + case DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_HD: + configRequest = protocolImpl.setAudioLDAC(AudioLDAC.fromPreferences(prefs)); + break; + case DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_FUNCTION_NC_AMBIENT: + configRequest = protocolImpl.setButtonFunctionNcAmbient(ButtonFunctionNcAmbient.fromPreferences(prefs)); + final Request rebootRequest = protocolImpl.reboot(); + if (rebootRequest != null) { + enqueueRequests(Collections.singletonList(rebootRequest)); + } + break; case DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_UPSAMPLING: configRequest = protocolImpl.setAudioUpsampling(AudioUpsampling.fromPreferences(prefs)); break; @@ -302,6 +315,28 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { return super.encodePowerOff(); } + @Override + public byte[] encodeReset(final int flags) { + if ((flags & RESET_FLAGS_REBOOT) == 0) { + return super.encodeReset(flags); + } + + if (protocolImpl == null) { + LOG.error("No protocol implementation, ignoring reboot request"); + return super.encodeReset(flags); + } + + final Request rebootRequest = protocolImpl.reboot(); + if (rebootRequest == null) { + LOG.warn("Failed to encode reboot request"); + return super.encodeReset(flags); + } + + pendingAcks++; + + return rebootRequest.encode(sequenceNumber); + } + public byte[] encodeAck(byte sequenceNumber) { return new Message(MessageType.ACK, (byte) (1 - sequenceNumber), new byte[0]).encode(); } 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 fadcadfae6..38706af440 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 @@ -24,8 +24,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphon import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AdaptiveVolumeControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; @@ -86,6 +88,10 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request getAudioUpsampling(); + public abstract Request setAudioLDAC(final AudioLDAC config); + + public abstract Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config); + public abstract Request setAudioUpsampling(final AudioUpsampling config); public abstract Request getAutomaticPowerOff(); @@ -138,6 +144,8 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request powerOff(); + public abstract Request reboot(); + public abstract Request getVolume(); public abstract Request setVolume(final int volume); 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 b620a598e2..9e40280346 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 @@ -42,8 +42,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphon import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AdaptiveVolumeControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; @@ -259,6 +261,32 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { ); } + @Override + public Request setAudioLDAC(final AudioLDAC config) { + return new Request( + PayloadTypeV1.AUDIO_UPSAMPLING_SET.getMessageType(), + new byte[]{ + PayloadTypeV1.AUDIO_UPSAMPLING_SET.getCode(), + (byte) 0x01, + (byte) 0x00, + (byte) (config.isEnabled() ? 0x00 : 0x01) + } + ); + } + + @Override + public Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config) { + return new Request( + PayloadTypeV1.TOUCH_SENSOR_SET.getMessageType(), + new byte[]{ + PayloadTypeV1.TOUCH_SENSOR_SET.getCode(), + (byte) 0xd1, + (byte) 0x02, + config.getMode().getCode() + } + ); + } + @Override public Request setAudioUpsampling(final AudioUpsampling config) { return new Request( @@ -546,6 +574,19 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { ); } + @Override + public Request reboot() { + return new Request( + MessageType.COMMAND_1, + new byte[]{ + (byte) 0x98, + (byte) 0x01, + (byte) 0x02, + (byte) 0x01 + } + ); + } + @Override public Request getVolume() { return new Request( 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 e5e6b5aea0..552eb8bf2e 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 @@ -33,8 +33,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphon import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AdaptiveVolumeControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; @@ -223,6 +225,18 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { ); } + @Override + public Request setAudioLDAC(final AudioLDAC config) { + LOG.warn("Audio LDAC not implemented for V2"); + return null; + } + + @Override + public Request setButtonFunctionNcAmbient(final ButtonFunctionNcAmbient config) { + LOG.warn("Button function NC ambient not implemented for V2"); + return null; + } + @Override public Request setAudioUpsampling(final AudioUpsampling config) { return new Request( @@ -504,6 +518,19 @@ public class SonyProtocolImplV2 extends SonyProtocolImplV1 { ); } + @Override + public Request reboot() { + return new Request( + MessageType.COMMAND_1, + new byte[]{ + (byte) 0x98, + (byte) 0x00, + (byte) 0x16, + (byte) 0x01 + } + ); + } + @Override public Request getVolume() { LOG.warn("Volume not implemented for V2"); diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 9365819710..be2e39827f 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -4473,6 +4473,18 @@ as_off + + @string/sony_button_function_nc_ambient_switch_ambient_sound + @string/sony_button_function_nc_ambient_google_assistant + @string/sony_button_function_nc_ambient_alexa_assistant + + + + switch_ambient_sound + google_assistant + alexa_assistant + + @string/sony_button_mode_off diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index afb3e45f85..29d1ca673a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3384,7 +3384,12 @@ 6.3k 16k Clear Bass + Audio HD Audio Upsampling + NC/Ambient Button Function + Switch ambient sound + Google Assistant + Alexa Assistant Touch sensor control Recognize taps between cheeks and ears Wide area tap diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_audio_hd.xml b/app/src/main/res/xml/devicesettings_sony_headphones_audio_hd.xml new file mode 100644 index 0000000000..f73cb7e0c2 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_audio_hd.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_button_function_nc_ambient.xml b/app/src/main/res/xml/devicesettings_sony_headphones_button_function_nc_ambient.xml new file mode 100644 index 0000000000..8fbeab62d0 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_button_function_nc_ambient.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1Test.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1Test.java index 5c3ce9a534..00cb14f726 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1Test.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1Test.java @@ -34,8 +34,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphon import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonModes; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; @@ -221,6 +223,29 @@ public class SonyProtocolImplV1Test { }}); } + @Test + public void setAudioLDAC() { + assertRequests(protocol::setAudioLDAC, new LinkedHashMap() {{ + put(new AudioLDAC(false), "e8:01:00:01"); + put(new AudioLDAC(true), "e8:01:00:00"); + }}); + } + + @Test + public void setButtonFunctionNcAmbient() { + assertRequests(protocol::setButtonFunctionNcAmbient, new LinkedHashMap() {{ + put(new ButtonFunctionNcAmbient(ButtonFunctionNcAmbient.Mode.SWITCH_AMBIENT_SOUND), "d8:d1:02:00"); + put(new ButtonFunctionNcAmbient(ButtonFunctionNcAmbient.Mode.GOOGLE_ASSISTANT), "d8:d1:02:01"); + put(new ButtonFunctionNcAmbient(ButtonFunctionNcAmbient.Mode.ALEXA_ASSISTANT), "d8:d1:02:02"); + }}); + } + + @Test + public void reboot() { + final Request request = protocol.reboot(); + assertRequest(request, 0x0c, "98:01:02:01"); + } + @Test public void getAutomaticPowerOff() { final Request request = protocol.getAutomaticPowerOff(); diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2Test.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2Test.java index 769c5cc637..ffbfb5fac4 100644 --- a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2Test.java +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v2/SonyProtocolImplV2Test.java @@ -40,8 +40,10 @@ import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphon import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyWF1000XM4Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioLDAC; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AudioUpsampling; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.ButtonFunctionNcAmbient; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerCustomBands; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.EqualizerPreset; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.PauseWhenTakenOff; @@ -174,6 +176,22 @@ public class SonyProtocolImplV2Test { assertRequest(requestDisabled, "3e:0c:01:00:00:00:03:e8:01:00:f9:3c"); } + @Test + public void setAudioLDAC() { + // TODO + } + + @Test + public void setButtonFunctionNcAmbient() { + // TODO + } + + @Test + public void reboot() { + final Request request = protocol.reboot(); + assertRequest(request, 0x0c, "98:00:16:01"); + } + @Test public void getAutomaticPowerOff() { // TODO