diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/AbstractEarFunCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/AbstractEarFunCoordinator.java index b77ad5292..47263c249 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/AbstractEarFunCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/AbstractEarFunCoordinator.java @@ -4,14 +4,12 @@ import androidx.annotation.NonNull; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunDeviceSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunSettingsCustomizer; public abstract class AbstractEarFunCoordinator extends AbstractDeviceCoordinator { @Override @@ -23,6 +21,11 @@ public abstract class AbstractEarFunCoordinator extends AbstractDeviceCoordinato return "EarFun"; } + @Override + public ConnectionType getConnectionType() { + return ConnectionType.BT_CLASSIC; + } + @NonNull @Override public Class getDeviceSupportClass() { @@ -30,7 +33,7 @@ public abstract class AbstractEarFunCoordinator extends AbstractDeviceCoordinato } @Override - public int getBondingStyle(){ + public int getBondingStyle() { return BONDING_STYLE_BOND; } @@ -43,9 +46,4 @@ public abstract class AbstractEarFunCoordinator extends AbstractDeviceCoordinato public int getDisabledIconResource() { return R.drawable.ic_device_nothingear_disabled; } - - @Override - public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { - return new EarFunSettingsCustomizer(device); - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirPro4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirPro4Coordinator.java index 7e6b26ce0..bba0ae44a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirPro4Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirPro4Coordinator.java @@ -2,18 +2,25 @@ package nodomain.freeyourgadget.gadgetbridge.devices.earfun; import androidx.annotation.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Arrays; import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4.EarFunAirPro4DeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4.EarFunAirPro4SettingsCustomizer; public class EarFunAirPro4Coordinator extends AbstractEarFunCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(EarFunAirPro4Coordinator.class); + @Override public int getDeviceNameResource() { return R.string.devicetype_earfun_air_pro_4; @@ -27,12 +34,22 @@ public class EarFunAirPro4Coordinator extends AbstractEarFunCoordinator { // can't only check with name, because the device name can be changed // via the device settings, so we use some of the UUIDs available on the device - // and the mac address prefix to hopefully detect this model reliable - String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb"; - String[] uuidPrefixes = {"0000180f", "0000180a"}; + // and the mac address prefix to hopefully detect this model reliably + String[] uuids = { + "00001101-0000-1000-8000-00805f9b34fb", + "0000111e-0000-1000-8000-00805f9b34fb", + "0000110b-0000-1000-8000-00805f9b34fb", + "0000110e-0000-1000-8000-00805f9b34fb", + "0000eb04-d102-11e1-9b23-00025b00a5a5", + "0000eb06-d102-11e1-9b23-00025b00a5a5", + "0000eb07-d102-11e1-9b23-00025b00a5a5", + "0000eb05-d102-11e1-9b23-00025b00a5a5", + "df21fe2c-2515-4fdb-8886-f12c4d67927c", + "0000180f-0000-1000-8000-00805f9b34fb", + "0000180a-0000-1000-8000-00805f9b34fb"}; - boolean allServicesSupported = Arrays.stream(uuidPrefixes) - .map(uuidPrefix -> UUID.fromString(uuidPrefix + UUID_SUFFIX)) + boolean allServicesSupported = Arrays.stream(uuids) + .map(UUID::fromString) .map(candidate::supportsService).allMatch(b -> b); boolean macAddressMatches = candidate.getMacAddress().toUpperCase().startsWith("70:5A:6F"); @@ -63,9 +80,14 @@ public class EarFunAirPro4Coordinator extends AbstractEarFunCoordinator { public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_headphones); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_10_band_equalizer); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_gestures); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name); - deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_10_band_equalizer); return deviceSpecificSettings; } + + @Override + public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { + return new EarFunAirPro4SettingsCustomizer(device); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirSCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirSCoordinator.java index 5eb853604..271487315 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirSCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/earfun/EarFunAirSCoordinator.java @@ -10,11 +10,13 @@ import java.util.UUID; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs.EarFunAirSDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs.EarFunAirSSettingsCustomizer; public class EarFunAirSCoordinator extends AbstractEarFunCoordinator { @@ -34,12 +36,19 @@ public class EarFunAirSCoordinator extends AbstractEarFunCoordinator { // can't only check with name, because the device name can be changed // via the device settings, so we use some of the UUIDs available on the device - // an the mac address prefix to hopefully detect this model reliable - String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb"; - String[] uuidPrefixes = {"00001101", "0000111e", "0000110b", "0000110e", "00000000"}; + // and the mac address prefix to hopefully detect this model reliably + String[] uuids = { + "00001101-0000-1000-8000-00805f9b34fb", + "0000111e-0000-1000-8000-00805f9b34fb", + "0000110b-0000-1000-8000-00805f9b34fb", + "0000110e-0000-1000-8000-00805f9b34fb", + "0000eb04-d102-11e1-9b23-00025b00a5a5", + "0000eb06-d102-11e1-9b23-00025b00a5a5", + "0000eb07-d102-11e1-9b23-00025b00a5a5", + "0000eb05-d102-11e1-9b23-00025b00a5a5"}; - boolean allServicesSupported = Arrays.stream(uuidPrefixes) - .map(uuidPrefix -> UUID.fromString(uuidPrefix + UUID_SUFFIX)) + boolean allServicesSupported = Arrays.stream(uuids) + .map(UUID::fromString) .map(candidate::supportsService).allMatch(b -> b); boolean macAddressMatches = candidate.getMacAddress().toUpperCase().startsWith("A8:99:DC"); @@ -69,9 +78,14 @@ public class EarFunAirSCoordinator extends AbstractEarFunCoordinator { public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_headphones); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_6_band_equalizer); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_gestures); deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name); - deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_6_band_equalizer); return deviceSpecificSettings; } + + @Override + public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { + return new EarFunAirSSettingsCustomizer(device); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacket.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacket.java index 0dda5680f..c307f0d80 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacket.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacket.java @@ -98,7 +98,7 @@ public class EarFunPacket { UNIDENTIFIED_0350((short) 0x0350), // 0001 Pro 4 SET_EQUALIZER_BAND((short) 0x0E01, OTHER_VENDOR_ID), // answers with UNIDENTIFIED_0F81 UNIDENTIFIED_0E80((short) 0x0E80, OTHER_VENDOR_ID), // 01 - UNIDENTIFIED_0F81((short) 0x0F81, OTHER_VENDOR_ID), // After setting EQ Band 01 + RESPONSE_EQUALIZER_BAND((short) 0x0F81, OTHER_VENDOR_ID), UNIDENTIFIED_1080((short) 0x1080, OTHER_VENDOR_ID), // 0100 -> 0101 if ANC not off UNIDENTIFIED_1081((short) 0x1081, OTHER_VENDOR_ID), // 01010000 <- otherwise, 0A010000 <- ANC Transparent UNIDENTIFIED_1082((short) 0x1082, OTHER_VENDOR_ID), // 01010000 -> 01014646 diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacketEncoder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacketEncoder.java index b243b8c5b..cf9ebe013 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacketEncoder.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunPacketEncoder.java @@ -16,6 +16,10 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class EarFunPacketEncoder { private static final Logger LOG = LoggerFactory.getLogger(EarFunPacketEncoder.class); + // the factor to converting equalizer gain between preference value and + // payload byte (int) value + // Gaia uses a factor of 60 to convert to dB and EarFun projects 6 dBs on a slider scale of 10 + private static final double EQUALIZER_GAIN_FACTOR = 60 * 0.6; public static byte[] encodeBatteryReq() { return joinPackets( @@ -76,7 +80,7 @@ public class EarFunPacketEncoder { } public static byte[] encodeSetEqualizerBand(double gainValue, Equalizer.Band band) { - short gain = (short) ((int) Math.round(gainValue * 60) & 0xFFFF); + short gain = (short) ((int) Math.round(gainValue * EQUALIZER_GAIN_FACTOR) & 0xFFFF); ByteBuffer buf = ByteBuffer.allocate(9); buf.put(band.bandId); buf.put((byte) 0xFF); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunProtocol.java index 11df7b188..d70b6346c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunProtocol.java @@ -63,6 +63,9 @@ public class EarFunProtocol extends GBDeviceProtocol { case REQUEST_RESPONSE_TOUCH_ACTION: events.add(handleTouchActionInfo(payload)); break; + // do nothing with these, they are returned after each EQ set operation and always return 01 + case RESPONSE_EQUALIZER_BAND: + break; default: LOG.error("no handler for packet type {}", packet.getCommand().name()); } @@ -92,11 +95,6 @@ public class EarFunProtocol extends GBDeviceProtocol { @Override public byte[] encodeSendConfiguration(String config) { - byte[] customConfiguration = encodeSendConfigurationCustomizer(config); - if (customConfiguration != null) { - return customConfiguration; - } - Prefs prefs = getDevicePrefs(); switch (config) { case PREF_EARFUN_AMBIENT_SOUND_CONTROL: @@ -137,15 +135,6 @@ public class EarFunProtocol extends GBDeviceProtocol { return null; } - /** - * Overwrite this in derived classes to do some device specific custom handling of configurations - * - * @return the config, encoded as byte array - */ - public byte[] encodeSendConfigurationCustomizer(String config) { - return null; - } - public byte[] encodeBatteryReq() { return EarFunPacketEncoder.encodeBatteryReq(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunSettingsCustomizer.java index d70772cc0..ea5532deb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/EarFunSettingsCustomizer.java @@ -3,24 +3,31 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun; import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*; +import android.content.Context; import android.os.Parcel; import android.text.InputFilter; -import android.text.Spanned; import androidx.annotation.NonNull; import androidx.preference.EditTextPreference; import androidx.preference.ListPreference; import androidx.preference.Preference; +import androidx.preference.SeekBarPreference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomizer { @@ -47,29 +54,6 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize @Override public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) { - if (preference.getKey().equals(PREF_EARFUN_AMBIENT_SOUND_CONTROL)) { - ListPreference listPreferenceAmbientSound = handler.findPreference(PREF_EARFUN_AMBIENT_SOUND_CONTROL); - ListPreference listPreferenceTransparencyMode = handler.findPreference(PREF_EARFUN_TRANSPARENCY_MODE); - ListPreference listPreferenceAncMode = handler.findPreference(PREF_EARFUN_ANC_MODE); - - if (listPreferenceAmbientSound == null || listPreferenceTransparencyMode == null || listPreferenceAncMode == null) { - return; - } - - switch (listPreferenceAmbientSound.getValue()) { - case "1": // noise cancelling - listPreferenceTransparencyMode.setVisible(false); - listPreferenceAncMode.setVisible(true); - break; - case "2": // transparency - listPreferenceTransparencyMode.setVisible(true); - listPreferenceAncMode.setVisible(false); - break; - default: - listPreferenceTransparencyMode.setVisible(false); - listPreferenceAncMode.setVisible(false); - } - } } @Override @@ -103,13 +87,90 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize EditTextPreference editTextDeviceName = handler.findPreference(PREF_EARFUN_DEVICE_NAME); if (editTextDeviceName != null) { editTextDeviceName.setOnBindEditTextListener(editText -> { - InputFilter[] filters = new InputFilter[]{new InputFilterLength(25)}; - editText.setFilters(filters); + editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(25)}); }); editTextDeviceName.setText(device.getName()); } } + protected void initializeEqualizerPresetListPreference(DeviceSpecificSettingsHandler handler, + Equalizer.EqualizerPreset[] equalizerPresets) { + ListPreference equalizerPresetListPreference = handler.findPreference(PREF_EARFUN_EQUALIZER_PRESET); + if (equalizerPresetListPreference != null) { + List entries = Arrays.stream(equalizerPresets) + .map(preset -> localizedPresetName(preset, handler.getContext())).collect(Collectors.toList()); + // add an additional element for user set custom band adjustments + entries.add(handler.getContext().getString(R.string.redmi_buds_5_pro_equalizer_preset_custom)); + + CharSequence[] entryValues = IntStream.rangeClosed(0, equalizerPresets.length) + .mapToObj(Integer::toString).toArray(String[]::new); + + equalizerPresetListPreference.setEntries(entries.toArray(new CharSequence[0])); + equalizerPresetListPreference.setEntryValues(entryValues); + } + } + + private String localizedPresetName(Equalizer.EqualizerPreset preset, Context context) { + if (preset.getLocalizedPresetName() != -1) { + return context.getString(preset.getLocalizedPresetName()); + } else { + return preset.getPresetName(); + } + } + + protected static void onPreferenceChangeEqualizerPreset(DeviceSpecificSettingsHandler handler, + Equalizer.BandConfig[] equalizerBands, + Equalizer.EqualizerPreset[] equalizerPresets) { + ListPreference listPreferenceEqualizerPreset = handler.findPreference(PREF_EARFUN_EQUALIZER_PRESET); + if (listPreferenceEqualizerPreset == null) { + return; + } + try { + int selectedOption = Integer.parseInt(listPreferenceEqualizerPreset.getValue()); + if (selectedOption >= equalizerPresets.length || selectedOption < 0) { + return; + } + Equalizer.EqualizerPreset preset = equalizerPresets[selectedOption]; + + IntStream.range(0, preset.getSettings().length).forEach(index -> { + String key = equalizerBands[index].getKey(); + if (key == null) { + return; + } + SeekBarPreference seekBarPreferenceEqualizerBand = handler.findPreference(key); + if (seekBarPreferenceEqualizerBand == null) { + return; + } + int gain = (int) Math.round(preset.getSettings()[index]); + seekBarPreferenceEqualizerBand.setValue(gain); + // call the change listener after setting last band to send new values to the device + if (index == preset.getSettings().length - 1) { + seekBarPreferenceEqualizerBand.callChangeListener(gain); + } + }); + } catch (NumberFormatException ignored) { + } + } + + protected static int getSelectedPresetFromEqualizerBands(DeviceSpecificSettingsHandler handler, + Equalizer.BandConfig[] equalizerBands, + Equalizer.EqualizerPreset[] equalizerPresets) { + double[] equalizerConfig = Arrays.stream(equalizerBands) + .filter(bandConfig -> bandConfig.getKey() != null) + .map(bandConfig -> { + SeekBarPreference bandSeekBarPreference = handler.findPreference(bandConfig.getKey()); + return bandSeekBarPreference.getValue(); + }) + .mapToDouble(Integer::doubleValue) + .toArray(); + + return IntStream.range(0, equalizerPresets.length) + .filter(i -> Arrays.equals(equalizerPresets[i].getSettings(), equalizerConfig)) + .findFirst() + // if filter settings do not match a preset, select the "custom" preset + .orElse(equalizerPresets.length); + } + @Override public Set getPreferenceKeysWithSummary() { return Collections.emptySet(); @@ -123,24 +184,4 @@ public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomize @Override public void writeToParcel(@NonNull Parcel parcel, int i) { } - - private static class InputFilterLength implements InputFilter { - private final int maxLength; - - public InputFilterLength(int maxLength) { - this.maxLength = maxLength; - } - - @Override - public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - int keep = maxLength - (dest.length() - (dend - dstart)); - if (keep <= 0) { - return ""; - } else if (keep >= end - start) { - return null; - } else { - return source.subSequence(start, start + keep); - } - } - } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4Protocol.java index d1ac13a4f..b6ed699f0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4Protocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4Protocol.java @@ -3,8 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; - import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder; @@ -16,18 +14,13 @@ public class EarFunAirPro4Protocol extends EarFunProtocol { private static final Logger LOG = LoggerFactory.getLogger(EarFunAirPro4Protocol.class); - public byte[] encodeSendConfigurationCustomizer(String config) { - - if (containsKey(Equalizer.TenBandEqualizer, config)) { + @Override + public byte[] encodeSendConfiguration(String config) { + if (Equalizer.containsKey(Equalizer.TenBandEqualizer, config)) { Prefs prefs = getDevicePrefs(); return EarFunPacketEncoder.encodeSetEqualizerTenBands(prefs); } - return null; - } - - private static boolean containsKey(Equalizer.BandConfig[] array, String key) { - return Arrays.stream(array) - .anyMatch(element -> element.key.equals(key)); + return super.encodeSendConfiguration(config); } protected EarFunAirPro4Protocol(GBDevice device) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4SettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4SettingsCustomizer.java new file mode 100644 index 000000000..a52f7b9a5 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airpro4/EarFunAirPro4SettingsCustomizer.java @@ -0,0 +1,79 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4; + +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_AMBIENT_SOUND_CONTROL; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_ANC_MODE; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_EQUALIZER_PRESET; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TRANSPARENCY_MODE; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer.TenBandEqualizerPresets; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunSettingsCustomizer; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +public class EarFunAirPro4SettingsCustomizer extends EarFunSettingsCustomizer { + + @Override + public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) { + super.onPreferenceChange(preference, handler); + String key = preference.getKey(); + if (key == null) { + return; + } + switch (key) { + case PREF_EARFUN_AMBIENT_SOUND_CONTROL: + onPreferenceChangeAmbientSoundControl(handler); + break; + case PREF_EARFUN_EQUALIZER_PRESET: + onPreferenceChangeEqualizerPreset(handler, Equalizer.TenBandEqualizer, TenBandEqualizerPresets); + break; + } + // if the band sliders match a preset, update the preset list + if (Equalizer.containsKey(Equalizer.TenBandEqualizer, key)) { + int equalizerPreset = getSelectedPresetFromEqualizerBands(handler, + Equalizer.TenBandEqualizer, TenBandEqualizerPresets); + ListPreference listPreferenceEqualizerPreset = handler.findPreference(PREF_EARFUN_EQUALIZER_PRESET); + if (listPreferenceEqualizerPreset != null) { + listPreferenceEqualizerPreset.setValue(Integer.toString(equalizerPreset)); + } + } + } + + @Override + public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs, String rootKey) { + super.customizeSettings(handler, prefs, rootKey); + initializeEqualizerPresetListPreference(handler, TenBandEqualizerPresets); + } + + private void onPreferenceChangeAmbientSoundControl(DeviceSpecificSettingsHandler handler) { + ListPreference listPreferenceAmbientSound = handler.findPreference(PREF_EARFUN_AMBIENT_SOUND_CONTROL); + ListPreference listPreferenceTransparencyMode = handler.findPreference(PREF_EARFUN_TRANSPARENCY_MODE); + ListPreference listPreferenceAncMode = handler.findPreference(PREF_EARFUN_ANC_MODE); + + if (listPreferenceAmbientSound == null || listPreferenceTransparencyMode == null || listPreferenceAncMode == null) { + return; + } + + switch (listPreferenceAmbientSound.getValue()) { + case "1": // noise cancelling + listPreferenceTransparencyMode.setVisible(false); + listPreferenceAncMode.setVisible(true); + break; + case "2": // transparency + listPreferenceTransparencyMode.setVisible(true); + listPreferenceAncMode.setVisible(false); + break; + default: + listPreferenceTransparencyMode.setVisible(false); + listPreferenceAncMode.setVisible(false); + } + } + + public EarFunAirPro4SettingsCustomizer(final GBDevice device) { + super(device); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSProtocol.java index 7c677fbbb..96ef244f1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSProtocol.java @@ -3,8 +3,6 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Arrays; - import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder; @@ -15,18 +13,13 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public class EarFunAirSProtocol extends EarFunProtocol { private static final Logger LOG = LoggerFactory.getLogger(EarFunAirSProtocol.class); - public byte[] encodeSendConfigurationCustomizer(String config) { - - if (containsKey(Equalizer.SixBandEqualizer, config)) { + @Override + public byte[] encodeSendConfiguration(String config) { + if (Equalizer.containsKey(Equalizer.SixBandEqualizer, config)) { Prefs prefs = getDevicePrefs(); return EarFunPacketEncoder.encodeSetEqualizerSixBands(prefs); } - return null; - } - - private static boolean containsKey(Equalizer.BandConfig[] array, String key) { - return Arrays.stream(array) - .anyMatch(element -> element.key.equals(key)); + return super.encodeSendConfiguration(config); } protected EarFunAirSProtocol(GBDevice device) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSSettingsCustomizer.java new file mode 100644 index 000000000..952f890ea --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/airs/EarFunAirSSettingsCustomizer.java @@ -0,0 +1,54 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs; + +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_EQUALIZER_PRESET; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer.SixBandEqualizerPresets; + +import androidx.preference.ListPreference; +import androidx.preference.Preference; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunSettingsCustomizer; +import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +public class EarFunAirSSettingsCustomizer extends EarFunSettingsCustomizer { + private static final Logger LOG = LoggerFactory.getLogger(EarFunAirSSettingsCustomizer.class); + + @Override + public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) { + super.onPreferenceChange(preference, handler); + String key = preference.getKey(); + if (key == null) { + return; + } + switch (key) { + case PREF_EARFUN_EQUALIZER_PRESET: + onPreferenceChangeEqualizerPreset(handler, + Equalizer.SixBandEqualizer, SixBandEqualizerPresets); + break; + } + // if the band sliders match a preset, update the preset list + if (Equalizer.containsKey(Equalizer.SixBandEqualizer, key)) { + int equalizerPreset = getSelectedPresetFromEqualizerBands(handler, + Equalizer.SixBandEqualizer, SixBandEqualizerPresets); + ListPreference listPreferenceEqualizerPreset = handler.findPreference(PREF_EARFUN_EQUALIZER_PRESET); + if (listPreferenceEqualizerPreset != null) { + listPreferenceEqualizerPreset.setValue(Integer.toString(equalizerPreset)); + } + } + } + + @Override + public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs, String rootKey) { + super.customizeSettings(handler, prefs, rootKey); + initializeEqualizerPresetListPreference(handler, SixBandEqualizerPresets); + } + + public EarFunAirSSettingsCustomizer(final GBDevice device) { + super(device); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/EarFunSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/EarFunSettingsPreferenceConst.java index 2dc1a7e8a..497aabac9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/EarFunSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/EarFunSettingsPreferenceConst.java @@ -26,4 +26,5 @@ public class EarFunSettingsPreferenceConst { public static final String PREF_EARFUN_EQUALIZER_BAND_8000 = "pref_earfun_equalizer_band_8000"; public static final String PREF_EARFUN_EQUALIZER_BAND_15000 = "pref_earfun_equalizer_band_15000"; public static final String PREF_EARFUN_EQUALIZER_BAND_16000 = "pref_earfun_equalizer_band_16000"; + public static final String PREF_EARFUN_EQUALIZER_PRESET = "pref_earfun_equalizer_preset"; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/Equalizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/Equalizer.java index b0306a375..1f1bfc4cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/Equalizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/earfun/prefs/Equalizer.java @@ -2,6 +2,11 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs; import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*; +import java.util.Arrays; +import java.util.Objects; + +import nodomain.freeyourgadget.gadgetbridge.R; + public class Equalizer { public enum Band { SIX_BAND_63((byte) 0xA1, (short) 0x00BD, (short) 0x0B33), @@ -42,6 +47,22 @@ public class Equalizer { this.qFactor = qFactor; this.defaultGain = defaultGain; } + + public byte getBandId() { + return bandId; + } + + public short getFrequency() { + return frequency; + } + + public short getqFactor() { + return qFactor; + } + + public double getDefaultGain() { + return defaultGain; + } } public static class BandConfig { @@ -50,6 +71,14 @@ public class Equalizer { this.key = key; } + public Band getBand() { + return band; + } + + public String getKey() { + return key; + } + public Band band; public String key; } @@ -79,4 +108,150 @@ public class Equalizer { new BandConfig(Band.TEN_BAND_8000, PREF_EARFUN_EQUALIZER_BAND_8000), new BandConfig(Band.TEN_BAND_16000, PREF_EARFUN_EQUALIZER_BAND_16000), }; + + public interface EqualizerPreset { + String getPresetName(); + + int getLocalizedPresetName(); + + double[] getSettings(); + + default String getFormattedSettings() { + return Arrays.toString(getSettings()); + } + + static void printAllPresets(EqualizerPreset[] presets) { + for (EqualizerPreset preset : presets) { + System.out.println(preset.getPresetName() + ": " + preset.getFormattedSettings()); + } + } + } + + public enum SixBandPreset implements EqualizerPreset { + // Default: Keeps all bands at their default values + DEFAULT(R.string.pref_title_equalizer_normal, new double[]{0, 0, 0, 0, 0, 0}), + // Natural: Balanced and natural sound profile that reproduces audio without any coloration + NATURAL(R.string.pref_title_equalizer_natural, new double[]{0, 0, 1, 1, 2, 3}), + // Bass Boost: Emphasizes the low frequencies for a deep, powerful bass + BASS_BOOST(R.string.pref_title_equalizer_bass_boost, new double[]{8, 3, 2, 0, 0, 0}), + // Treble Boost: Enhances the high frequencies for a crisp and bright sound + TREBLE_BOOST(R.string.pref_title_equalizer_trebble, new double[]{0, 0, 0, 2, 3, 5}), + // Soft: Creates a gentle, smooth, and mellow sound + SOFT(R.string.pref_title_equalizer_soft, new double[]{-5, -2, +2, +3, 0, -3}), + // Dynamic: Produces a lively and energetic sound with well-defined bass and crisp highs + DYNAMIC(R.string.pref_title_equalizer_dynamic, new double[]{+7, +3, +2, +3, +5, +7}), + // Clear: Achieves a balanced and transparent sound, ideal for detailed audio work + CLEAR(R.string.pref_title_equalizer_clear, new double[]{+2, 0, +3, +5, +3, +5}), + // Relaxed: Produces a calming and soothing sound, perfect for unwinding + RELAXED(R.string.sony_equalizer_preset_relaxed, new double[]{+2, +1, 0, -1, -3, -5}), + // Vocal: Enhances the mid-range frequencies for clear and prominent vocals + VOCAL(R.string.sony_equalizer_preset_vocal, new double[]{-2, 0, +4, +5, +2, -1}); + + public final String presetName; + public final int localizedPresetName; + public final double[] settings; + + SixBandPreset(String name, double[] settings) { + this.presetName = name; + this.localizedPresetName = -1; + this.settings = settings; + } + + SixBandPreset(int localizedName, double[] settings) { + this.presetName = ""; + this.localizedPresetName = localizedName; + this.settings = settings; + } + + public String getPresetName() { + return presetName; + } + + public int getLocalizedPresetName() { + return localizedPresetName; + } + + public double[] getSettings() { + return settings; + } + } + + public enum TenBandPreset implements EqualizerPreset { + // Default: Keeps all bands at their default values + DEFAULT(R.string.pref_title_equalizer_normal, new double[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}), + // Natural: Balanced and natural sound profile that reproduces audio without any coloration + NATURAL(R.string.pref_title_equalizer_natural, new double[]{0, 0, 1, 2, 2, -1, -1, -1, -2, 1}), + // Bass Boost: Emphasizes the low frequencies for a deep, powerful bass + BASS_BOOST(R.string.pref_title_equalizer_bass_boost, new double[]{+8, +6, +4, +2, 0, 0, 0, 0, 0, 0}), + // Treble Boost: Enhances the high frequencies for a crisp and bright sound + TREBLE_BOOST(R.string.pref_title_equalizer_trebble, new double[]{0, 0, 0, 0, 0, 0, +2, +2, +3, +4}), + // Soft: Creates a gentle, smooth, and mellow sound + SOFT(R.string.pref_title_equalizer_soft, new double[]{-5, -4, -2, 0, +2, +3, 0, -2, -3, -5}), + // Dynamic: Produces a lively and energetic sound with well-defined bass and crisp highs + DYNAMIC(R.string.pref_title_equalizer_dynamic, new double[]{+6, +6, +4, +2, +2, +3, +4, +5, +6, +7}), + // Clear: Achieves a balanced and transparent sound, ideal for detailed audio work + CLEAR(R.string.pref_title_equalizer_clear, new double[]{+3, +2, +2, +2, +3, +5, +3, +3, +4, +5}), + // Relaxed: Produces a calming and soothing sound, perfect for unwinding + RELAXED(R.string.sony_equalizer_preset_relaxed, new double[]{+2, +1, 0, 0, 0, -1, -2, -3, -4, -5}), + // Vocal: Enhances the mid-range frequencies for clear and prominent vocals + VOCAL(R.string.sony_equalizer_preset_vocal, new double[]{-3, -2, 0, +2, +3, +5, +3, +2, 0, -1}); + + public final String presetName; + public final int localizedPresetName; + public final double[] settings; + + + TenBandPreset(String name, double[] settings) { + this.presetName = name; + this.localizedPresetName = -1; + this.settings = settings; + } + + TenBandPreset(int localizedName, double[] settings) { + this.presetName = ""; + this.localizedPresetName = localizedName; + this.settings = settings; + } + + public String getPresetName() { + return presetName; + } + + public int getLocalizedPresetName() { + return localizedPresetName; + } + + public double[] getSettings() { + return settings; + } + } + + public static boolean containsKey(Equalizer.BandConfig[] array, String key) { + return Arrays.stream(array) + .anyMatch(element -> Objects.equals(element.key, key)); + } + + public static EqualizerPreset[] SixBandEqualizerPresets = { + SixBandPreset.DEFAULT, + SixBandPreset.NATURAL, + SixBandPreset.BASS_BOOST, + SixBandPreset.TREBLE_BOOST, + SixBandPreset.SOFT, + SixBandPreset.DYNAMIC, + SixBandPreset.CLEAR, + SixBandPreset.RELAXED, + SixBandPreset.VOCAL + }; + + public static EqualizerPreset[] TenBandEqualizerPresets = { + TenBandPreset.DEFAULT, + TenBandPreset.NATURAL, + TenBandPreset.BASS_BOOST, + TenBandPreset.TREBLE_BOOST, + TenBandPreset.SOFT, + TenBandPreset.DYNAMIC, + TenBandPreset.CLEAR, + TenBandPreset.RELAXED, + TenBandPreset.VOCAL + }; } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 1dc3fbf7d..aea26a1c4 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -3548,7 +3548,7 @@ @string/pref_default - @string/earfun_transparency_mode_natural + @string/pref_title_equalizer_natural diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4efac792..75b430337 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2638,6 +2638,7 @@ Dynamic Clear Treble boost + Natural Dolby Mode Equalizer Enable or disable equalizer @@ -3706,7 +3707,6 @@ Pairing failed: %s Device name: %s\nMac address: %s\nBind key: %s Transparency mode - Natural ANC mode Strong ANC Balanced ANC diff --git a/app/src/main/res/xml/devicesettings_earfun_10_band_equalizer.xml b/app/src/main/res/xml/devicesettings_earfun_10_band_equalizer.xml index f3b1f88ef..c0fe4806e 100644 --- a/app/src/main/res/xml/devicesettings_earfun_10_band_equalizer.xml +++ b/app/src/main/res/xml/devicesettings_earfun_10_band_equalizer.xml @@ -7,87 +7,91 @@ android:key="pref_earfun_10_band_equalizer" android:persistent="false" android:title="@string/pref_header_equalizer"> - + + android:max="10" + android:title="31.5 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="63 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="125 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="250 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="500 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="1 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="2 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="4 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="8 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="16 kHz" + app:min="-10" + app:showSeekBarValue="true" /> diff --git a/app/src/main/res/xml/devicesettings_earfun_6_band_equalizer.xml b/app/src/main/res/xml/devicesettings_earfun_6_band_equalizer.xml index 329d76a56..52106be91 100644 --- a/app/src/main/res/xml/devicesettings_earfun_6_band_equalizer.xml +++ b/app/src/main/res/xml/devicesettings_earfun_6_band_equalizer.xml @@ -7,55 +7,59 @@ android:key="pref_earfun_6_band_equalizer" android:persistent="false" android:title="@string/pref_header_equalizer"> - + + android:max="10" + android:title="63 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="180 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="500 Hz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="1 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="8 kHz" + app:min="-10" + app:showSeekBarValue="true" /> + android:max="10" + android:title="15 kHz" + app:min="-10" + app:showSeekBarValue="true" /> diff --git a/app/src/main/res/xml/devicesettings_earfun_device_name.xml b/app/src/main/res/xml/devicesettings_earfun_device_name.xml index 8ca4c5ce0..08eaee17c 100644 --- a/app/src/main/res/xml/devicesettings_earfun_device_name.xml +++ b/app/src/main/res/xml/devicesettings_earfun_device_name.xml @@ -3,8 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto">