1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-28 01:24:05 +02:00

Add support for Moondrop Space Travel

All features provided by the Moondrop Link App are supported.
This includes setting the equalizer profile and
configuring the touch actions.
This commit is contained in:
Severin von Wnuck-Lipinski 2024-06-30 18:42:11 +02:00 committed by José Rebelo
parent 0035c52d2a
commit 8fbc12c868
12 changed files with 726 additions and 0 deletions

View File

@ -403,6 +403,22 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_SONY_ADAPTIVE_VOLUME_CONTROL = "pref_adaptive_volume_control";
public static final String PREF_SONY_WIDE_AREA_TAP = "pref_wide_area_tap";
public static final String PREF_MOONDROP_EQUALIZER_PRESET = "pref_moondrop_equalizer_preset";
public static final String PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD = "pref_moondrop_touch_play_pause_earbud";
public static final String PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER = "pref_moondrop_touch_play_pause_trigger";
public static final String PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD = "pref_moondrop_touch_media_prev_earbud";
public static final String PREF_MOONDROP_TOUCH_MEDIA_PREV_TRIGGER = "pref_moondrop_touch_media_prev_trigger";
public static final String PREF_MOONDROP_TOUCH_MEDIA_NEXT_EARBUD = "pref_moondrop_touch_media_next_earbud";
public static final String PREF_MOONDROP_TOUCH_MEDIA_NEXT_TRIGGER = "pref_moondrop_touch_media_next_trigger";
public static final String PREF_MOONDROP_TOUCH_CALL_PICK_HANG_EARBUD = "pref_moondrop_touch_call_pick_hang_earbud";
public static final String PREF_MOONDROP_TOUCH_CALL_PICK_HANG_TRIGGER = "pref_moondrop_touch_call_pick_hang_trigger";
public static final String PREF_MOONDROP_TOUCH_CALL_START_EARBUD = "pref_moondrop_touch_call_start_earbud";
public static final String PREF_MOONDROP_TOUCH_CALL_START_TRIGGER = "pref_moondrop_touch_call_start_trigger";
public static final String PREF_MOONDROP_TOUCH_ASSISTANT_EARBUD = "pref_moondrop_touch_assistant_earbud";
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_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level";
public static final String PREFS_ACTIVITY_IN_DEVICE_CARD = "prefs_activity_in_device_card";

View File

@ -678,6 +678,22 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
addPreferenceHandlerFor(PREF_SOUNDCORE_CONTROL_LONG_PRESS_ACTION_LEFT);
addPreferenceHandlerFor(PREF_SOUNDCORE_CONTROL_LONG_PRESS_ACTION_RIGHT);
addPreferenceHandlerFor(PREF_MOONDROP_EQUALIZER_PRESET);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_MEDIA_PREV_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_MEDIA_NEXT_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_MEDIA_NEXT_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_CALL_PICK_HANG_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_CALL_PICK_HANG_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_CALL_START_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_CALL_START_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_ASSISTANT_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_ASSISTANT_TRIGGER);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_ANC_MODE_EARBUD);
addPreferenceHandlerFor(PREF_MOONDROP_TOUCH_ANC_MODE_TRIGGER);
addPreferenceHandlerFor(PREF_FEMOMETER_MEASUREMENT_MODE);
addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL);

View File

