From 9fd323789edc1f2d2ce6c10b42be7d2e2c0635c3 Mon Sep 17 00:00:00 2001 From: duhow Date: Sat, 13 Jun 2026 15:57:02 +0200 Subject: [PATCH] refactor: add protocol implementation and command variables --- .../soundcore/AbstractSoundcoreProtocol.java | 4 - .../aeroFit/SoundcoreAeroFitProtocol.java | 56 ++--- .../liberty/SoundcoreLibertyProtocol.java | 138 +++--------- .../motion300/SoundcoreMotion300Protocol.java | 8 +- .../impl/v1/SoundcoreProtocolImplV1.java | 206 ++++++++++++++++++ .../soundcore/q30/SoundcoreQ30Protocol.java | 56 ++--- .../sport_x20/SoundcoreSportX20Protocol.java | 111 ++-------- 7 files changed, 288 insertions(+), 291 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/protocol/impl/v1/SoundcoreProtocolImplV1.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/AbstractSoundcoreProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/AbstractSoundcoreProtocol.java index 5da6b0ff7c..cb14e5d829 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/AbstractSoundcoreProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/AbstractSoundcoreProtocol.java @@ -38,8 +38,4 @@ public abstract class AbstractSoundcoreProtocol extends GBDeviceProtocol { else return 0x00; } - public byte[] encodeDeviceInfoRequest() { - return new SoundcorePacket((short) 0x0101).encode(); - } - } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/aeroFit/SoundcoreAeroFitProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/aeroFit/SoundcoreAeroFitProtocol.java index 5293841e9a..a7caf8b649 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/aeroFit/SoundcoreAeroFitProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/aeroFit/SoundcoreAeroFitProtocol.java @@ -10,20 +10,24 @@ import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.AbstractSoundcoreProtocol; import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.SoundcorePacket; +import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class SoundcoreAeroFitProtocol extends AbstractSoundcoreProtocol { +public class SoundcoreAeroFitProtocol extends SoundcoreProtocolImplV1 { private static final Logger LOG = LoggerFactory.getLogger(SoundcoreAeroFitProtocol.class); + private static final short CMD_GET_CONNECTED_DEVICES = (short) 0x010b; + private static final short CMD_SET_BATTERY_LOW_TONE = (short) 0x8210; + private static final short CMD_SET_GAMING_MODE = (short) 0x8701; + private static final short CMD_SET_TOUCH_LOCK = (short) 0x9410; + private static final int battery_case = 0; private static final int battery_earphone_left = 1; private static final int battery_earphone_right = 2; @@ -34,8 +38,7 @@ public class SoundcoreAeroFitProtocol extends AbstractSoundcoreProtocol { @Override public GBDeviceEvent[] decodeResponse(byte[] responseData) { - ByteBuffer buf = ByteBuffer.wrap(responseData); - SoundcorePacket packet = SoundcorePacket.decode(buf); + SoundcorePacket packet = decodePacket(responseData); if (packet == null) return null; @@ -44,21 +47,21 @@ public class SoundcoreAeroFitProtocol extends AbstractSoundcoreProtocol { short cmd = packet.getCommand(); byte[] payload = packet.getPayload(); - if (cmd == (short) 0x0101) { + if (cmd == CMD_GET_DEVICE_INFO) { String firmware1 = readString(payload, 4, 5); String firmware2 = readString(payload, 9, 5); String serialNumber = readString(payload, 14, 16); handleBatteryInfo(devEvts, null, payload[2], payload[3]); devEvts.add(buildVersionInfo(firmware1, firmware2, serialNumber)); - } else if (cmd == (short) 0x010b) { + } else if (cmd == CMD_GET_CONNECTED_DEVICES) { // shows connected devices from 50 onwards // readString(payload, 50, 112) // maybe also other settings LOG.debug("Incoming Information about connected devices, dump: " + hexdump(responseData)); - } else if (cmd == (short) 0x0301) { // Battery Update + } else if (cmd == CMD_NOTIFY_BATTERY_INFO) { handleBatteryInfo(devEvts, payload[2], payload[0], payload[1]); - } else if (cmd == (short) 0x0401) { + } else if (cmd == CMD_NOTIFY_CHARGING_INFO) { boolean leftInCase = payload[0] == 0x01; boolean rightInCase = payload[1] == 0x01; LOG.info("Left Earbud in Charging Case: " + leftInCase + ", Right Earbud in Charging Case: " + rightInCase); @@ -88,21 +91,21 @@ public class SoundcoreAeroFitProtocol extends AbstractSoundcoreProtocol { // Control case PREF_SOUNDCORE_CONTROL_TOUCH_DISABLED: boolean touchDisabled = prefs.getBoolean(PREF_SOUNDCORE_CONTROL_TOUCH_DISABLED, false); - return new SoundcorePacket((short) 0x9410, new byte[]{encodeBoolean(touchDisabled)}).encode(); + return encodeBooleanCommand(CMD_SET_TOUCH_LOCK, touchDisabled); // Miscellaneous Settings case PREF_SOUNDCORE_TOUCH_TONE: boolean touchTone = prefs.getBoolean(PREF_SOUNDCORE_TOUCH_TONE, false); - return new SoundcorePacket((short) 0x8301, new byte[]{encodeBoolean(touchTone)}).encode(); + return encodeBooleanCommand(CMD_SET_TOUCH_TONE, touchTone); case PREF_SOUNDCORE_BATTERY_LOW_TONE: boolean batteryLowTone = prefs.getBoolean(PREF_SOUNDCORE_BATTERY_LOW_TONE, false); - return new SoundcorePacket((short) 0x8210, new byte[]{encodeBoolean(batteryLowTone)}).encode(); + return encodeBooleanCommand(CMD_SET_BATTERY_LOW_TONE, batteryLowTone); case PREF_SOUNDCORE_GAMING_MODE: boolean gamingMode = prefs.getBoolean(PREF_SOUNDCORE_GAMING_MODE, false); - return new SoundcorePacket((short) 0x8701, new byte[]{encodeBoolean(gamingMode)}).encode(); + return encodeBooleanCommand(CMD_SET_GAMING_MODE, gamingMode); case PREF_SOUNDCORE_AUTO_POWER_OFF: int duration = Integer.parseInt(prefs.getString(PREF_SOUNDCORE_AUTO_POWER_OFF, "3")); - return setAutoPowerOff(duration).encode(); + return encodeAutoPowerOff(duration, (byte) 0x03); default: LOG.debug("Unsupported CONFIG: " + config); } @@ -110,29 +113,4 @@ public class SoundcoreAeroFitProtocol extends AbstractSoundcoreProtocol { return super.encodeSendConfiguration(config); } - /** - * 0: No Auto Power off - * 1: Auto Power off 10 min - * 2: Auto Power off 20 min - * 3: Auto Power off 30 min - * 4: Auto Power off 60 min - */ - private SoundcorePacket setAutoPowerOff(int duration) { - byte[] payload; - - if (duration > 0) - payload = new byte[] { (byte)0x01, (byte)(duration - 1) }; - else - payload = new byte[] { (byte)0x00, (byte)0x03 }; - - return new SoundcorePacket((short) 0x8601, payload); - } - - @Override - public byte[] encodeFindDevice(boolean start) { - boolean findLeft = start; - boolean findRight = start; - byte[] payload = new byte[]{encodeBoolean(findLeft), encodeBoolean(findRight), 0x00}; - return new SoundcorePacket((short) 0x8910, payload).encode(); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/liberty/SoundcoreLibertyProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/liberty/SoundcoreLibertyProtocol.java index 0f2592c91a..3fa03e9a3b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/liberty/SoundcoreLibertyProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/liberty/SoundcoreLibertyProtocol.java @@ -2,12 +2,9 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.liberty; import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump; -import android.content.SharedPreferences; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; @@ -15,14 +12,21 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSett import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControlButtonMode; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.AbstractSoundcoreProtocol; import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.SoundcorePacket; +import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { +public class SoundcoreLibertyProtocol extends SoundcoreProtocolImplV1 { private static final Logger LOG = LoggerFactory.getLogger(SoundcoreLibertyProtocol.class); + private static final short CMD_GET_UNKNOWN_DATA_8D01 = (short) 0x8d01; + private static final short CMD_GET_UNKNOWN_DATA_8205 = (short) 0x8205; + private static final short CMD_SET_AMBIENT_SOUND_CONTROL_BUTTON_MODE = (short) 0x8206; + private static final short CMD_SET_TOUCH_LOCK = (short) 0x8304; + private static final short CMD_SET_WEARING_DETECTION = (short) 0x8101; + private static final short CMD_SET_WEARING_TONE = (short) 0x8c01; + private static final int battery_case = 0; private static final int battery_earphone_left = 1; private static final int battery_earphone_right = 2; @@ -33,8 +37,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { @Override public GBDeviceEvent[] decodeResponse(byte[] responseData) { - ByteBuffer buf = ByteBuffer.wrap(responseData); - SoundcorePacket packet = SoundcorePacket.decode(buf); + SoundcorePacket packet = decodePacket(responseData); if (packet == null) return null; @@ -43,7 +46,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { short cmd = packet.getCommand(); byte[] payload = packet.getPayload(); - if (cmd == (short) 0x0101) { + if (cmd == CMD_GET_DEVICE_INFO) { int batteryLeft = payload[2] * 20; int batteryRight = payload[3] * 20; @@ -55,15 +58,15 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { devEvts.add(buildBatteryInfo(battery_earphone_left, batteryLeft)); devEvts.add(buildBatteryInfo(battery_earphone_right, batteryRight)); devEvts.add(buildVersionInfo(firmware1, firmware2, serialNumber)); - } else if (cmd == (short) 0x8d01) { + } else if (cmd == CMD_GET_UNKNOWN_DATA_8D01) { LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData)); - } else if (cmd == (short) 0x8205) { + } else if (cmd == CMD_GET_UNKNOWN_DATA_8205) { LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData)); - } else if (cmd == (short) 0x0105) { + } else if (cmd == CMD_GET_UNKNOWN_DATA_0105) { LOG.debug("Unknown incoming message - command: " + cmd + ", dump: " + hexdump(responseData)); - } else if (cmd == (short) 0x0106) { // ANC Mode Update - decodeAudioMode(payload); - } else if (cmd == (short) 0x0301) { // Battery Update + } else if (cmd == CMD_NOTIFY_AUDIO_MODE) { + decodeAdvancedAudioMode(payload); + } else if (cmd == CMD_NOTIFY_BATTERY_INFO) { int batteryLeft = payload[0] * 20; int batteryRight = payload[1] * 20; int batteryCase = payload[2] * 20; @@ -79,40 +82,6 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { return devEvts.toArray(new GBDeviceEvent[devEvts.size()]); } - private void decodeAudioMode(byte[] payload) { - SharedPreferences prefs = getDevicePrefs().getPreferences(); - SharedPreferences.Editor editor = prefs.edit(); - String ambient_sound_mode = "off"; - int anc_strength = 0; - - if (payload[0] == 0x00) { - ambient_sound_mode = "noise_cancelling"; - } else if (payload[0] == 0x01) { - ambient_sound_mode = "ambient_sound"; - } else if (payload[0] == 0x02) { - ambient_sound_mode = "off"; - } - - if (payload[1] == 0x10) { - anc_strength = 0; - } else if (payload[1] == 0x20) { - anc_strength = 1; - } else if (payload[1] == 0x30) { - anc_strength = 2; - } - - boolean vocal_mode = (payload[2] == 0x01); - boolean adaptive_anc = (payload[3] == 0x01); - boolean windnoiseReduction = (payload[4] == 0x01); - - editor.putString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, ambient_sound_mode); - editor.putInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, anc_strength); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, vocal_mode); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, adaptive_anc); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, windnoiseReduction); - editor.apply(); - } - @Override public byte[] encodeSendConfiguration(String config) { Prefs prefs = getDevicePrefs(); @@ -125,7 +94,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING: case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL: - return encodeAudioMode(); + return encodeAdvancedAudioMode(true); // Control case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_CONTROL_SINGLE_TAP_DISABLED: @@ -178,13 +147,13 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { // Miscellaneous Settings case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION: boolean wearingDetection = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_DETECTION, false); - return new SoundcorePacket((short) 0x8101, new byte[]{encodeBoolean(wearingDetection)}).encode(); + return encodeBooleanCommand(CMD_SET_WEARING_DETECTION, wearingDetection); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE: boolean wearingTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WEARING_TONE, false); - return new SoundcorePacket((short) 0x8c01, new byte[]{encodeBoolean(wearingTone)}).encode(); + return encodeBooleanCommand(CMD_SET_WEARING_TONE, wearingTone); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE: boolean touchTone = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE, false); - return new SoundcorePacket((short) 0x8301, new byte[]{encodeBoolean(touchTone)}).encode(); + return encodeBooleanCommand(CMD_SET_TOUCH_TONE, touchTone); default: LOG.debug("Unsupported CONFIG: " + config); } @@ -194,66 +163,14 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { byte[] encodeMysteryDataRequest1() { byte[] payload = new byte[]{0x00}; - return new SoundcorePacket((short) 0x8d01, payload).encode(); + return encodeCommand(CMD_GET_UNKNOWN_DATA_8D01, payload); } byte[] encodeMysteryDataRequest2() { - return new SoundcorePacket((short) 0x0105).encode(); + return encodeRequest(CMD_GET_UNKNOWN_DATA_0105); } byte[] encodeMysteryDataRequest3() { byte[] payload = new byte[]{0x00}; - return new SoundcorePacket((short) 0x8205, payload).encode(); - } - - /** - * Encodes the following settings to a payload to set the audio-mode on the headphones: - * PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL If ANC, Transparent or neither should be active - * PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING If the strenght of the ANC should be set manual or adaptively according to ambient noise - * PREF_SONY_AMBIENT_SOUND_LEVEL How strong the ANC should be in manual mode - * PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE If the Transparency should focus on vocals or should be fully transparent - * PREF_SOUNDCORE_WIND_NOISE_REDUCTION If Transparency or ANC should reduce Wind Noise - * @return The payload - */ - private byte[] encodeAudioMode() { - Prefs prefs = getDevicePrefs(); - - byte ambient_sound_mode; - switch (prefs.getString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, "off")) { - case "noise_cancelling": - ambient_sound_mode = 0x00; - break; - case "ambient_sound": - ambient_sound_mode = 0x01; - break; - case "off": - ambient_sound_mode = 0x02; - break; - default: - LOG.error("Invalid Ambient Mode selected"); - return null; - } - - byte anc_strength; - switch (prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, 0)) { - case 0: - anc_strength = 0x10; - break; - case 1: - anc_strength = 0x20; - break; - case 2: - anc_strength = 0x30; - break; - default: - LOG.error("Invalid ANC Strength selected"); - return null; - } - - byte adaptive_anc = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, true)); - byte vocal_mode = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, false)); - byte windnoise_reduction = encodeBoolean(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, false)); - - byte[] payload = new byte[]{ambient_sound_mode, anc_strength, vocal_mode, adaptive_anc, windnoise_reduction, 0x01}; - return new SoundcorePacket((short) 0x8106, payload).encode(); + return encodeCommand(CMD_GET_UNKNOWN_DATA_8205, payload); } /** @@ -280,7 +197,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { return null; } payload = new byte[]{0x00, action.getCode(), enabled_byte}; - return new SoundcorePacket((short) 0x8304, payload).encode(); + return encodeCommand(CMD_SET_TOUCH_LOCK, payload); } /** @@ -307,8 +224,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { LOG.error("Invalid Tap action"); return null; } - byte[] payload = new byte[] {encodeBoolean(right), action.getCode(), function_byte}; - return new SoundcorePacket((short) 0x8104, payload).encode(); + return encodeControlFunctionMessage(right, action.getCode(), function_byte); } /** @@ -318,7 +234,7 @@ public class SoundcoreLibertyProtocol extends AbstractSoundcoreProtocol { private byte[] encodeControlAmbientModeMessage(boolean anc, boolean transparency, boolean normal) { // Original app does not allow only one true flag. Unsure if Earbuds accept this state. byte ambientModes = (byte) (4 * (normal?1:0) + 2 * (transparency?1:0) + (anc?1:0)); - return new SoundcorePacket((short) 0x8206, new byte[] {ambientModes}).encode(); + return encodeCommand(CMD_SET_AMBIENT_SOUND_CONTROL_BUTTON_MODE, new byte[] {ambientModes}); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/motion300/SoundcoreMotion300Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/motion300/SoundcoreMotion300Protocol.java index 5f218ec014..a64bb289ab 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/motion300/SoundcoreMotion300Protocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/motion300/SoundcoreMotion300Protocol.java @@ -38,12 +38,15 @@ import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1.CMD_GET_DEVICE_INFO; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1.CMD_NOTIFY_BATTERY_INFO; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1.CMD_NOTIFY_CHARGING_INFO; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1.CMD_SET_AUTO_POWER_OFF; public class SoundcoreMotion300Protocol extends GBDeviceProtocol { private static final Logger LOG = LoggerFactory.getLogger(SoundcoreMotion300Protocol.class); // Some of these commands are not used right now, they serve as documentation - private static final short CMD_GET_DEVICE_INFO = (short)0x0101; private static final short CMD_GET_LDAC_MODE = (short)0x7f01; private static final short CMD_GET_BUTTON_BRIGHTNESS = (short)0x9310; private static final short CMD_GET_EQUALIZER = (short)0x8902; @@ -51,14 +54,11 @@ public class SoundcoreMotion300Protocol extends GBDeviceProtocol { private static final short CMD_SET_VOICE_PROMPTS = (short)0x9001; private static final short CMD_SET_BUTTON_BRIGHTNESS = (short)0x9210; - private static final short CMD_SET_AUTO_POWER_OFF = (short)0x8601; private static final short CMD_SET_LDAC_MODE = (short)0xff01; private static final short CMD_SET_ADAPTIVE_DIRECTION = (short)0x8a02; private static final short CMD_SET_EQUALIZER_PRESET = (short)0x8b02; private static final short CMD_SET_EQUALIZER_CUSTOM = (short)0x8d02; - private static final short CMD_NOTIFY_BATTERY_INFO = (short)0x0301; - private static final short CMD_NOTIFY_CHARGING_INFO = (short)0x0401; private static final short CMD_NOTIFY_VOLUME_INFO = (short)0x0901; private static final short CMD_NOTIFY_PLAYBACK_INFO = (short)0x2101; private static final short CMD_NOTIFY_BASS_MODE = (short)0x8e02; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/protocol/impl/v1/SoundcoreProtocolImplV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/protocol/impl/v1/SoundcoreProtocolImplV1.java new file mode 100644 index 0000000000..626bb9d06e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/protocol/impl/v1/SoundcoreProtocolImplV1.java @@ -0,0 +1,206 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1; + +import android.content.SharedPreferences; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.AbstractSoundcoreProtocol; +import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.SoundcorePacket; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +public abstract class SoundcoreProtocolImplV1 extends AbstractSoundcoreProtocol { + public static final short CMD_GET_DEVICE_INFO = (short) 0x0101; + public static final short CMD_GET_UNKNOWN_DATA_0105 = (short) 0x0105; + + public static final short CMD_NOTIFY_BATTERY_INFO = (short) 0x0301; + public static final short CMD_NOTIFY_CHARGING_INFO = (short) 0x0401; + public static final short CMD_NOTIFY_AUDIO_MODE = (short) 0x0106; + + public static final short CMD_SET_AUDIO_MODE = (short) 0x8106; + public static final short CMD_SET_CONTROL_FUNCTION = (short) 0x8104; + public static final short CMD_SET_TOUCH_TONE = (short) 0x8301; + public static final short CMD_SET_AUTO_POWER_OFF = (short) 0x8601; + public static final short CMD_SET_FIND_DEVICE = (short) 0x8910; + public static final short CMD_ENABLE_PAIRING_MODE = (short) 0x850b; + + protected SoundcoreProtocolImplV1(final GBDevice device) { + super(device); + } + + protected SoundcorePacket decodePacket(final byte[] responseData) { + return SoundcorePacket.decode(ByteBuffer.wrap(responseData)); + } + + protected byte[] encodeRequest(final short command) { + return new SoundcorePacket(command).encode(); + } + + protected byte[] encodeCommand(final short command, final byte[] payload) { + return new SoundcorePacket(command, payload).encode(); + } + + protected byte[] encodeBooleanCommand(final short command, final boolean enabled) { + return encodeCommand(command, new byte[]{encodeBoolean(enabled)}); + } + + public byte[] encodeDeviceInfoRequest() { + return encodeRequest(CMD_GET_DEVICE_INFO); + } + + @Override + public byte[] encodeFindDevice(final boolean start) { + return encodeCommand(CMD_SET_FIND_DEVICE, new byte[]{ + encodeBoolean(start), + encodeBoolean(start), + 0x00 + }); + } + + protected byte[] encodePairingMode() { + return encodeCommand(CMD_ENABLE_PAIRING_MODE, new byte[]{0x00, (byte) 0x90}); + } + + protected byte[] encodeAutoPowerOff(final int duration, final byte disabledDuration) { + final byte[] payload; + + if (duration > 0) { + payload = new byte[]{0x01, (byte) (duration - 1)}; + } else { + payload = new byte[]{0x00, disabledDuration}; + } + + return encodeCommand(CMD_SET_AUTO_POWER_OFF, payload); + } + + protected byte[] encodeControlFunctionMessage(final boolean right, final byte action, final byte function) { + return encodeCommand(CMD_SET_CONTROL_FUNCTION, new byte[]{encodeBoolean(right), action, function}); + } + + protected byte[] encodeAdvancedAudioMode(final boolean appendStateByte) { + final Prefs prefs = getDevicePrefs(); + final Byte ambientSoundMode = encodeAmbientSoundMode(prefs.getString( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, + "off" + )); + + if (ambientSoundMode == null) { + return null; + } + + final Byte ancStrength = encodeAncStrength(prefs.getInt( + DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, + 0 + )); + + if (ancStrength == null) { + return null; + } + + final byte vocalMode = encodeBoolean(prefs.getBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, + false + )); + final byte adaptiveAnc = encodeBoolean(prefs.getBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, + true + )); + final byte windNoiseReduction = encodeBoolean(prefs.getBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, + false + )); + + final byte[] payload = new byte[]{ + ambientSoundMode.byteValue(), + ancStrength.byteValue(), + vocalMode, + adaptiveAnc, + windNoiseReduction + }; + + if (appendStateByte) { + final byte[] statePayload = Arrays.copyOf(payload, payload.length + 1); + statePayload[statePayload.length - 1] = 0x01; + return encodeCommand(CMD_SET_AUDIO_MODE, statePayload); + } + + return encodeCommand(CMD_SET_AUDIO_MODE, payload); + } + + protected void decodeAdvancedAudioMode(final byte[] payload) { + if (payload.length < 5) { + return; + } + + final SharedPreferences.Editor editor = getDevicePrefs().getPreferences().edit(); + + editor.putString( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, + decodeAmbientSoundMode(payload[0]) + ); + editor.putInt( + DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, + decodeAncStrength(payload[1]) + ); + editor.putBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, + payload[2] == 0x01 + ); + editor.putBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, + payload[3] == 0x01 + ); + editor.putBoolean( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, + payload[4] == 0x01 + ); + editor.apply(); + } + + protected Byte encodeAmbientSoundMode(final String ambientMode) { + switch (ambientMode) { + case "noise_cancelling": + return (byte) 0x00; + case "ambient_sound": + return (byte) 0x01; + case "off": + return (byte) 0x02; + default: + return null; + } + } + + protected String decodeAmbientSoundMode(final byte ambientMode) { + switch (ambientMode) { + case 0x00: + return "noise_cancelling"; + case 0x01: + return "ambient_sound"; + case 0x02: + default: + return "off"; + } + } + + protected Byte encodeAncStrength(final int strength) { + if (strength < 0 || strength > 2) { + return null; + } + + return (byte) ((strength + 1) << 4); + } + + protected int decodeAncStrength(final byte strength) { + switch (strength & 0x30) { + case 0x20: + return 1; + case 0x30: + return 2; + case 0x10: + default: + return 0; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/q30/SoundcoreQ30Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/q30/SoundcoreQ30Protocol.java index 1c5a003f8b..fd51240263 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/q30/SoundcoreQ30Protocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/q30/SoundcoreQ30Protocol.java @@ -7,7 +7,6 @@ import android.content.SharedPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -15,22 +14,23 @@ import java.util.List; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.AbstractSoundcoreProtocol; import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.SoundcorePacket; +import nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.protocol.impl.v1.SoundcoreProtocolImplV1; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; -public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { +public class SoundcoreQ30Protocol extends SoundcoreProtocolImplV1 { private static final Logger LOG = LoggerFactory.getLogger(SoundcoreQ30Protocol.class); + private static final short CMD_SET_EQUALIZER = (short) 0x8102; + protected SoundcoreQ30Protocol(GBDevice device) { super(device); } @Override public GBDeviceEvent[] decodeResponse(byte[] responseData) { - ByteBuffer buf = ByteBuffer.wrap(responseData); - SoundcorePacket packet = SoundcorePacket.decode(buf); + SoundcorePacket packet = decodePacket(responseData); if (packet == null) return null; @@ -39,7 +39,7 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { short cmd = packet.getCommand(); byte[] payload = packet.getPayload(); - if (cmd == (short) 0x0101) { + if (cmd == CMD_GET_DEVICE_INFO) { int battery = payload[0] * 20; // only 20% steps available here devEvts.add(buildBatteryInfo(0, battery)); @@ -56,9 +56,9 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { String serialNumber = readString(payload, 44, 16); devEvts.add(buildVersionInfo(firmware1, firmware2, serialNumber)); - } else if (cmd == (short) 0x0106) { // ANC Mode Updated by Button + } else if (cmd == CMD_NOTIFY_AUDIO_MODE) { decodeAudioMode(payload); - } else if (cmd == (short) 0x8106) { + } else if (cmd == CMD_SET_AUDIO_MODE) { // Acknowledgement for changed Ambient Mode // empty payload } else { @@ -94,20 +94,14 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { private byte[] encodeAudioMode() { Prefs prefs = getDevicePrefs(); - byte ambient_sound_mode; - switch (prefs.getString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, "off")) { - case "noise_cancelling": - ambient_sound_mode = 0x00; - break; - case "ambient_sound": - ambient_sound_mode = 0x01; - break; - case "off": - ambient_sound_mode = 0x02; - break; - default: - LOG.error("Invalid Ambient Mode selected"); - return null; + final Byte ambient_sound_mode = encodeAmbientSoundMode(prefs.getString( + DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, + "off" + )); + + if (ambient_sound_mode == null) { + LOG.error("Invalid Ambient Mode selected"); + return null; } byte anc_mode; @@ -126,8 +120,8 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { return null; } - byte[] payload = new byte[]{ambient_sound_mode, anc_mode, 0x01}; - return new SoundcorePacket((short) 0x8106, payload).encode(); + byte[] payload = new byte[]{ambient_sound_mode.byteValue(), anc_mode, 0x01}; + return encodeCommand(CMD_SET_AUDIO_MODE, payload); } /** @@ -136,17 +130,9 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { private void decodeAudioMode(byte[] payload) { SharedPreferences prefs = getDevicePrefs().getPreferences(); SharedPreferences.Editor editor = prefs.edit(); - String ambient_sound_mode = "off"; + String ambient_sound_mode = decodeAmbientSoundMode(payload[0]); String anc_mode = "transport"; - if (payload[0] == 0x00) { - ambient_sound_mode = "noise_cancelling"; - } else if (payload[0] == 0x01) { - ambient_sound_mode = "ambient_sound"; - } else if (payload[0] == 0x02) { - ambient_sound_mode = "off"; - } - if (payload[1] == 0x00) { anc_mode = "transport"; } else if (payload[1] == 0x01) { @@ -178,7 +164,7 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { byte band8 = 0x78; byte[] payload = new byte[]{(byte) 0xfe, (byte) 0xfe, band1, band2, band3, band4, band5, band6, band7, band8}; - return new SoundcorePacket((short) 0x8102, payload).encode(); + return encodeCommand(CMD_SET_EQUALIZER, payload); } private void decodeEqualizer(byte[] payload) { @@ -195,6 +181,6 @@ public class SoundcoreQ30Protocol extends AbstractSoundcoreProtocol { byte[] encodeMysteryDataRequest2() { - return new SoundcorePacket((short) 0x0105).encode(); + return encodeRequest(CMD_GET_UNKNOWN_DATA_0105); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/sport_x20/SoundcoreSportX20Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/sport_x20/SoundcoreSportX20Protocol.java index 6d7dbc7a99..1a9f692c4a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/sport_x20/SoundcoreSportX20Protocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/soundcore/sport_x20/SoundcoreSportX20Protocol.java @@ -2,7 +2,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.soundcore.sport_x20 import android.content.SharedPreferences; -import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; @@ -15,6 +14,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { private static final short CMD_SET_EQUALIZER = (short) 0x8703; + private static final short CMD_SET_3D_SURROUND = (short) 0x8602; + private static final int CUSTOM_PRESET_ID = 0xfe; private static final int EQ_BANDS = 8; @@ -53,11 +54,10 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { @Override public GBDeviceEvent[] decodeResponse(final byte[] responseData) { - final ByteBuffer buf = ByteBuffer.wrap(responseData); - final SoundcorePacket packet = SoundcorePacket.decode(buf); + final SoundcorePacket packet = decodePacket(responseData); - if (packet != null && packet.getCommand() == (short) 0x0106) { - decodeAncAudioMode(packet.getPayload()); + if (packet != null && packet.getCommand() == CMD_NOTIFY_AUDIO_MODE) { + decodeAdvancedAudioMode(packet.getPayload()); return new GBDeviceEvent[0]; } @@ -80,7 +80,7 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING: case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL: - return encodeAncAudioMode(); + return encodeAdvancedAudioMode(false); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_CONTROL_SINGLE_TAP_ACTION_LEFT: prefString = prefs.getString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_CONTROL_SINGLE_TAP_ACTION_LEFT, "PLAYPAUSE"); @@ -109,11 +109,11 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE: final boolean pressAlert = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TOUCH_TONE, false); - return new SoundcorePacket((short) 0x8301, new byte[]{encodeBoolean(pressAlert)}).encode(); + return encodeBooleanCommand(CMD_SET_TOUCH_TONE, pressAlert); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_3D_SURROUND: final boolean surround3d = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_3D_SURROUND, false); - return new SoundcorePacket((short) 0x8602, new byte[]{encodeBoolean(surround3d)}).encode(); + return encodeBooleanCommand(CMD_SET_3D_SURROUND, surround3d); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_EQUALIZER_PRESET: case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_EQUALIZER_BAND1_VALUE: @@ -127,21 +127,13 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { return encodeEqualizer(); case DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ENABLE_PAIRING_MODE: - return new SoundcorePacket((short) 0x850b, new byte[]{0x00, (byte) 0x90}).encode(); + return encodePairingMode(); default: return super.encodeSendConfiguration(config); } } - @Override - public byte[] encodeFindDevice(final boolean start) { - final boolean findLeft = start; - final boolean findRight = start; - final byte[] payload = new byte[]{encodeBoolean(findLeft), encodeBoolean(findRight), 0x00}; - return new SoundcorePacket((short) 0x8910, payload).encode(); - } - private byte[] encodeControlFunctionMessage(final TapAction action, final boolean right, final TapFunction function) { final byte functionByte; switch (action) { @@ -158,8 +150,7 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { return null; } - final byte[] payload = new byte[]{encodeBoolean(right), action.getCode(), functionByte}; - return new SoundcorePacket((short) 0x8104, payload).encode(); + return encodeControlFunctionMessage(right, action.getCode(), functionByte); } /** @@ -170,83 +161,7 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { * 4: Auto Power off 60 min */ private byte[] encodeAutoPowerOff(final int duration) { - final byte[] payload; - - if (duration > 0) { - payload = new byte[]{(byte) 0x01, (byte) (duration - 1)}; - } else { - payload = new byte[]{(byte) 0x00, (byte) 0x03}; - } - - return new SoundcorePacket((short) 0x8601, payload).encode(); - } - - private void decodeAncAudioMode(final byte[] payload) { - if (payload.length < 5) { - return; - } - - final SharedPreferences prefs = getDevicePrefs().getPreferences(); - final SharedPreferences.Editor editor = prefs.edit(); - - String ambientSoundMode = "off"; - if (payload[0] == 0x00) { - ambientSoundMode = "noise_cancelling"; - } else if (payload[0] == 0x01) { - ambientSoundMode = "ambient_sound"; - } else if (payload[0] == 0x02) { - ambientSoundMode = "off"; - } - - int ancStrength = ((payload[1] & 0x30) >> 4) - 1; - - final boolean vocalMode = (payload[2] == 0x01); - final boolean adaptiveAnc = (payload[3] == 0x01); - final boolean windNoiseReduction = (payload[4] == 0x01); - - editor.putString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, ambientSoundMode); - editor.putInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, ancStrength); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, vocalMode); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, adaptiveAnc); - editor.putBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, windNoiseReduction); - editor.apply(); - } - - private byte[] encodeAncAudioMode() { - final Prefs prefs = getDevicePrefs(); - - final String ambientMode = prefs.getString(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_AMBIENT_SOUND_CONTROL, "off"); - final int ancStrengthValue = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL, 0); - final boolean vocalMode = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_TRANSPARENCY_VOCAL_MODE, false); - final boolean adaptive = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_ADAPTIVE_NOISE_CANCELLING, true); - final boolean windReduction = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SOUNDCORE_WIND_NOISE_REDUCTION, false); - - if (ancStrengthValue < 0 || ancStrengthValue > 2) { - return null; - } - final byte ancStrengthByte = (byte) ((ancStrengthValue + 1) << 4); - - final byte ambientModeByte; - switch (ambientMode) { - case "noise_cancelling": - ambientModeByte = 0x00; - break; - case "ambient_sound": - ambientModeByte = 0x01; - break; - case "off": - ambientModeByte = 0x02; - break; - default: - return null; - } - - final byte adaptiveByte = encodeBoolean(adaptive); - final byte vocalModeByte = encodeBoolean(vocalMode); - final byte windReductionByte = encodeBoolean(windReduction); - - final byte[] payload = new byte[]{ambientModeByte, ancStrengthByte, vocalModeByte, adaptiveByte, windReductionByte}; - return new SoundcorePacket((short) 0x8106, payload).encode(); + return encodeAutoPowerOff(duration, (byte) 0x03); } private byte[] encodeEqualizer() { @@ -256,7 +171,7 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { if (preset != CUSTOM_PRESET_ID) { final byte[] presetPayload = EQ_PRESET_PAYLOADS.get(preset); if (presetPayload != null) { - return new SoundcorePacket(CMD_SET_EQUALIZER, presetPayload).encode(); + return encodeCommand(CMD_SET_EQUALIZER, presetPayload); } } @@ -276,7 +191,7 @@ public class SoundcoreSportX20Protocol extends SoundcoreLibertyProtocol { bandsToHex(tailBands) ); - return new SoundcorePacket(CMD_SET_EQUALIZER, payload).encode(); + return encodeCommand(CMD_SET_EQUALIZER, payload); } private void decodeEqualizer(final byte[] payload) {