mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Moondrop: add support for Moondrop Space Travel 2 and its audio curation modes (ANC)
This commit is contained in:
+1
@@ -607,6 +607,7 @@ public class DeviceSettingsPreferenceConst {
|
||||
public static final String PREF_MOONDROP_TOUCH_ASSISTANT_TRIGGER = "pref_moondrop_touch_assistant_trigger";
|
||||
public static final String PREF_MOONDROP_TOUCH_ANC_MODE_EARBUD = "pref_moondrop_touch_anc_mode_earbud";
|
||||
public static final String PREF_MOONDROP_TOUCH_ANC_MODE_TRIGGER = "pref_moondrop_touch_anc_mode_trigger";
|
||||
public static final String PREF_MOONDROP_ANC_MODE = "pref_moondrop_anc_mode";
|
||||
|
||||
public static final String PREF_MISCALE_SMALL_OBJECTS = "pref_miscale_small_objects";
|
||||
|
||||
|
||||
+1
@@ -924,6 +924,7 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
|
||||
addPreferenceHandlerFor(PREF_SOUNDCORE_EQUALIZER_BAND9_VALUE);
|
||||
|
||||
addPreferenceHandlerFor(PREF_MOONDROP_EQUALIZER_PRESET);
|
||||
addPreferenceHandlerFor(PREF_MOONDROP_ANC_MODE);
|
||||
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD);
|
||||
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER);
|
||||
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD);
|
||||
|
||||
+7
-1
@@ -22,6 +22,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
@@ -37,7 +38,7 @@ public class MoondropSpaceTravelCoordinator extends AbstractBLClassicDeviceCoord
|
||||
|
||||
@Override
|
||||
protected Pattern getSupportedDeviceName() {
|
||||
return Pattern.compile("Moondrop Space Travel");
|
||||
return Pattern.compile("^(?:Moondrop )?Space Travel(?: 2)?$");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +79,11 @@ public class MoondropSpaceTravelCoordinator extends AbstractBLClassicDeviceCoord
|
||||
return MoondropSpaceTravelDeviceSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new MoondropSpaceTravelSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceCoordinator.DeviceKind getDeviceKind(@NonNull GBDevice device) {
|
||||
return DeviceKind.EARBUDS;
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/* Copyright (C) 2026 Jan Petrlík
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.moondrop;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_MOONDROP_ANC_MODE;
|
||||
|
||||
public class MoondropSpaceTravelSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
private final GBDevice device;
|
||||
|
||||
public MoondropSpaceTravelSettingsCustomizer(final GBDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs,
|
||||
final String rootKey) {
|
||||
if (rootKey == null) {
|
||||
// The device does not emit notifications when the user changes ANC mode via
|
||||
// touch, so refresh the current mode whenever the user opens the settings screen.
|
||||
GBApplication.deviceService(device).onReadConfiguration(PREF_MOONDROP_ANC_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static final Creator<MoondropSpaceTravelSettingsCustomizer> CREATOR = new Creator<MoondropSpaceTravelSettingsCustomizer>() {
|
||||
@Override
|
||||
public MoondropSpaceTravelSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
final GBDevice device = in.readParcelable(MoondropSpaceTravelSettingsCustomizer.class.getClassLoader());
|
||||
return new MoondropSpaceTravelSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MoondropSpaceTravelSettingsCustomizer[] newArray(final int size) {
|
||||
return new MoondropSpaceTravelSettingsCustomizer[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(final Parcel dest, final int flags) {
|
||||
dest.writeParcelable(device, 0);
|
||||
}
|
||||
}
|
||||
+12
@@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.moondrop;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_MOONDROP_ANC_MODE;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btbr.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractHeadphoneSerialDeviceSupportV2;
|
||||
@@ -30,8 +32,18 @@ public class MoondropSpaceTravelDeviceSupport extends AbstractHeadphoneSerialDev
|
||||
protected TransactionBuilder initializeDevice(final TransactionBuilder builder) {
|
||||
builder.write(mDeviceProtocol.encodeGetEqualizerPreset());
|
||||
builder.write(mDeviceProtocol.encodeGetTouchActions());
|
||||
builder.write(mDeviceProtocol.encodeGetAudioCurationMode());
|
||||
builder.setDeviceState(GBDevice.State.INITIALIZED);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadConfiguration(final String config) {
|
||||
if (PREF_MOONDROP_ANC_MODE.equals(config)) {
|
||||
final TransactionBuilder builder = createTransactionBuilder("read anc mode");
|
||||
builder.write(mDeviceProtocol.encodeGetAudioCurationMode());
|
||||
builder.queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+36
@@ -41,6 +41,10 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
private static final byte TOUCH_ACTIONS_PDU_GET = (byte)0x02;
|
||||
private static final byte TOUCH_ACTIONS_PDU_SET = (byte)0x03;
|
||||
|
||||
private static final byte AUDIO_CURATION_FEATURE = (byte)0x08;
|
||||
private static final byte AUDIO_CURATION_PDU_GET_MODE = (byte)0x03;
|
||||
private static final byte AUDIO_CURATION_PDU_SET_MODE = (byte)0x04;
|
||||
|
||||
protected MoondropSpaceTravelProtocol(GBDevice device) {
|
||||
super(device);
|
||||
}
|
||||
@@ -67,6 +71,8 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
events.add(handlePacketEqualizerPreset(payload));
|
||||
else if (featureId == TOUCH_ACTIONS_FEATURE && pduId == TOUCH_ACTIONS_PDU_GET)
|
||||
events.add(handlePacketTouchActions(payload));
|
||||
else if (featureId == AUDIO_CURATION_FEATURE && pduId == AUDIO_CURATION_PDU_GET_MODE)
|
||||
events.add(handlePacketAudioCurationMode(payload));
|
||||
}
|
||||
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
@@ -81,6 +87,21 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
return new GBDeviceEventUpdatePreferences(PREF_MOONDROP_EQUALIZER_PRESET, String.valueOf(preset));
|
||||
}
|
||||
|
||||
private GBDeviceEvent handlePacketAudioCurationMode(byte[] payload) {
|
||||
if (payload.length < 1)
|
||||
return null;
|
||||
|
||||
// GET_MODE returns a 0-based slot index, SET_MODE and the UI dropdown use a
|
||||
// bitmask (Normal=1, ANC=2, Transparency=4). Convert before storing the
|
||||
// preference.
|
||||
int slot = payload[0] & 0xff;
|
||||
if (slot > 2)
|
||||
return null;
|
||||
int mode = 1 << slot;
|
||||
|
||||
return new GBDeviceEventUpdatePreferences(PREF_MOONDROP_ANC_MODE, String.valueOf(mode));
|
||||
}
|
||||
|
||||
private GBDeviceEvent handlePacketTouchActions(byte[] payload) {
|
||||
if (payload.length != TOUCH_ACTIONS_PKT_LEN)
|
||||
return null;
|
||||
@@ -103,6 +124,8 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
switch (config) {
|
||||
case PREF_MOONDROP_EQUALIZER_PRESET:
|
||||
return encodeSetEqualizerPreset();
|
||||
case PREF_MOONDROP_ANC_MODE:
|
||||
return encodeSetAudioCurationMode();
|
||||
case PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD:
|
||||
case PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER:
|
||||
case PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD:
|
||||
@@ -127,6 +150,10 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
return new GaiaPacket(EQUALIZER_PRESET_FEATURE, EQUALIZER_PRESET_PDU_GET).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeGetAudioCurationMode() {
|
||||
return new GaiaPacket(AUDIO_CURATION_FEATURE, AUDIO_CURATION_PDU_GET_MODE).encode();
|
||||
}
|
||||
|
||||
public byte[] encodeGetTouchActions() {
|
||||
return new GaiaPacket(TOUCH_ACTIONS_FEATURE, TOUCH_ACTIONS_PDU_GET).encode();
|
||||
}
|
||||
@@ -140,6 +167,15 @@ public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
|
||||
return new GaiaPacket(EQUALIZER_PRESET_FEATURE, EQUALIZER_PRESET_PDU_SET, payload).encode();
|
||||
}
|
||||
|
||||
private byte[] encodeSetAudioCurationMode() {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte mode = Byte.parseByte(prefs.getString(PREF_MOONDROP_ANC_MODE, "1"));
|
||||
|
||||
byte[] payload = new byte[] { mode };
|
||||
|
||||
return new GaiaPacket(AUDIO_CURATION_FEATURE, AUDIO_CURATION_PDU_SET_MODE, payload).encode();
|
||||
}
|
||||
|
||||
private byte[] encodeSetTouchActions() {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
byte actionPlayPause = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD, PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER);
|
||||
|
||||
@@ -4749,6 +4749,18 @@
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="moondrop_anc_mode_names">
|
||||
<item>@string/prefs_active_noise_cancelling_level_normal</item>
|
||||
<item>@string/prefs_active_noise_cancelling</item>
|
||||
<item>@string/prefs_active_noise_cancelling_transparency</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="moondrop_anc_mode_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>4</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="moondrop_touch_earbud_names">
|
||||
<item>@string/left</item>
|
||||
<item>@string/right</item>
|
||||
|
||||
@@ -2142,7 +2142,7 @@
|
||||
<string name="devicetype_soundcore_q30" translatable="false">Soundcore Q30</string>
|
||||
<string name="devicetype_soundcore_aerofit2" translatable="false">Soundcore AeroFit 2</string>
|
||||
<string name="devicetype_pixel_buds_a_series" translatable="false">Pixel Buds A-Series</string>
|
||||
<string name="devicetype_moondrop_space_travel" translatable="false">Moondrop Space Travel</string>
|
||||
<string name="devicetype_moondrop_space_travel" translatable="false">Moondrop Space Travel / 2</string>
|
||||
<string name="devicetype_binary_sensor">Binary sensor</string>
|
||||
<string name="devicetype_honor_band3" translatable="false">Honor Band 3</string>
|
||||
<string name="devicetype_honor_band4" translatable="false">Honor Band 4</string>
|
||||
|
||||
@@ -8,4 +8,11 @@
|
||||
android:key="pref_moondrop_equalizer_preset"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
android:title="@string/prefs_equalizer_preset" />
|
||||
<ListPreference
|
||||
android:entries="@array/moondrop_anc_mode_names"
|
||||
android:entryValues="@array/moondrop_anc_mode_values"
|
||||
android:icon="@drawable/ic_voice"
|
||||
android:key="pref_moondrop_anc_mode"
|
||||
app:useSimpleSummaryProvider="true"
|
||||
android:title="@string/prefs_active_noise_cancelling_level" />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user