@ -0,0 +1,82 @@
/* Copyright (C) 2024 Severin von Wnuck-Lipinski
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 androidx.annotation.NonNull;
import java.util.regex.Pattern;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLClassicDeviceCoordinator;
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.moondrop.MoondropSpaceTravelDeviceSupport;
public class MoondropSpaceTravelCoordinator extends AbstractBLClassicDeviceCoordinator {
@Override
public String getManufacturer() {
return "Moondrop";
}
@Override
protected Pattern getSupportedDeviceName() {
return Pattern.compile("Moondrop Space Travel");
}
@Override
public int getDeviceNameResource() {
return R.string.devicetype_moondrop_space_travel;
}
@Override
public int getBatteryCount() {
return 0;
}
@Override
protected void deleteDevice(
@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session)
throws GBException {}
@Override
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
final DeviceSpecificSettings settings = new DeviceSpecificSettings();
settings.addRootScreen(R.xml.devicesettings_moondrop_space_travel_equalizer);
settings.addRootScreen(DeviceSpecificSettingsScreen.TOUCH_OPTIONS);
settings.addSubScreen(
DeviceSpecificSettingsScreen.TOUCH_OPTIONS,
R.xml.devicesettings_moondrop_space_travel_touch);
settings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS);
settings.addSubScreen(
DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS,
R.xml.devicesettings_headphones);
return settings;
}
@NonNull
@Override
public Class<? extends DeviceSupport> getDeviceSupportClass() {
return MoondropSpaceTravelDeviceSupport.class;
}
}

View File

@ -174,6 +174,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd.MijiaLywsd02Coor
import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd.MijiaLywsd03Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd.MijiaMhoC303Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miscale2.MiScale2DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.moondrop.MoondropSpaceTravelCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear2Coordinator;
@ -409,6 +410,7 @@ public enum DeviceType {
SONY_WH_1000XM5(SonyWH1000XM5Coordinator.class),
SONY_WF_1000XM5(SonyWF1000XM5Coordinator.class),
SOUNDCORE_LIBERTY3_PRO(SoundcoreLiberty3ProCoordinator.class),
MOONDROP_SPACE_TRAVEL(MoondropSpaceTravelCoordinator.class),
BOSE_QC35(QC35Coordinator.class),
HONORBAND3(HonorBand3Coordinator.class),
HONORBAND4(HonorBand4Coordinator.class),

View File

@ -0,0 +1,136 @@
/* Copyright (C) 2024 Severin von Wnuck-Lipinski
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.service.devices.moondrop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
public class GaiaPacket {
private static final Logger LOG = LoggerFactory.getLogger(GaiaPacket.class);
public static final byte PDU_COMMAND = (byte)0x00;
public static final byte PDU_NOTIFICATION = (byte)0x01;
public static final byte PDU_RESPONSE = (byte)0x02;
public static final byte PDU_ERROR = (byte)0x03;
private static final int HEADER_LENGTH = 8;
private static final byte START_OF_PACKET = (byte)0xff;
private static final byte VERSION = (byte)0x04;
private static final byte FLAGS_NO_CHECK = (byte)0x00;
// Qualcomm's vendor ID
private static final short VENDOR_ID = 0x001d;
private byte featureId;
private byte pduType;
private byte pduId;
private byte[] payload;
public GaiaPacket(byte featureId, byte pduId) {
this(featureId, pduId, new byte[] {});
}
public GaiaPacket(byte featureId, byte pduId, byte[] payload) {
this(featureId, PDU_COMMAND, pduId, payload);
}
private GaiaPacket(byte featureId, byte pduType, byte pduId, byte[] payload) {
LOG.debug(
"Packet: featureId={}, pduType={}, pduId={}, length={}",
String.format("0x%02x", featureId),
String.format("0x%02x", pduType),
String.format("0x%02x", pduId),
payload.length);
this.featureId = featureId;
this.pduType = pduType;
this.pduId = pduId;
this.payload = payload;
}
public byte getFeatureId() {
return featureId;
}
public byte getPduType() {
return pduType;
}
public byte getPduId() {
return pduId;
}
public byte[] getPayload() {
return payload;
}
public static GaiaPacket decode(ByteBuffer buf) {
if (buf.remaining() < HEADER_LENGTH)
return null;
if (buf.get() != START_OF_PACKET) {
LOG.error("Invalid GAIA start of packet: {}", hexdump(buf.array()));
return null;
}
if (buf.get() != VERSION) {
LOG.error("Invalid GAIA version: {}", hexdump(buf.array()));
return null;
}
if (buf.get() != FLAGS_NO_CHECK) {
LOG.error("Invalid GAIA flags: {}", hexdump(buf.array()));
return null;
}
byte length = buf.get();
if (buf.getShort() != VENDOR_ID) {
LOG.error("Invalid GAIA vendor ID: {}", hexdump(buf.array()));
return null;
}
short commandId = buf.getShort();
byte featureId = (byte)((commandId & 0xfe00) >> 9);
byte pduType = (byte)((commandId & 0x0180) >> 7);
byte pduId = (byte)(commandId & 0x7f);
byte[] payload = new byte[length];
buf.get(payload);
return new GaiaPacket(featureId, pduType, pduId, payload);
}
public byte[] encode() {
ByteBuffer buf = ByteBuffer.allocate(HEADER_LENGTH + payload.length);
buf.put(START_OF_PACKET);
buf.put(VERSION);
buf.put(FLAGS_NO_CHECK);
buf.put((byte) payload.length);
buf.putShort(VENDOR_ID);
buf.putShort((short)((featureId << 9) | (pduType << 7) | pduId));
buf.put(payload);
return buf.array();
}
}

View File

@ -0,0 +1,43 @@
/* Copyright (C) 2024 Severin von Wnuck-Lipinski
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.service.devices.moondrop;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractHeadphoneDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
public class MoondropSpaceTravelDeviceSupport extends AbstractHeadphoneDeviceSupport {
@Override
public boolean useAutoConnect() {
return false;
}
@Override
protected GBDeviceProtocol createDeviceProtocol() {
return new MoondropSpaceTravelProtocol(getDevice());
}
@Override
protected GBDeviceIoThread createDeviceIOThread() {
return new MoondropSpaceTravelIOThread(
getDevice(),
getContext(),
(MoondropSpaceTravelProtocol)getDeviceProtocol(),
MoondropSpaceTravelDeviceSupport.this,
getBluetoothAdapter());
}
}

View File

@ -0,0 +1,64 @@
/* Copyright (C) 2024 Severin von Wnuck-Lipinski
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.service.devices.moondrop;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
public class MoondropSpaceTravelIOThread extends BtClassicIoThread {
private static final Logger LOG = LoggerFactory.getLogger(MoondropSpaceTravelIOThread.class);
private final MoondropSpaceTravelProtocol protocol;
public MoondropSpaceTravelIOThread(
GBDevice device,
Context context,
MoondropSpaceTravelProtocol protocol,
MoondropSpaceTravelDeviceSupport support,
BluetoothAdapter adapter) {
super(device, context, protocol, support, adapter);
this.protocol = protocol;
}
@Override
protected void initialize() {
write(protocol.encodeGetEqualizerPreset());
write(protocol.encodeGetTouchActions());
setUpdateState(GBDevice.State.INITIALIZED);
}
@Override
protected byte[] parseIncoming(InputStream stream) throws IOException {
byte[] buffer = new byte[1048576];
int bytes = stream.read(buffer);
LOG.debug("read " + bytes + " bytes. " + hexdump(buffer, 0, bytes));
return Arrays.copyOf(buffer, bytes);
}
}

View File

@ -0,0 +1,187 @@
/* Copyright (C) 2024 Severin von Wnuck-Lipinski
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.service.devices.moondrop;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
public class MoondropSpaceTravelProtocol extends GBDeviceProtocol {
private static final int EQUALIZER_PRESET_PKT_LEN = 1;
private static final byte EQUALIZER_PRESET_FEATURE = (byte)0x05;
private static final byte EQUALIZER_PRESET_PDU_GET = (byte)0x02;
private static final byte EQUALIZER_PRESET_PDU_SET = (byte)0x03;
private static final int TOUCH_ACTIONS_PKT_LEN = 12;
private static final byte TOUCH_ACTIONS_FEATURE = (byte)0x01;
private static final byte TOUCH_ACTIONS_PDU_GET = (byte)0x02;
private static final byte TOUCH_ACTIONS_PDU_SET = (byte)0x03;
protected MoondropSpaceTravelProtocol(GBDevice device) {
super(device);
}
@Override
public GBDeviceEvent[] decodeResponse(byte[] data) {
List<GBDeviceEvent> events = new ArrayList<>();
ByteBuffer buf = ByteBuffer.wrap(data);
while (buf.hasRemaining()) {
GaiaPacket packet = GaiaPacket.decode(buf);
if (packet == null)
break;
if (packet.getPduType() != GaiaPacket.PDU_RESPONSE)
continue;
short featureId = packet.getFeatureId();
short pduId = packet.getPduId();
byte[] payload = packet.getPayload();
if (featureId == EQUALIZER_PRESET_FEATURE && pduId == EQUALIZER_PRESET_PDU_GET)
events.add(handlePacketEqualizerPreset(payload));
else if (featureId == TOUCH_ACTIONS_FEATURE && pduId == TOUCH_ACTIONS_PDU_GET)
events.add(handlePacketTouchActions(payload));
}
return null;
}
private GBDeviceEvent handlePacketEqualizerPreset(byte[] payload) {
if (payload.length != EQUALIZER_PRESET_PKT_LEN)
return null;
byte preset = payload[0];
return new GBDeviceEventUpdatePreferences(PREF_MOONDROP_EQUALIZER_PRESET, String.valueOf(preset));
}
private GBDeviceEvent handlePacketTouchActions(byte[] payload) {
if (payload.length != TOUCH_ACTIONS_PKT_LEN)
return null;
Map<String, Object> prefs = new HashMap<>();
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD, PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER, payload[1]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD, PREF_MOONDROP_TOUCH_MEDIA_PREV_TRIGGER, payload[2]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_MEDIA_NEXT_EARBUD, PREF_MOONDROP_TOUCH_MEDIA_NEXT_TRIGGER, payload[3]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_CALL_PICK_HANG_EARBUD, PREF_MOONDROP_TOUCH_CALL_PICK_HANG_TRIGGER, payload[6]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_CALL_START_EARBUD, PREF_MOONDROP_TOUCH_CALL_START_TRIGGER, payload[7]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_ASSISTANT_EARBUD, PREF_MOONDROP_TOUCH_ASSISTANT_TRIGGER, payload[8]));
prefs.putAll(decodeTouchAction(PREF_MOONDROP_TOUCH_ANC_MODE_EARBUD, PREF_MOONDROP_TOUCH_ANC_MODE_TRIGGER, payload[10]));
return new GBDeviceEventUpdatePreferences(prefs);
}
@Override
public byte[] encodeSendConfiguration(String config) {
switch (config) {
case PREF_MOONDROP_EQUALIZER_PRESET:
return encodeSetEqualizerPreset();
case PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD:
case PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER:
case PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD:
case PREF_MOONDROP_TOUCH_MEDIA_PREV_TRIGGER:
case PREF_MOONDROP_TOUCH_MEDIA_NEXT_EARBUD:
case PREF_MOONDROP_TOUCH_MEDIA_NEXT_TRIGGER:
case PREF_MOONDROP_TOUCH_CALL_PICK_HANG_EARBUD:
case PREF_MOONDROP_TOUCH_CALL_PICK_HANG_TRIGGER:
case PREF_MOONDROP_TOUCH_CALL_START_EARBUD:
case PREF_MOONDROP_TOUCH_CALL_START_TRIGGER:
case PREF_MOONDROP_TOUCH_ASSISTANT_EARBUD:
case PREF_MOONDROP_TOUCH_ASSISTANT_TRIGGER:
case PREF_MOONDROP_TOUCH_ANC_MODE_EARBUD:
case PREF_MOONDROP_TOUCH_ANC_MODE_TRIGGER:
return encodeSetTouchActions();
}
return super.encodeSendConfiguration(config);
}
public byte[] encodeGetEqualizerPreset() {
return new GaiaPacket(EQUALIZER_PRESET_FEATURE, EQUALIZER_PRESET_PDU_GET).encode();
}
public byte[] encodeGetTouchActions() {
return new GaiaPacket(TOUCH_ACTIONS_FEATURE, TOUCH_ACTIONS_PDU_GET).encode();
}
private byte[] encodeSetEqualizerPreset() {
Prefs prefs = getDevicePrefs();
byte preset = Byte.parseByte(prefs.getString(PREF_MOONDROP_EQUALIZER_PRESET, "0"));
byte[] payload = new byte[] { preset };
return new GaiaPacket(EQUALIZER_PRESET_FEATURE, EQUALIZER_PRESET_PDU_SET, payload).encode();
}
private byte[] encodeSetTouchActions() {
Prefs prefs = getDevicePrefs();
byte actionPlayPause = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_PLAY_PAUSE_EARBUD, PREF_MOONDROP_TOUCH_PLAY_PAUSE_TRIGGER);
byte actionMediaPrev = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_MEDIA_PREV_EARBUD, PREF_MOONDROP_TOUCH_MEDIA_PREV_TRIGGER);
byte actionMediaNext = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_MEDIA_NEXT_EARBUD, PREF_MOONDROP_TOUCH_MEDIA_NEXT_TRIGGER);
byte actionCallPickHang = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_CALL_PICK_HANG_EARBUD, PREF_MOONDROP_TOUCH_CALL_PICK_HANG_TRIGGER);
byte actionCallStart = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_CALL_START_EARBUD, PREF_MOONDROP_TOUCH_CALL_START_TRIGGER);
byte actionAssistant = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_ASSISTANT_EARBUD, PREF_MOONDROP_TOUCH_ASSISTANT_TRIGGER);
byte actionAncMode = encodeTouchAction(prefs, PREF_MOONDROP_TOUCH_ANC_MODE_EARBUD, PREF_MOONDROP_TOUCH_ANC_MODE_TRIGGER);
byte[] payload = new byte[] {
(byte)0x01,
actionPlayPause,
actionMediaPrev,
actionMediaNext,
(byte)0x30,
(byte)0x30,
actionCallPickHang,
actionCallStart,
actionAssistant,
actionAssistant,
actionAncMode,
actionAncMode,
};
return new GaiaPacket(TOUCH_ACTIONS_FEATURE, TOUCH_ACTIONS_PDU_SET, payload).encode();
}
private byte encodeTouchAction(Prefs prefs, String prefEarbud, String prefTrigger) {
int earbud = Integer.valueOf(prefs.getString(prefEarbud, "3"));
int trigger = Integer.valueOf(prefs.getString(prefTrigger, "0"));
return (byte)((earbud << 4) | trigger);
}
private Map<String, Object> decodeTouchAction(String prefEarbud, String prefTrigger, byte action) {
int earbud = (action & 0xf0) >> 4;
int trigger = action & 0x0f;
return new HashMap<String, Object>() {{
put(prefEarbud, String.valueOf(earbud));
put(prefTrigger, String.valueOf(trigger));
}};
}
}

View File

@ -3462,6 +3462,47 @@
<item>AMBIENT_SOUND_CONTROL</item>
</string-array>
<string-array name="moondrop_equalizer_preset_names">
<item>@string/moondrop_equalizer_preset_reference</item>
<item>@string/moondrop_equalizer_preset_basshead</item>
<item>@string/moondrop_equalizer_preset_monitor</item>
</string-array>
<string-array name="moondrop_equalizer_preset_values">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="moondrop_touch_earbud_names">
<item>@string/left</item>
<item>@string/right</item>
<item>@string/moondrop_touch_earbud_both</item>
</string-array>
<string-array name="moondrop_touch_earbud_values">
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="moondrop_touch_trigger_names">
<item>@string/pref_button_action_disabled</item>
<item>@string/single_tap</item>
<item>@string/double_tap</item>
<item>@string/triple_tap</item>
<item>@string/moondrop_touch_trigger_long_press_1s</item>
<item>@string/moondrop_touch_trigger_long_press_3s</item>
</string-array>
<string-array name="moondrop_touch_trigger_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</string-array>
<string-array name="fitness_tracking_apps_package_names">
<item>de.dennisguse.opentracks</item>

View File

@ -1574,6 +1574,7 @@
<string name="devicetype_sony_linkbuds">Sony LinkBuds</string>
<string name="devicetype_sony_linkbuds_s">Sony LinkBuds S</string>
<string name="devicetype_soundcore_liberty3_pro">Soundcore Liberty 3 Pro</string>
<string name="devicetype_moondrop_space_travel">Moondrop Space Travel</string>
<string name="devicetype_binary_sensor">Binary sensor</string>
<string name="devicetype_honor_band3">Honor Band 3</string>
<string name="devicetype_honor_band4">Honor Band 4</string>
@ -2375,6 +2376,19 @@
<string name="sony_protocol_v1">Version 1</string>
<string name="sony_protocol_v2">Version 2</string>
<string name="sony_protocol_v3">Version 3</string>
<string name="moondrop_equalizer_preset_reference">Reference</string>
<string name="moondrop_equalizer_preset_basshead">Basshead</string>
<string name="moondrop_equalizer_preset_monitor">Monitor</string>
<string name="moondrop_touch_earbud">Earbud</string>
<string name="moondrop_touch_earbud_both">Both</string>
<string name="moondrop_touch_trigger">Trigger</string>
<string name="moondrop_touch_action_play_pause">Play/Pause</string>
<string name="moondrop_touch_action_call_pick_hang">Pick/Hang Call</string>
<string name="moondrop_touch_action_call_start">Start Call</string>
<string name="moondrop_touch_action_assistant">Trigger Voice Assistant</string>
<string name="moondrop_touch_action_anc_mode">Switch Active Noise Cancelling Mode</string>
<string name="moondrop_touch_trigger_long_press_1s">Long Press (1s)</string>
<string name="moondrop_touch_trigger_long_press_3s">Long Press (3s)</string>
<string name="protocol_version">Protocol Version</string>
<string name="pref_screen_auto_brightness_title">Auto Brightness</string>
<string name="pref_screen_auto_brightness_summary">Adjust screen brightness according to ambient light</string>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<ListPreference
android:entries="@array/moondrop_equalizer_preset_names"
android:entryValues="@array/moondrop_equalizer_preset_values"
android:icon="@drawable/ic_graphic_eq"
android:key="pref_moondrop_equalizer_preset"
android:summary="%s"
android:title="@string/prefs_equalizer_preset" />
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/moondrop_touch_action_play_pause">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_play_pause_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_play_pause_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_media_previous">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_media_prev_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_media_prev_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_media_next">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_media_next_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_media_next_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/moondrop_touch_action_call_pick_hang">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_call_pick_hang_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_call_pick_hang_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/moondrop_touch_action_call_start">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_call_start_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_call_start_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/moondrop_touch_action_assistant">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_assistant_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_assistant_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/moondrop_touch_action_anc_mode">
<ListPreference
android:entries="@array/moondrop_touch_earbud_names"
android:entryValues="@array/moondrop_touch_earbud_values"
android:icon="@drawable/ic_hearing"
android:key="pref_moondrop_touch_anc_mode_earbud"
android:summary="%s"
android:title="@string/moondrop_touch_earbud" />
<ListPreference
android:entries="@array/moondrop_touch_trigger_names"
android:entryValues="@array/moondrop_touch_trigger_values"
android:icon="@drawable/ic_touch"
android:key="pref_moondrop_touch_anc_mode_trigger"
android:summary="%s"
android:title="@string/moondrop_touch_trigger" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>