mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-09-01 20:15:52 +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:
parent
0035c52d2a
commit
8fbc12c868
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}};
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
Loading…
Reference in New Issue
Block a user