mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
CMF Buds 2a: Add equalizer and ultra bass
This commit is contained in:
+3
@@ -374,6 +374,9 @@ public class DeviceSettingsPreferenceConst {
|
|||||||
|
|
||||||
public static final String PREF_NOTHING_EAR1_INEAR = "pref_nothing_inear_detection";
|
public static final String PREF_NOTHING_EAR1_INEAR = "pref_nothing_inear_detection";
|
||||||
public static final String PREF_NOTHING_EAR1_AUDIOMODE = "pref_nothing_audiomode";
|
public static final String PREF_NOTHING_EAR1_AUDIOMODE = "pref_nothing_audiomode";
|
||||||
|
public static final String PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED = "pref_nothing_ultra_bass_enabled";
|
||||||
|
public static final String PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL = "pref_nothing_ultra_bass_level";
|
||||||
|
public static final String PREF_HEADPHONES_EQUALIZER = "pref_headphones_equalizer";
|
||||||
public static final String PREF_HEADPHONES_LOW_LATENCY = "pref_headphones_low_latency";
|
public static final String PREF_HEADPHONES_LOW_LATENCY = "pref_headphones_low_latency";
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+17
@@ -18,6 +18,9 @@ package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||||
@@ -66,6 +69,12 @@ public abstract class AbstractEarCoordinator extends AbstractBLClassicDeviceCoor
|
|||||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_nothing_ear1);
|
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_nothing_ear1);
|
||||||
|
if (!getEqualizerPresets().isEmpty()) {
|
||||||
|
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_nothing_equalizer);
|
||||||
|
}
|
||||||
|
if (supportsUltraBass()) {
|
||||||
|
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_nothing_ultra_bass);
|
||||||
|
}
|
||||||
if (supportsLowLatency()) {
|
if (supportsLowLatency()) {
|
||||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_headphones_low_latency);
|
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_headphones_low_latency);
|
||||||
}
|
}
|
||||||
@@ -100,4 +109,12 @@ public abstract class AbstractEarCoordinator extends AbstractBLClassicDeviceCoor
|
|||||||
public boolean supportsLowLatency() {
|
public boolean supportsLowLatency() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<NothingEqualizer> getEqualizerPresets() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsUltraBass() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -16,6 +16,9 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||||
package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
|
package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
@@ -70,4 +73,21 @@ public class CmfBuds2aCoordinator extends AbstractEarCoordinator {
|
|||||||
public boolean supportsLowLatency() {
|
public boolean supportsLowLatency() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<NothingEqualizer> getEqualizerPresets() {
|
||||||
|
return Arrays.asList(
|
||||||
|
NothingEqualizer.POP,
|
||||||
|
NothingEqualizer.ROCK,
|
||||||
|
NothingEqualizer.ELECTRONIC,
|
||||||
|
NothingEqualizer.ENHANCE_VOCALS,
|
||||||
|
NothingEqualizer.CLASSICAL,
|
||||||
|
NothingEqualizer.DIRAC
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsUltraBass() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-2
@@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpec
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||||
|
|
||||||
public class EarSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
public class EarSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||||
public static final Creator<EarSettingsCustomizer> CREATOR = new Creator<EarSettingsCustomizer>() {
|
public static final Creator<EarSettingsCustomizer> CREATOR = new Creator<>() {
|
||||||
@Override
|
@Override
|
||||||
public EarSettingsCustomizer createFromParcel(final Parcel in) {
|
public EarSettingsCustomizer createFromParcel(final Parcel in) {
|
||||||
return new EarSettingsCustomizer();
|
return new EarSettingsCustomizer();
|
||||||
@@ -88,6 +89,33 @@ public class EarSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
|||||||
((ListPreference) audioModePref).setEntries(entries.toArray(new CharSequence[0]));
|
((ListPreference) audioModePref).setEntries(entries.toArray(new CharSequence[0]));
|
||||||
((ListPreference) audioModePref).setEntryValues(entryValues.toArray(new CharSequence[0]));
|
((ListPreference) audioModePref).setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Preference equalizerPref = handler.findPreference(DeviceSettingsPreferenceConst.PREF_HEADPHONES_EQUALIZER);
|
||||||
|
if (equalizerPref instanceof ListPreference listPreference) {
|
||||||
|
final List<NothingEqualizer> supportedPresets = earCoordinator.getEqualizerPresets();
|
||||||
|
if (supportedPresets.isEmpty()) {
|
||||||
|
listPreference.setVisible(false);
|
||||||
|
} else {
|
||||||
|
final CharSequence[] originalEntries = listPreference.getEntries();
|
||||||
|
final CharSequence[] originalEntryValues = listPreference.getEntryValues();
|
||||||
|
|
||||||
|
final List<CharSequence> entries = new ArrayList<>();
|
||||||
|
final List<CharSequence> entryValues = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < originalEntryValues.length; i++) {
|
||||||
|
final NothingEqualizer preset = NothingEqualizer.fromPreferenceValue(originalEntryValues[i].toString());
|
||||||
|
if (preset == null || !supportedPresets.contains(preset)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.add(originalEntries[i]);
|
||||||
|
entryValues.add(originalEntryValues[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
listPreference.setEntries(entries.toArray(new CharSequence[0]));
|
||||||
|
listPreference.setEntryValues(entryValues.toArray(new CharSequence[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,6 +129,6 @@ public class EarSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(final Parcel dest, final int flags) {
|
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
|
||||||
|
|
||||||
|
public enum NothingEqualizer {
|
||||||
|
ROCK((byte) 0x01),
|
||||||
|
ELECTRONIC((byte) 0x02),
|
||||||
|
POP((byte) 0x03),
|
||||||
|
ENHANCE_VOCALS((byte) 0x04),
|
||||||
|
CLASSICAL((byte) 0x05),
|
||||||
|
DIRAC((byte) 0x07),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final byte code;
|
||||||
|
|
||||||
|
NothingEqualizer(final byte code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getCode() {
|
||||||
|
return this.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NothingEqualizer fromCode(final byte code) {
|
||||||
|
for (NothingEqualizer value : NothingEqualizer.values()) {
|
||||||
|
if (value.getCode() == code) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NothingEqualizer fromPreferenceValue(final String preferenceValue) {
|
||||||
|
if (preferenceValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return NothingEqualizer.valueOf(preferenceValue);
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+92
@@ -37,6 +37,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdateDevi
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.AbstractEarCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.AbstractEarCoordinator;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.NothingEqualizer;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractHeadphoneBTBRDeviceSupport;
|
import nodomain.freeyourgadget.gadgetbridge.service.AbstractHeadphoneBTBRDeviceSupport;
|
||||||
@@ -74,6 +75,12 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
sendCommand(builder, nothingProtocol.encodeFirmwareVersionReq());
|
sendCommand(builder, nothingProtocol.encodeFirmwareVersionReq());
|
||||||
sendCommand(builder, nothingProtocol.encodeBatteryStatusReq());
|
sendCommand(builder, nothingProtocol.encodeBatteryStatusReq());
|
||||||
sendCommand(builder, nothingProtocol.encodeAudioModeStatusReq());
|
sendCommand(builder, nothingProtocol.encodeAudioModeStatusReq());
|
||||||
|
if (!getCoordinator().getEqualizerPresets().isEmpty()) {
|
||||||
|
sendCommand(builder, nothingProtocol.encodeEqualizerStatusReq());
|
||||||
|
}
|
||||||
|
if (getCoordinator().supportsUltraBass()) {
|
||||||
|
sendCommand(builder, nothingProtocol.encodeUltraBassStatusReq());
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@@ -135,6 +142,19 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
// OFF: 55 6001 40F0 0100 5C 02 28 9353
|
// OFF: 55 6001 40F0 0100 5C 02 28 9353
|
||||||
// OFF Response: 55 6001 4070 0000 5C FF FF95
|
// OFF Response: 55 6001 4070 0000 5C FF FF95
|
||||||
break;
|
break;
|
||||||
|
case DeviceSettingsPreferenceConst.PREF_HEADPHONES_EQUALIZER:
|
||||||
|
sendCommand("set equalizer", nothingProtocol.encodeEqualizer(prefs.getString(DeviceSettingsPreferenceConst.PREF_HEADPHONES_EQUALIZER, NothingEqualizer.DIRAC.name())));
|
||||||
|
break;
|
||||||
|
case DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED:
|
||||||
|
case DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL:
|
||||||
|
sendCommand(
|
||||||
|
"set ultra bass",
|
||||||
|
nothingProtocol.encodeUltraBass(
|
||||||
|
prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED, false),
|
||||||
|
prefs.getInt(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL, 2)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.debug("CONFIG: " + config);
|
LOG.debug("CONFIG: " + config);
|
||||||
break;
|
break;
|
||||||
@@ -166,6 +186,8 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
private static final short audio_mode_status = (short) 0xc01e;
|
private static final short audio_mode_status = (short) 0xc01e;
|
||||||
private static final short audio_mode_status2 = (short) 0xe003;
|
private static final short audio_mode_status2 = (short) 0xe003;
|
||||||
private static final short low_latency = (short) 0xf040;
|
private static final short low_latency = (short) 0xf040;
|
||||||
|
private static final short equalizer_status = (short) 0xc050;
|
||||||
|
private static final short ultra_bass_status = (short) 0xc04e;
|
||||||
|
|
||||||
private static final short unk_maybe_ack = (short) 0xf002;
|
private static final short unk_maybe_ack = (short) 0xf002;
|
||||||
private static final short unk_close_case = (short) 0xe002; //sent twice when the case is closed with earphones in
|
private static final short unk_close_case = (short) 0xe002; //sent twice when the case is closed with earphones in
|
||||||
@@ -175,6 +197,8 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
private static final short in_ear_detection = (short) 0xf004;
|
private static final short in_ear_detection = (short) 0xf004;
|
||||||
private static final short in_ear_detection2 = (short) 0xc00e;
|
private static final short in_ear_detection2 = (short) 0xc00e;
|
||||||
private static final short audio_mode = (short) 0xf00f;
|
private static final short audio_mode = (short) 0xf00f;
|
||||||
|
private static final short equalizer = (short) 0xf01d;
|
||||||
|
private static final short ultra_bass = (short) 0xf051;
|
||||||
|
|
||||||
private final boolean incrementCounter;
|
private final boolean incrementCounter;
|
||||||
private int messageCounter = 0x00;
|
private int messageCounter = 0x00;
|
||||||
@@ -259,6 +283,20 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
devEvts.add(handleInEarStatus(payload));
|
devEvts.add(handleInEarStatus(payload));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case equalizer_status:
|
||||||
|
case equalizer:
|
||||||
|
if (payload.length > 0) {
|
||||||
|
devEvts.add(handleEqualizerStatus(payload));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ultra_bass_status:
|
||||||
|
case ultra_bass:
|
||||||
|
if (payload.length > 0) {
|
||||||
|
devEvts.add(handleUltraBassStatus(payload));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case unk_maybe_ack:
|
case unk_maybe_ack:
|
||||||
LOG.debug("received ack");
|
LOG.debug("received ack");
|
||||||
break;
|
break;
|
||||||
@@ -320,6 +358,14 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
return encodeMessage((short) 0x120, audio_mode_status, new byte[]{});
|
return encodeMessage((short) 0x120, audio_mode_status, new byte[]{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] encodeEqualizerStatusReq() {
|
||||||
|
return encodeMessage((short) 0x120, equalizer_status, new byte[]{});
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] encodeUltraBassStatusReq() {
|
||||||
|
return encodeMessage((short) 0x120, ultra_bass_status, new byte[]{});
|
||||||
|
}
|
||||||
|
|
||||||
private GBDeviceEventVersionInfo handleFirmwareVersion(byte[] payload) {
|
private GBDeviceEventVersionInfo handleFirmwareVersion(byte[] payload) {
|
||||||
GBDeviceEventVersionInfo evt = new GBDeviceEventVersionInfo();
|
GBDeviceEventVersionInfo evt = new GBDeviceEventVersionInfo();
|
||||||
evt.fwVersion = new String(payload);
|
evt.fwVersion = new String(payload);
|
||||||
@@ -351,6 +397,32 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
return preferencesEvent;
|
return preferencesEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private GBDeviceEventUpdatePreferences handleEqualizerStatus(final byte[] payload) {
|
||||||
|
final GBDeviceEventUpdatePreferences preferencesEvent = new GBDeviceEventUpdatePreferences();
|
||||||
|
|
||||||
|
final NothingEqualizer equalizerPreset = NothingEqualizer.fromCode(payload[0]);
|
||||||
|
if (equalizerPreset == null) {
|
||||||
|
LOG.warn("Unknown equalizer code: {}", payload[0] & 0xff);
|
||||||
|
return preferencesEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
preferencesEvent.withPreference(DeviceSettingsPreferenceConst.PREF_HEADPHONES_EQUALIZER, equalizerPreset.name());
|
||||||
|
return preferencesEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GBDeviceEventUpdatePreferences handleUltraBassStatus(final byte[] payload) {
|
||||||
|
final GBDeviceEventUpdatePreferences preferencesEvent = new GBDeviceEventUpdatePreferences();
|
||||||
|
|
||||||
|
preferencesEvent.withPreference(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED, payload[0] == 0x01);
|
||||||
|
|
||||||
|
if (payload.length >= 2) {
|
||||||
|
final int level = Math.max(1, Math.min(5, ((payload[1] & 0xff) + 1) / 2));
|
||||||
|
preferencesEvent.withPreference(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferencesEvent;
|
||||||
|
}
|
||||||
|
|
||||||
byte[] encodeInEarDetection(byte enabled) {
|
byte[] encodeInEarDetection(byte enabled) {
|
||||||
return encodeMessage((short) 0x120, in_ear_detection, new byte[]{0x01, 0x01, enabled});
|
return encodeMessage((short) 0x120, in_ear_detection, new byte[]{0x01, 0x01, enabled});
|
||||||
}
|
}
|
||||||
@@ -372,6 +444,26 @@ public class Ear1Support extends AbstractHeadphoneBTBRDeviceSupport {
|
|||||||
return encodeMessage((short) 0x120, low_latency, new byte[]{payload});
|
return encodeMessage((short) 0x120, low_latency, new byte[]{payload});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
byte[] encodeEqualizer(final String desired) {
|
||||||
|
NothingEqualizer preset = NothingEqualizer.DIRAC;
|
||||||
|
|
||||||
|
final NothingEqualizer requested = NothingEqualizer.fromPreferenceValue(desired);
|
||||||
|
if (requested != null) {
|
||||||
|
preset = requested;
|
||||||
|
} else {
|
||||||
|
LOG.warn("Unknown equalizer preset: {}, using default", desired);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeMessage((short) 0x120, equalizer, new byte[]{preset.getCode(), 0x00});
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] encodeUltraBass(final boolean enabled, final int level) {
|
||||||
|
final int clampedLevel = Math.max(1, Math.min(5, level));
|
||||||
|
final byte rawLevel = (byte) (clampedLevel * 2);
|
||||||
|
|
||||||
|
return encodeMessage((short) 0x120, ultra_bass, new byte[]{(byte) (enabled ? 0x01 : 0x00), rawLevel});
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] encodeFindDevice(boolean start) {
|
public byte[] encodeFindDevice(boolean start) {
|
||||||
byte payload = (byte) (start ? 0x01 : 0x00);
|
byte payload = (byte) (start ? 0x01 : 0x00);
|
||||||
return encodeMessage((short) 0x120, find_device, new byte[]{payload});
|
return encodeMessage((short) 0x120, find_device, new byte[]{payload});
|
||||||
|
|||||||
@@ -3564,6 +3564,24 @@
|
|||||||
<item>off</item>
|
<item>off</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="nothing_ear1_equalizer_names">
|
||||||
|
<item>@string/nothing_equalizer_rock</item>
|
||||||
|
<item>@string/nothing_equalizer_electronic</item>
|
||||||
|
<item>@string/nothing_equalizer_pop</item>
|
||||||
|
<item>@string/nothing_equalizer_enhance_vocals</item>
|
||||||
|
<item>@string/nothing_equalizer_classical</item>
|
||||||
|
<item>@string/nothing_equalizer_dirac</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="nothing_ear1_equalizer_values">
|
||||||
|
<item>ROCK</item>
|
||||||
|
<item>ELECTRONIC</item>
|
||||||
|
<item>POP</item>
|
||||||
|
<item>ENHANCE_VOCALS</item>
|
||||||
|
<item>CLASSICAL</item>
|
||||||
|
<item>DIRAC</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="notification_list_is_blacklist_names">
|
<string-array name="notification_list_is_blacklist_names">
|
||||||
<item>@string/pref_title_notification_use_as_deny</item>
|
<item>@string/pref_title_notification_use_as_deny</item>
|
||||||
<item>@string/pref_title_notification_use_as_allow</item>
|
<item>@string/pref_title_notification_use_as_allow</item>
|
||||||
|
|||||||
@@ -3177,6 +3177,15 @@
|
|||||||
<string name="nothing_prefs_inear_title">In-Ear detection</string>
|
<string name="nothing_prefs_inear_title">In-Ear detection</string>
|
||||||
<string name="prefs_in_ear_detection_summary">Play calls through your earbuds when they are in your ears</string>
|
<string name="prefs_in_ear_detection_summary">Play calls through your earbuds when they are in your ears</string>
|
||||||
<string name="nothing_prefs_audiomode_title">Audio mode</string>
|
<string name="nothing_prefs_audiomode_title">Audio mode</string>
|
||||||
|
<string name="nothing_prefs_ultra_bass_title">Ultra Bass</string>
|
||||||
|
<string name="nothing_prefs_ultra_bass_summary">Boost low frequencies for a stronger bass response</string>
|
||||||
|
<string name="nothing_prefs_ultra_bass_level_title">Ultra Bass Level</string>
|
||||||
|
<string name="nothing_equalizer_rock">Rock</string>
|
||||||
|
<string name="nothing_equalizer_electronic">Electronic</string>
|
||||||
|
<string name="nothing_equalizer_pop">Pop</string>
|
||||||
|
<string name="nothing_equalizer_enhance_vocals">Enhance vocals</string>
|
||||||
|
<string name="nothing_equalizer_classical">Classical</string>
|
||||||
|
<string name="nothing_equalizer_dirac">Dirac</string>
|
||||||
<string name="prefs_equalizer_preset">Equalizer Preset</string>
|
<string name="prefs_equalizer_preset">Equalizer Preset</string>
|
||||||
<string name="pref_title_equalizer_normal">Normal</string>
|
<string name="pref_title_equalizer_normal">Normal</string>
|
||||||
<string name="pref_title_equalizer_bass_boost">Bass boost</string>
|
<string name="pref_title_equalizer_bass_boost">Bass boost</string>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<ListPreference
|
||||||
|
android:icon="@drawable/ic_equalizer"
|
||||||
|
android:entryValues="@array/nothing_ear1_equalizer_values"
|
||||||
|
android:entries="@array/nothing_ear1_equalizer_names"
|
||||||
|
android:key="pref_headphones_equalizer"
|
||||||
|
app:useSimpleSummaryProvider="true"
|
||||||
|
android:title="@string/prefs_equalizer_preset" />
|
||||||
|
</androidx.preference.PreferenceScreen>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_speaker"
|
||||||
|
android:key="pref_nothing_ultra_bass"
|
||||||
|
android:layout="@layout/preference_checkbox"
|
||||||
|
android:summary="@string/nothing_prefs_ultra_bass_summary"
|
||||||
|
android:title="@string/nothing_prefs_ultra_bass_title" />
|
||||||
|
<SeekBarPreference
|
||||||
|
android:defaultValue="2"
|
||||||
|
android:dependency="pref_nothing_ultra_bass"
|
||||||
|
android:icon="@drawable/ic_speaker"
|
||||||
|
android:key="pref_nothing_ultra_bass_level"
|
||||||
|
android:max="5"
|
||||||
|
android:title="@string/nothing_prefs_ultra_bass_level_title"
|
||||||
|
app:min="1"
|
||||||
|
app:showSeekBarValue="true" />
|
||||||
|
</androidx.preference.PreferenceScreen>
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.service.devices.nothing;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.NothingEqualizer;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.test.TestBase;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||||
|
|
||||||
|
import static nodomain.freeyourgadget.gadgetbridge.util.CheckSums.getCRC16ansi;
|
||||||
|
|
||||||
|
public class NothingProtocolTest extends TestBase {
|
||||||
|
@Test
|
||||||
|
public void testDecodeEqualizerAndUltraBassStatus() {
|
||||||
|
final Ear1Support.NothingProtocol protocol = new Ear1Support.NothingProtocol(true);
|
||||||
|
|
||||||
|
final GBDeviceEvent[] equalizerEvents = protocol.decodeResponse(GB.hexStringToByteArray("5560015040010060057f43"));
|
||||||
|
final GBDeviceEventUpdatePreferences equalizerUpdate = findEvent(equalizerEvents, GBDeviceEventUpdatePreferences.class);
|
||||||
|
Assert.assertNotNull(equalizerUpdate);
|
||||||
|
Assert.assertEquals(NothingEqualizer.CLASSICAL.name(), equalizerUpdate.preferences.get(DeviceSettingsPreferenceConst.PREF_HEADPHONES_EQUALIZER));
|
||||||
|
|
||||||
|
final GBDeviceEvent[] ultraBassEvents = protocol.decodeResponse(GB.hexStringToByteArray("5560014e4002001b00028071"));
|
||||||
|
final GBDeviceEventUpdatePreferences ultraBassUpdate = findEvent(ultraBassEvents, GBDeviceEventUpdatePreferences.class);
|
||||||
|
Assert.assertNotNull(ultraBassUpdate);
|
||||||
|
Assert.assertEquals(false, ultraBassUpdate.preferences.get(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_ENABLED));
|
||||||
|
Assert.assertEquals(1, ultraBassUpdate.preferences.get(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_ULTRA_BASS_LEVEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeEqualizerCommand() {
|
||||||
|
final Ear1Support.NothingProtocol protocol = new Ear1Support.NothingProtocol(true);
|
||||||
|
|
||||||
|
final byte[] packet = protocol.encodeEqualizer(NothingEqualizer.ELECTRONIC.name());
|
||||||
|
final byte[] expected = GB.hexStringToByteArray("5560011df00200140200ebcd");
|
||||||
|
assertMessageEquals(expected, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeUltraBassCommand() {
|
||||||
|
final Ear1Support.NothingProtocol protocol = new Ear1Support.NothingProtocol(true);
|
||||||
|
|
||||||
|
final byte[] packet = protocol.encodeUltraBass(true, 2);
|
||||||
|
final byte[] expected = GB.hexStringToByteArray("55600151f0020070010426e5");
|
||||||
|
assertMessageEquals(expected, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertMessageEquals(final byte[] expected, final byte[] actual) {
|
||||||
|
Assert.assertNotNull(expected);
|
||||||
|
Assert.assertNotNull(actual);
|
||||||
|
|
||||||
|
Assert.assertTrue("message too short", expected.length >= 8 && actual.length >= 8);
|
||||||
|
Assert.assertEquals("sof differs", expected[0], actual[0]);
|
||||||
|
|
||||||
|
final int expectedControl = readLeShort(expected, 1);
|
||||||
|
final int actualControl = readLeShort(actual, 1);
|
||||||
|
Assert.assertEquals("control differs", expectedControl, actualControl);
|
||||||
|
|
||||||
|
final int expectedCommand = readLeShort(expected, 3);
|
||||||
|
final int actualCommand = readLeShort(actual, 3);
|
||||||
|
Assert.assertEquals("command differs", expectedCommand, actualCommand);
|
||||||
|
|
||||||
|
final int expectedPayloadLength = readLeShort(expected, 5);
|
||||||
|
final int actualPayloadLength = readLeShort(actual, 5);
|
||||||
|
Assert.assertEquals("payload length field differs", expectedPayloadLength, actualPayloadLength);
|
||||||
|
|
||||||
|
final boolean expectedCrc = (expectedControl & 0x20) != 0;
|
||||||
|
final boolean actualCrc = (actualControl & 0x20) != 0;
|
||||||
|
Assert.assertEquals("crc flag differs", expectedCrc, actualCrc);
|
||||||
|
|
||||||
|
final int expectedTotalLength = 8 + expectedPayloadLength + (expectedCrc ? 2 : 0);
|
||||||
|
final int actualTotalLength = 8 + actualPayloadLength + (actualCrc ? 2 : 0);
|
||||||
|
Assert.assertEquals("expected message has invalid size", expectedTotalLength, expected.length);
|
||||||
|
Assert.assertEquals("actual message has invalid size", actualTotalLength, actual.length);
|
||||||
|
|
||||||
|
// Counter byte at offset 7 is intentionally ignored.
|
||||||
|
Assert.assertArrayEquals(
|
||||||
|
"payload differs",
|
||||||
|
Arrays.copyOfRange(expected, 8, 8 + expectedPayloadLength),
|
||||||
|
Arrays.copyOfRange(actual, 8, 8 + actualPayloadLength)
|
||||||
|
);
|
||||||
|
|
||||||
|
assertCrcIsValid(expected, expectedCrc);
|
||||||
|
assertCrcIsValid(actual, actualCrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readLeShort(final byte[] msg, final int offset) {
|
||||||
|
return (msg[offset] & 0xff) | ((msg[offset + 1] & 0xff) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertCrcIsValid(final byte[] msg, final boolean hasCrc) {
|
||||||
|
if (!hasCrc) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int payloadLength = readLeShort(msg, 5);
|
||||||
|
final int crcOffset = 8 + payloadLength;
|
||||||
|
final int expectedCrc = readLeShort(msg, crcOffset);
|
||||||
|
final int actualCrc = getCRC16ansi(Arrays.copyOf(msg, crcOffset));
|
||||||
|
Assert.assertEquals("invalid crc", expectedCrc, actualCrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T extends GBDeviceEvent> T findEvent(final GBDeviceEvent[] events, final Class<T> cls) {
|
||||||
|
for (GBDeviceEvent event : events) {
|
||||||
|
if (cls.isInstance(event)) {
|
||||||
|
return (T) event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user