mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
Add support for EarFun Headphone devices Air S and Air Pro 4
This commit is contained in:
committed by
José Rebelo
parent
f35bcc8890
commit
d9d0573812
+51
@@ -0,0 +1,51 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.earfun;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunSettingsCustomizer;
|
||||
|
||||
public abstract class AbstractEarFunCoordinator extends AbstractDeviceCoordinator {
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "EarFun";
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return EarFunDeviceSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(){
|
||||
return BONDING_STYLE_BOND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDefaultIconResource() {
|
||||
return R.drawable.ic_device_nothingear;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDisabledIconResource() {
|
||||
return R.drawable.ic_device_nothingear_disabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
|
||||
return new EarFunSettingsCustomizer(device);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.earfun;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4.EarFunAirPro4DeviceSupport;
|
||||
|
||||
public class EarFunAirPro4Coordinator extends AbstractEarFunCoordinator {
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_earfun_air_pro_4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(GBDeviceCandidate candidate) {
|
||||
if (candidate.getName().startsWith("EarFun Air Pro 4")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// can't only check with name, because the device name can be changed
|
||||
// via the device settings, so we use some of the UUIDs available on the device
|
||||
// and the mac address prefix to hopefully detect this model reliable
|
||||
String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb";
|
||||
String[] uuidPrefixes = {"0000180f", "0000180a"};
|
||||
|
||||
boolean allServicesSupported = Arrays.stream(uuidPrefixes)
|
||||
.map(uuidPrefix -> UUID.fromString(uuidPrefix + UUID_SUFFIX))
|
||||
.map(candidate::supportsService).allMatch(b -> b);
|
||||
|
||||
boolean macAddressMatches = candidate.getMacAddress().toUpperCase().startsWith("70:5A:6F");
|
||||
|
||||
return allServicesSupported && macAddressMatches;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return EarFunAirPro4DeviceSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount(final GBDevice device) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(final GBDevice device) {
|
||||
BatteryConfig battery1 = new BatteryConfig(2, R.drawable.ic_buds_pro_case, R.string.battery_case);
|
||||
BatteryConfig battery2 = new BatteryConfig(0, R.drawable.ic_nothing_ear_l, R.string.left_earbud);
|
||||
BatteryConfig battery3 = new BatteryConfig(1, R.drawable.ic_nothing_ear_r, R.string.right_earbud);
|
||||
return new BatteryConfig[]{battery1, battery2, battery3};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_headphones);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_pro_4_gestures);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_10_band_equalizer);
|
||||
return deviceSpecificSettings;
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.earfun;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs.EarFunAirSDeviceSupport;
|
||||
|
||||
public class EarFunAirSCoordinator extends AbstractEarFunCoordinator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunAirSCoordinator.class);
|
||||
|
||||
@Override
|
||||
public int getDeviceNameResource() {
|
||||
return R.string.devicetype_earfun_air_s;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supports(GBDeviceCandidate candidate) {
|
||||
if (candidate.getName().startsWith("EarFun Air S")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// can't only check with name, because the device name can be changed
|
||||
// via the device settings, so we use some of the UUIDs available on the device
|
||||
// an the mac address prefix to hopefully detect this model reliable
|
||||
String UUID_SUFFIX = "-0000-1000-8000-00805f9b34fb";
|
||||
String[] uuidPrefixes = {"00001101", "0000111e", "0000110b", "0000110e", "00000000"};
|
||||
|
||||
boolean allServicesSupported = Arrays.stream(uuidPrefixes)
|
||||
.map(uuidPrefix -> UUID.fromString(uuidPrefix + UUID_SUFFIX))
|
||||
.map(candidate::supportsService).allMatch(b -> b);
|
||||
|
||||
boolean macAddressMatches = candidate.getMacAddress().toUpperCase().startsWith("A8:99:DC");
|
||||
|
||||
return allServicesSupported && macAddressMatches;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Class<? extends DeviceSupport> getDeviceSupportClass() {
|
||||
return EarFunAirSDeviceSupport.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBatteryCount(final GBDevice device) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BatteryConfig[] getBatteryConfig(final GBDevice device) {
|
||||
BatteryConfig battery1 = new BatteryConfig(0, R.drawable.ic_nothing_ear_l, R.string.left_earbud);
|
||||
BatteryConfig battery2 = new BatteryConfig(1, R.drawable.ic_nothing_ear_r, R.string.right_earbud);
|
||||
return new BatteryConfig[]{battery1, battery2};
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) {
|
||||
final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings();
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_headphones);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_air_s_gestures);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_device_name);
|
||||
deviceSpecificSettings.addRootScreen(R.xml.devicesettings_earfun_6_band_equalizer);
|
||||
return deviceSpecificSettings;
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.colmi.ColmiR10Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.cycling_sensor.coordinator.CyclingSensorCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.divoom.PixooCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.domyos.DomyosT540Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.earfun.EarFunAirPro4Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.earfun.EarFunAirSCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.evenrealities.G1DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.femometer.FemometerVinca2DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.fitpro.FitProDeviceCoordinator;
|
||||
@@ -611,6 +613,8 @@ public enum DeviceType {
|
||||
COLMI_R09(ColmiR09Coordinator.class),
|
||||
COLMI_R10(ColmiR10Coordinator.class),
|
||||
B_AND_W_P_SERIES(BandWPSeriesDeviceCoordinator.class),
|
||||
EARFUN_AIR_S(EarFunAirSCoordinator.class),
|
||||
EARFUN_AIR_PRO_4(EarFunAirPro4Coordinator.class),
|
||||
SCANNABLE(ScannableDeviceCoordinator.class),
|
||||
CYCLING_SENSOR(CyclingSensorCoordinator.class),
|
||||
BLE_GATT_CLIENT(BleGattClientCoordinator.class),
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.AbstractHeadphoneDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class EarFunDeviceSupport extends AbstractHeadphoneDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunDeviceSupport.class);
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
super.onSendConfiguration(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestNewFunction() {
|
||||
super.onTestNewFunction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized EarFunIOThread getDeviceIOThread() {
|
||||
return (EarFunIOThread) super.getDeviceIOThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected GBDeviceProtocol createDeviceProtocol() {
|
||||
return new EarFunProtocol(getDevice());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GBDeviceIoThread createDeviceIOThread() {
|
||||
return new EarFunIOThread(getDevice(), getContext(), (EarFunProtocol) getDeviceProtocol(),
|
||||
EarFunDeviceSupport.this, getBluetoothAdapter());
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
|
||||
|
||||
public class EarFunIOThread extends BtClassicIoThread {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunIOThread.class);
|
||||
private final EarFunProtocol earFunProtocol;
|
||||
public static final UUID GAIA_SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
||||
private static final UUID GAIA_UUID = UUID.fromString("00001107-D102-11E1-9B23-00025B00A5A5");
|
||||
|
||||
@NonNull
|
||||
protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) {
|
||||
return GAIA_SPP_UUID;
|
||||
}
|
||||
|
||||
public EarFunIOThread(GBDevice device, Context context, EarFunProtocol deviceProtocol,
|
||||
EarFunDeviceSupport earFunDeviceSupport, BluetoothAdapter bluetoothAdapter) {
|
||||
super(device, context, deviceProtocol, earFunDeviceSupport, bluetoothAdapter);
|
||||
earFunProtocol = deviceProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
super.initialize();
|
||||
write(earFunProtocol.encodeFirmwareVersionReq());
|
||||
write(earFunProtocol.encodeBatteryReq());
|
||||
write(earFunProtocol.encodeSoundReq());
|
||||
write(earFunProtocol.encodeTouchActionReq());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] parseIncoming(InputStream inStream) throws IOException {
|
||||
byte[] buffer = new byte[1048576]; //HUGE read
|
||||
int bytes = inStream.read(buffer);
|
||||
LOG.debug("read " + bytes + " bytes. " + hexdump(buffer, 0, bytes));
|
||||
return Arrays.copyOf(buffer, bytes);
|
||||
}
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class EarFunPacket {
|
||||
|
||||
/*
|
||||
EarFun uses the structure of Gaia packages, a checksum is never used
|
||||
|
||||
0 bytes 1 2 3 4 5 6 7 8 9 len+8
|
||||
+--------+---------+--------+--------+--------+--------+--------+--------+ +--------+--------+
|
||||
| SOP | VERSION | FLAGS | LENGTH | VENDOR ID | COMMAND ID | | PAYLOAD ... |
|
||||
+--------+---------+--------+--------+--------+--------+--------+--------+ +--------+--------+
|
||||
*/
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunPacket.class);
|
||||
private static final int HEADER_LENGTH = 8;
|
||||
private static final byte START_OF_PACKET = (byte) 0xff;
|
||||
public static final byte DEFAULT_VERSION = (byte) 0x04;
|
||||
private static final List<Byte> SUPPORTED_VERSIONS = Arrays.asList(DEFAULT_VERSION, (byte) 0x03);
|
||||
private static final byte FLAGS_NO_CHECKSUM = (byte) 0x00;
|
||||
public static final short DEFAULT_VENDOR_ID = (short) 0x000A;
|
||||
public static final short OTHER_VENDOR_ID = (short) 0x001D;
|
||||
private static final List<Short> SUPPORTED_VENDOR_IDS = Arrays.asList(DEFAULT_VENDOR_ID, OTHER_VENDOR_ID);
|
||||
static final int COMMAND_MASK = 0x7FFF;
|
||||
static final int ACK_MASK = 0x8000;
|
||||
public static final int COMMAND_INTENT_GET = 0x0080;
|
||||
public static final int COMMAND_TYPE_MASK = 0x7F00;
|
||||
public static final int COMMAND_CODE_MASK = 0x00FF;
|
||||
public static final byte TYPE_CONFIGURATION = (byte) 0x01;
|
||||
public static final byte TYPE_CONTROL = (byte) 0x02;
|
||||
public static final byte TYPE_STATUS = (byte) 0x03;
|
||||
public static final byte TYPE_FEATURE = (byte) 0x05;
|
||||
public static final byte TYPE_DATA_TRANSFER = (byte) 0x06;
|
||||
public static final byte TYPE_DEBUG = (byte) 0x07;
|
||||
public static final byte TYPE_NOTIFICATION = (byte) 0x40;
|
||||
public static final byte TYPE_ACKNOWLEDGE = (byte) 0x80;
|
||||
|
||||
public enum Command {
|
||||
/*
|
||||
A Command has the following structure:
|
||||
0 bytes 1 2
|
||||
+--------+---------+
|
||||
| Type | Code |
|
||||
+--------+---------+
|
||||
|
||||
to bring some structure into the commands, we use the following naming conventions:
|
||||
REQUEST_.. commands to request a status, payload is usually empty in this case
|
||||
RESPONSE_.. commands that are send back from the headphones as response to a REQUEST
|
||||
SET_.. commands that can be used to set a value
|
||||
*/
|
||||
REQUEST_CONFIGURATION((short) 0x0001, OTHER_VENDOR_ID),
|
||||
REQUEST_CONFIGURATION2((short) 0x0007, OTHER_VENDOR_ID),
|
||||
REQUEST_018D((short) 0x000D, OTHER_VENDOR_ID),
|
||||
// Pro 4: 000D01050108070103060207010005
|
||||
RESPONSE_CONFIGURATION((short) 0x0101, OTHER_VENDOR_ID), // 00030108010103060207010004
|
||||
UNIDENTIFIED_0107((short) 0x0107, OTHER_VENDOR_ID), // response, no payload
|
||||
RESPONSE_CONFIGURATION2((short) 0x0187, OTHER_VENDOR_ID), // 00
|
||||
RESPONSE_018D((short) 0x018D, OTHER_VENDOR_ID), // 05
|
||||
UNIDENTIFIED_0282((short) 0x0282, OTHER_VENDOR_ID), // 01
|
||||
REQUEST_RESPONSE_0300((short) 0x0300), // 00030301
|
||||
REQUEST_RESPONSE_BATTERY_STATE_LEFT((short) 0x0306),
|
||||
REQUEST_RESPONSE_BATTERY_STATE_RIGHT((short) 0x0307),
|
||||
REQUEST_RESPONSE_FIRMWARE_VERSION((short) 0x0309),
|
||||
SET_GAME_MODE((short) 0x0312),
|
||||
REQUEST_RESPONSE_GAME_MODE((short) 0x0313),
|
||||
SET_AMBIENT_SOUND((short) 0x0314),
|
||||
REQUEST_RESPONSE_AMBIENT_SOUND((short) 0x0315),
|
||||
SET_DEVICENAME((short) 0x0316),
|
||||
REQUEST_RESPONSE_BATTERY_STATE_CASE((short) 0x0317),
|
||||
REQUEST_RESPONSE_0318((short) 0x0318), // 0000 or 0001
|
||||
UNIDENTIFIED_0321((short) 0x0321), // Pro 4
|
||||
UNIDENTIFIED_0326((short) 0x0326), // 0001 Pro 4
|
||||
SET_TOUCH_ACTION((short) 0x030A),
|
||||
REQUEST_RESPONSE_TOUCH_ACTION((short) 0x030B),
|
||||
UNIDENTIFIED_032C((short) 0x032C), // 00004C47205137000000000000000000000000000000006DC3F201 Pro 4
|
||||
UNIDENTIFIED_032F((short) 0x032F), // 0013 Pro 4 // check
|
||||
UNIDENTIFIED_0331((short) 0x0331), // 0000 Pro 4 // check
|
||||
UNIDENTIFIED_0333((short) 0x0333), // 0000 Pro 4 // check
|
||||
UNIDENTIFIED_0335((short) 0x0335), // 0000 Pro 4 // check
|
||||
UNIDENTIFIED_0339((short) 0x0339), // 0001 Pro 4 // check
|
||||
SET_ANC_MODE((short) 0x033A),
|
||||
REQUEST_RESPONSE_ANC_MODE((short) 0x033B),
|
||||
SET_TRANSPARENCY_MODE((short) 0x033C),
|
||||
REQUEST_RESPONSE_TRANSPARENCY_MODE((short) 0x033D),
|
||||
UNIDENTIFIED_034A((short) 0x034A), // 0000 Pro 4
|
||||
UNIDENTIFIED_034C((short) 0x034C), // 0000 Pro 4
|
||||
UNIDENTIFIED_034D((short) 0x034D), // 0003 Pro 4
|
||||
UNIDENTIFIED_0348((short) 0x0348), // no REQUEST
|
||||
UNIDENTIFIED_0350((short) 0x0350), // 0001 Pro 4
|
||||
SET_EQUALIZER_BAND((short) 0x0E01, OTHER_VENDOR_ID), // answers with UNIDENTIFIED_0F81
|
||||
UNIDENTIFIED_0E80((short) 0x0E80, OTHER_VENDOR_ID), // 01
|
||||
UNIDENTIFIED_0F81((short) 0x0F81, OTHER_VENDOR_ID), // After setting EQ Band 01
|
||||
UNIDENTIFIED_1080((short) 0x1080, OTHER_VENDOR_ID), // 0100 -> 0101 if ANC not off
|
||||
UNIDENTIFIED_1081((short) 0x1081, OTHER_VENDOR_ID), // 01010000 <- otherwise, 0A010000 <- ANC Transparent
|
||||
UNIDENTIFIED_1082((short) 0x1082, OTHER_VENDOR_ID), // 01010000 -> 01014646
|
||||
UNIDENTIFIED_1083((short) 0x1083, OTHER_VENDOR_ID), // 0101, 0205, 03FF,
|
||||
UNIDENTIFIED_1084((short) 0x1084, OTHER_VENDOR_ID), // 01FF, 02FF, 03FF, 04FF,
|
||||
UNIDENTIFIED_1085((short) 0x1085, OTHER_VENDOR_ID), // 00
|
||||
UNIDENTIFIED_8300((short) 0x8300), // Pro 4
|
||||
UNIDENTIFIED_8306((short) 0x8306); // Pro 4
|
||||
|
||||
public final short commandId;
|
||||
public final short vendorId;
|
||||
public final byte version;
|
||||
|
||||
Command(short commandId) {
|
||||
this(commandId, DEFAULT_VENDOR_ID, DEFAULT_VERSION);
|
||||
}
|
||||
|
||||
Command(short commandId, short vendorId) {
|
||||
this(commandId, vendorId, DEFAULT_VERSION);
|
||||
}
|
||||
|
||||
Command(short commandId, short vendorId, byte version) {
|
||||
this.commandId = commandId;
|
||||
this.vendorId = vendorId;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public short getCommand() {
|
||||
return (short) (commandId & COMMAND_MASK);
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return (byte) ((commandId & COMMAND_TYPE_MASK) >> 8);
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return (byte) ((commandId & COMMAND_CODE_MASK));
|
||||
}
|
||||
|
||||
public boolean isAcknowledgement() {
|
||||
return (commandId & ACK_MASK) != 0;
|
||||
}
|
||||
|
||||
public boolean isIntentGet() {
|
||||
return (commandId & COMMAND_INTENT_GET) != 0;
|
||||
}
|
||||
|
||||
public static String describeType(byte type) {
|
||||
switch (type) {
|
||||
case TYPE_CONFIGURATION:
|
||||
return "configuration";
|
||||
case TYPE_CONTROL:
|
||||
return "control";
|
||||
case TYPE_STATUS:
|
||||
return "status";
|
||||
case TYPE_FEATURE:
|
||||
return "feature";
|
||||
case TYPE_DATA_TRANSFER:
|
||||
return "datatransfer";
|
||||
case TYPE_DEBUG:
|
||||
return "debug";
|
||||
case TYPE_NOTIFICATION:
|
||||
return "notification";
|
||||
case TYPE_ACKNOWLEDGE:
|
||||
return "acknowledge";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static String describeCommandId(short commandId) {
|
||||
byte type = (byte) ((commandId & COMMAND_TYPE_MASK) >> 8);
|
||||
byte code = (byte) ((commandId & COMMAND_CODE_MASK));
|
||||
return "Command{" +
|
||||
" commandId=" + hexdumpValue(commandId) +
|
||||
", type=" + hexdumpValue(type) + String.format(" (%s)", describeType(type)) +
|
||||
", code=" + hexdumpValue(code) +
|
||||
" }";
|
||||
}
|
||||
|
||||
public static Command getCommandById(short commandId) {
|
||||
short commandValue = (short) (commandId & COMMAND_MASK);
|
||||
return Arrays.stream(Command.values())
|
||||
.filter(command -> command.commandId == commandValue)
|
||||
.findFirst()
|
||||
.orElseGet(() -> {
|
||||
LOG.error("unknown command: {}", describeCommandId(commandId));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return describeCommandId(commandId);
|
||||
}
|
||||
}
|
||||
|
||||
private final Command command;
|
||||
private final byte[] payload;
|
||||
|
||||
public EarFunPacket(Command command) {
|
||||
this(command, new byte[0]);
|
||||
}
|
||||
|
||||
public EarFunPacket(Command command, byte payload) {
|
||||
this(command, new byte[]{payload});
|
||||
}
|
||||
|
||||
public EarFunPacket(Command command, byte[] payload) {
|
||||
this.command = command;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public Command getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public static EarFunPacket decode(ByteBuffer buf) {
|
||||
if (buf.remaining() < HEADER_LENGTH)
|
||||
return null;
|
||||
|
||||
if (buf.get() != START_OF_PACKET) {
|
||||
LOG.error("Invalid start of packet: {}", hexdump(buf.array()));
|
||||
return null;
|
||||
}
|
||||
|
||||
byte version = buf.get();
|
||||
if (!SUPPORTED_VERSIONS.contains(version)) {
|
||||
LOG.error("Invalid version: {} in packet {}", hexdumpValue(version), hexdump(buf.array()));
|
||||
return null;
|
||||
}
|
||||
|
||||
byte flags = buf.get();
|
||||
if (flags != FLAGS_NO_CHECKSUM) {
|
||||
LOG.error("Invalid flags: {} in packet {}", hexdumpValue(flags), hexdump(buf.array()));
|
||||
return null;
|
||||
}
|
||||
byte length = buf.get();
|
||||
|
||||
short vendorId = buf.getShort();
|
||||
if (!SUPPORTED_VENDOR_IDS.contains(vendorId)) {
|
||||
LOG.error("Invalid vendor ID: {} in packet {}", hexdumpValue(vendorId), hexdump(buf.array()));
|
||||
return null;
|
||||
}
|
||||
|
||||
short commandId = buf.getShort();
|
||||
Command command = Command.getCommandById(commandId);
|
||||
if (command == null) {
|
||||
LOG.error("Received unknown command ID: {} in packet {}", hexdumpValue(commandId), hexdump(buf.array()));
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] payload = new byte[length];
|
||||
buf.get(payload);
|
||||
|
||||
return new EarFunPacket(command, payload);
|
||||
}
|
||||
|
||||
public byte[] encode() {
|
||||
ByteBuffer buf = ByteBuffer.allocate(HEADER_LENGTH + payload.length);
|
||||
buf.put(START_OF_PACKET);
|
||||
buf.put(command.version);
|
||||
buf.put(FLAGS_NO_CHECKSUM);
|
||||
buf.put((byte) payload.length);
|
||||
buf.putShort(command.vendorId);
|
||||
buf.putShort(command.commandId);
|
||||
buf.put(payload);
|
||||
LOG.debug("encoded package: {}", hexdump(buf.array()));
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
public static String hexdumpValue(byte value) {
|
||||
return String.format("%02X", value & 0xFF);
|
||||
}
|
||||
|
||||
public static String hexdumpValue(short value) {
|
||||
return String.format("%04X", value & 0xFFFF);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EarFunGaiaPacket{" +
|
||||
" command=" + command.toString() +
|
||||
", length=" + payload.length +
|
||||
", payload=" + hexdump(payload) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Interactions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class EarFunPacketEncoder {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunPacketEncoder.class);
|
||||
|
||||
public static byte[] encodeBatteryReq() {
|
||||
return joinPackets(
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_LEFT).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_RIGHT).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_BATTERY_STATE_CASE).encode()
|
||||
);
|
||||
}
|
||||
|
||||
public static byte[] encodeSoundReq() {
|
||||
return joinPackets(
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_GAME_MODE).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_AMBIENT_SOUND).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_ANC_MODE).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TRANSPARENCY_MODE).encode()
|
||||
);
|
||||
}
|
||||
|
||||
public static byte[] encodeTouchActionReq() {
|
||||
return joinPackets(new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_TOUCH_ACTION).encode());
|
||||
}
|
||||
|
||||
public static byte[] encodeFirmwareVersionReq() {
|
||||
return new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_FIRMWARE_VERSION).encode();
|
||||
}
|
||||
|
||||
public static byte[] encodeSetGesture(Prefs prefs, String config, Interactions.InteractionType interactionType, Interactions.Position position) {
|
||||
byte action = (byte) (Integer.parseInt(prefs.getString(config, "0")) & 0xFF);
|
||||
byte[] payload;
|
||||
if (position == Interactions.Position.LEFT) {
|
||||
payload = new byte[]{interactionType.value, action, (byte) 0x00, (byte) 0x00};
|
||||
} else {
|
||||
payload = new byte[]{(byte) 0x00, (byte) 0x00, interactionType.value, action};
|
||||
}
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_TOUCH_ACTION, payload).encode();
|
||||
}
|
||||
|
||||
public static byte[] encodeSetEqualizerSixBands(Prefs prefs) {
|
||||
List<byte[]> equalizerConfig = Arrays.stream(Equalizer.SixBandEqualizer).map(bandConfig -> {
|
||||
if (bandConfig.key != null) {
|
||||
return encodeSetEqualizerBand((short) (prefs.getInt(bandConfig.key, 0) & 0xFFFF), bandConfig.band);
|
||||
} else {
|
||||
return encodeSetEqualizerBand(bandConfig.band.defaultGain, bandConfig.band);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return joinPackets(equalizerConfig);
|
||||
}
|
||||
|
||||
public static byte[] encodeSetEqualizerTenBands(Prefs prefs) {
|
||||
List<byte[]> equalizerConfig = Arrays.stream(Equalizer.TenBandEqualizer).map(bandConfig -> {
|
||||
if (bandConfig.key != null) {
|
||||
return encodeSetEqualizerBand((short) (prefs.getInt(bandConfig.key, 0) & 0xFFFF), bandConfig.band);
|
||||
} else {
|
||||
return encodeSetEqualizerBand(bandConfig.band.defaultGain, bandConfig.band);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
return joinPackets(equalizerConfig);
|
||||
}
|
||||
|
||||
public static byte[] encodeSetEqualizerBand(double gainValue, Equalizer.Band band) {
|
||||
short gain = (short) ((int) Math.round(gainValue * 60) & 0xFFFF);
|
||||
ByteBuffer buf = ByteBuffer.allocate(9);
|
||||
buf.put(band.bandId);
|
||||
buf.put((byte) 0xFF);
|
||||
buf.put((byte) 0x98);
|
||||
buf.putShort(band.frequency);
|
||||
buf.putShort(gain);
|
||||
buf.putShort(band.qFactor);
|
||||
byte[] payload = buf.array();
|
||||
LOG.debug("equalizer payload: {}", hexdump(payload));
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_EQUALIZER_BAND, payload).encode();
|
||||
}
|
||||
|
||||
public static byte[] joinPackets(List<byte[]> arrays) {
|
||||
int totalLength = arrays.stream().mapToInt(array -> array.length).sum();
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(totalLength);
|
||||
arrays.forEach(byteBuffer::put);
|
||||
return byteBuffer.array();
|
||||
}
|
||||
|
||||
public static byte[] joinPackets(byte[]... arrays) {
|
||||
return joinPackets(Arrays.asList(arrays));
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder.joinPackets;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunResponseHandler.*;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Interactions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class EarFunProtocol extends GBDeviceProtocol {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunProtocol.class);
|
||||
|
||||
@Override
|
||||
public GBDeviceEvent[] decodeResponse(byte[] data) {
|
||||
List<GBDeviceEvent> events = new ArrayList<>();
|
||||
ByteBuffer buf = ByteBuffer.wrap(data);
|
||||
|
||||
while (buf.hasRemaining()) {
|
||||
EarFunPacket packet = EarFunPacket.decode(buf);
|
||||
|
||||
if (packet == null) break;
|
||||
|
||||
EarFunPacket.Command command = packet.getCommand();
|
||||
byte[] payload = packet.getPayload();
|
||||
LOG.info("received {} {}", packet.getCommand().name(), packet);
|
||||
|
||||
switch (command) {
|
||||
case REQUEST_RESPONSE_BATTERY_STATE_LEFT:
|
||||
events.add(handleBatteryInfo(0, payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_BATTERY_STATE_RIGHT:
|
||||
events.add(handleBatteryInfo(1, payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_BATTERY_STATE_CASE:
|
||||
events.add(handleBatteryInfo(2, payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_FIRMWARE_VERSION:
|
||||
events.add(handleFirmwareVersionInfo(payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_GAME_MODE:
|
||||
events.add(handleGameModeInfo(payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_AMBIENT_SOUND:
|
||||
events.add(handleAmbientSoundInfo(payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_ANC_MODE:
|
||||
events.add(handleAncModeInfo(payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_TRANSPARENCY_MODE:
|
||||
events.add(handleTransparencyModeInfo(payload));
|
||||
break;
|
||||
case REQUEST_RESPONSE_TOUCH_ACTION:
|
||||
events.add(handleTouchActionInfo(payload));
|
||||
break;
|
||||
default:
|
||||
LOG.error("no handler for packet type {}", packet.getCommand().name());
|
||||
}
|
||||
}
|
||||
return events.toArray(new GBDeviceEvent[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeTestNewFunction() {
|
||||
return joinPackets(
|
||||
new EarFunPacket(EarFunPacket.Command.REQUEST_RESPONSE_0318).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0321).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0326).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_032C).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_032F).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0331).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0333).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0335).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0339).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034A).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034C).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_034D).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0348).encode(),
|
||||
new EarFunPacket(EarFunPacket.Command.UNIDENTIFIED_0350).encode()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeSendConfiguration(String config) {
|
||||
byte[] customConfiguration = encodeSendConfigurationCustomizer(config);
|
||||
if (customConfiguration != null) {
|
||||
return customConfiguration;
|
||||
}
|
||||
|
||||
Prefs prefs = getDevicePrefs();
|
||||
switch (config) {
|
||||
case PREF_EARFUN_AMBIENT_SOUND_CONTROL:
|
||||
int ambientSound = Integer.parseInt(prefs.getString(PREF_EARFUN_AMBIENT_SOUND_CONTROL, "0"));
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_AMBIENT_SOUND, (byte) ambientSound).encode();
|
||||
case PREF_EARFUN_SINGLE_TAP_LEFT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.SINGLE, Interactions.Position.LEFT);
|
||||
case PREF_EARFUN_SINGLE_TAP_RIGHT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.SINGLE, Interactions.Position.RIGHT);
|
||||
case PREF_EARFUN_DOUBLE_TAP_LEFT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.DOUBLE, Interactions.Position.LEFT);
|
||||
case PREF_EARFUN_DOUBLE_TAP_RIGHT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.DOUBLE, Interactions.Position.RIGHT);
|
||||
case PREF_EARFUN_TRIPPLE_TAP_LEFT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.TRIPLE, Interactions.Position.LEFT);
|
||||
case PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.TRIPLE, Interactions.Position.RIGHT);
|
||||
case PREF_EARFUN_LONG_TAP_LEFT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.LONG, Interactions.Position.LEFT);
|
||||
case PREF_EARFUN_LONG_TAP_RIGHT_ACTION:
|
||||
return EarFunPacketEncoder.encodeSetGesture(prefs, config, Interactions.InteractionType.LONG, Interactions.Position.RIGHT);
|
||||
case PREF_EARFUN_GAME_MODE:
|
||||
int gameMode = prefs.getBoolean(PREF_EARFUN_GAME_MODE, false) ? 1 : 0;
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_GAME_MODE, (byte) gameMode).encode();
|
||||
case PREF_EARFUN_DEVICE_NAME:
|
||||
String deviceName = prefs.getString(PREF_EARFUN_DEVICE_NAME, "");
|
||||
byte[] utf8EncodedName = deviceName.getBytes(StandardCharsets.UTF_8);
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_DEVICENAME, utf8EncodedName).encode();
|
||||
case PREF_EARFUN_ANC_MODE:
|
||||
byte ancMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_ANC_MODE, "0")) & 0xFF);
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_ANC_MODE, ancMode).encode();
|
||||
case PREF_EARFUN_TRANSPARENCY_MODE:
|
||||
byte transparencyMode = (byte) (Integer.parseInt(prefs.getString(PREF_EARFUN_TRANSPARENCY_MODE, "0")) & 0xFF);
|
||||
return new EarFunPacket(EarFunPacket.Command.SET_TRANSPARENCY_MODE, transparencyMode).encode();
|
||||
default:
|
||||
LOG.error("unhandled send configuration {}", config);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite this in derived classes to do some device specific custom handling of configurations
|
||||
*
|
||||
* @return the config, encoded as byte array
|
||||
*/
|
||||
public byte[] encodeSendConfigurationCustomizer(String config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] encodeBatteryReq() {
|
||||
return EarFunPacketEncoder.encodeBatteryReq();
|
||||
}
|
||||
|
||||
public byte[] encodeSoundReq() {
|
||||
return EarFunPacketEncoder.encodeSoundReq();
|
||||
}
|
||||
|
||||
public byte[] encodeTouchActionReq() {
|
||||
return EarFunPacketEncoder.encodeTouchActionReq();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] encodeFirmwareVersionReq() {
|
||||
return EarFunPacketEncoder.encodeFirmwareVersionReq();
|
||||
}
|
||||
|
||||
protected EarFunProtocol(GBDevice device) {
|
||||
super(device);
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_AMBIENT_SOUND_CONTROL;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_ANC_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_GAME_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TRANSPARENCY_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Interactions.interactionPrefs;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
|
||||
public class EarFunResponseHandler {
|
||||
|
||||
public static GBDeviceEvent handleBatteryInfo(int index, byte[] payload) {
|
||||
int batteryLevel = payload[1];
|
||||
GBDeviceEventBatteryInfo batteryInfo = new GBDeviceEventBatteryInfo();
|
||||
batteryInfo.batteryIndex = index;
|
||||
batteryInfo.level = (batteryLevel > 0) ? batteryLevel : GBDevice.BATTERY_UNKNOWN;
|
||||
batteryInfo.state = (batteryLevel > 0) ? BatteryState.BATTERY_NORMAL : BatteryState.UNKNOWN;
|
||||
return batteryInfo;
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleFirmwareVersionInfo(byte[] payload) {
|
||||
final GBDeviceEventVersionInfo versionInfo = new GBDeviceEventVersionInfo();
|
||||
String[] versionParts = (new String(payload, StandardCharsets.UTF_8)).split("_");
|
||||
versionInfo.fwVersion = versionParts[versionParts.length - 1];
|
||||
if (versionParts.length > 1) {
|
||||
versionInfo.hwVersion = String.join(" ", Arrays.copyOf(versionParts, versionParts.length - 1));
|
||||
} else {
|
||||
versionInfo.hwVersion = GBApplication.getContext().getString(R.string.n_a);
|
||||
}
|
||||
return versionInfo;
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleGameModeInfo(byte[] payload) {
|
||||
boolean gameMode = payload[1] == 0x01;
|
||||
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_GAME_MODE, gameMode);
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleAmbientSoundInfo(byte[] payload) {
|
||||
String ambientSoundMode = Integer.toString(payload[1]);
|
||||
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_AMBIENT_SOUND_CONTROL, ambientSoundMode);
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleAncModeInfo(byte[] payload) {
|
||||
String ancMode = Integer.toString(payload[1]);
|
||||
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_ANC_MODE, ancMode);
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleTransparencyModeInfo(byte[] payload) {
|
||||
String transparencyMode = Integer.toString(payload[1]);
|
||||
return new GBDeviceEventUpdatePreferences(PREF_EARFUN_TRANSPARENCY_MODE, transparencyMode);
|
||||
}
|
||||
|
||||
public static GBDeviceEvent handleTouchActionInfo(byte[] payload) {
|
||||
GBDeviceEventUpdatePreferences updateEvent = new GBDeviceEventUpdatePreferences();
|
||||
ByteBuffer buf = ByteBuffer.wrap(payload);
|
||||
buf.get();
|
||||
Arrays.stream(interactionPrefs).forEach(interactionType -> {
|
||||
// only use the last byte - the first byte initially contains some non zero data
|
||||
int action = buf.getShort() & 0xFF;
|
||||
updateEvent.preferences.put(interactionType, Integer.toString(action));
|
||||
});
|
||||
return updateEvent;
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
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;
|
||||
|
||||
public class EarFunSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
|
||||
final GBDevice device;
|
||||
|
||||
public EarFunSettingsCustomizer(final GBDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public static final Creator<EarFunSettingsCustomizer> CREATOR = new Creator<EarFunSettingsCustomizer>() {
|
||||
@Override
|
||||
public EarFunSettingsCustomizer createFromParcel(final Parcel in) {
|
||||
final GBDevice device = in.readParcelable(EarFunSettingsCustomizer.class.getClassLoader());
|
||||
return new EarFunSettingsCustomizer(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EarFunSettingsCustomizer[] newArray(final int size) {
|
||||
return new EarFunSettingsCustomizer[size];
|
||||
}
|
||||
};
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunSettingsCustomizer.class);
|
||||
|
||||
@Override
|
||||
public void onPreferenceChange(Preference preference, DeviceSpecificSettingsHandler handler) {
|
||||
if (preference.getKey().equals(PREF_EARFUN_AMBIENT_SOUND_CONTROL)) {
|
||||
ListPreference listPreferenceAmbientSound = handler.findPreference(PREF_EARFUN_AMBIENT_SOUND_CONTROL);
|
||||
ListPreference listPreferenceTransparencyMode = handler.findPreference(PREF_EARFUN_TRANSPARENCY_MODE);
|
||||
ListPreference listPreferenceAncMode = handler.findPreference(PREF_EARFUN_ANC_MODE);
|
||||
|
||||
if (listPreferenceAmbientSound == null || listPreferenceTransparencyMode == null || listPreferenceAncMode == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (listPreferenceAmbientSound.getValue()) {
|
||||
case "1": // noise cancelling
|
||||
listPreferenceTransparencyMode.setVisible(false);
|
||||
listPreferenceAncMode.setVisible(true);
|
||||
break;
|
||||
case "2": // transparency
|
||||
listPreferenceTransparencyMode.setVisible(true);
|
||||
listPreferenceAncMode.setVisible(false);
|
||||
break;
|
||||
default:
|
||||
listPreferenceTransparencyMode.setVisible(false);
|
||||
listPreferenceAncMode.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeSettings(DeviceSpecificSettingsHandler handler, Prefs prefs, String rootKey) {
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_DEVICE_NAME);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_AMBIENT_SOUND_CONTROL);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_TRANSPARENCY_MODE);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_ANC_MODE);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_SINGLE_TAP_LEFT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_SINGLE_TAP_RIGHT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_DOUBLE_TAP_LEFT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_DOUBLE_TAP_RIGHT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_TRIPPLE_TAP_LEFT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_LONG_TAP_LEFT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_LONG_TAP_RIGHT_ACTION);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_GAME_MODE);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_31_5);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_63);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_125);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_180);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_250);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_500);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_1000);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_2000);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_4000);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_8000);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_15000);
|
||||
handler.addPreferenceHandlerFor(PREF_EARFUN_EQUALIZER_BAND_16000);
|
||||
|
||||
EditTextPreference editTextDeviceName = handler.findPreference(PREF_EARFUN_DEVICE_NAME);
|
||||
if (editTextDeviceName != null) {
|
||||
editTextDeviceName.setOnBindEditTextListener(editText -> {
|
||||
InputFilter[] filters = new InputFilter[]{new InputFilterLength(25)};
|
||||
editText.setFilters(filters);
|
||||
});
|
||||
editTextDeviceName.setText(device.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getPreferenceKeysWithSummary() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel parcel, int i) {
|
||||
}
|
||||
|
||||
private static class InputFilterLength implements InputFilter {
|
||||
private final int maxLength;
|
||||
|
||||
public InputFilterLength(int maxLength) {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
int keep = maxLength - (dest.length() - (dend - dstart));
|
||||
if (keep <= 0) {
|
||||
return "";
|
||||
} else if (keep >= end - start) {
|
||||
return null;
|
||||
} else {
|
||||
return source.subSequence(start, start + keep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class EarFunAirPro4DeviceSupport extends EarFunDeviceSupport {
|
||||
|
||||
protected GBDeviceProtocol createDeviceProtocol() {
|
||||
return new EarFunAirPro4Protocol(getDevice());
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airpro4;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class EarFunAirPro4Protocol extends EarFunProtocol {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunAirPro4Protocol.class);
|
||||
|
||||
public byte[] encodeSendConfigurationCustomizer(String config) {
|
||||
|
||||
if (containsKey(Equalizer.TenBandEqualizer, config)) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
return EarFunPacketEncoder.encodeSetEqualizerTenBands(prefs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean containsKey(Equalizer.BandConfig[] array, String key) {
|
||||
return Arrays.stream(array)
|
||||
.anyMatch(element -> element.key.equals(key));
|
||||
}
|
||||
|
||||
protected EarFunAirPro4Protocol(GBDevice device) {
|
||||
super(device);
|
||||
DeviceType type = device.getType();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
|
||||
public class EarFunAirSDeviceSupport extends EarFunDeviceSupport {
|
||||
|
||||
protected GBDeviceProtocol createDeviceProtocol() {
|
||||
return new EarFunAirSProtocol(getDevice());
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.airs;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunPacketEncoder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.EarFunProtocol;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class EarFunAirSProtocol extends EarFunProtocol {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EarFunAirSProtocol.class);
|
||||
|
||||
public byte[] encodeSendConfigurationCustomizer(String config) {
|
||||
|
||||
if (containsKey(Equalizer.SixBandEqualizer, config)) {
|
||||
Prefs prefs = getDevicePrefs();
|
||||
return EarFunPacketEncoder.encodeSetEqualizerSixBands(prefs);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean containsKey(Equalizer.BandConfig[] array, String key) {
|
||||
return Arrays.stream(array)
|
||||
.anyMatch(element -> element.key.equals(key));
|
||||
}
|
||||
|
||||
protected EarFunAirSProtocol(GBDevice device) {
|
||||
super(device);
|
||||
DeviceType type = device.getType();
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs;
|
||||
|
||||
public class EarFunSettingsPreferenceConst {
|
||||
public static final String PREF_EARFUN_DEVICE_NAME = "pref_earfun_device_name";
|
||||
public static final String PREF_EARFUN_AMBIENT_SOUND_CONTROL = "pref_earfun_ambient_sound_control";
|
||||
public static final String PREF_EARFUN_TRANSPARENCY_MODE = "pref_earfun_transparency_mode";
|
||||
public static final String PREF_EARFUN_ANC_MODE = "pref_earfun_anc_mode";
|
||||
public static final String PREF_EARFUN_SINGLE_TAP_LEFT_ACTION = "pref_earfun_single_tap_left_action";
|
||||
public static final String PREF_EARFUN_SINGLE_TAP_RIGHT_ACTION = "pref_earfun_single_tap_right_action";
|
||||
public static final String PREF_EARFUN_DOUBLE_TAP_LEFT_ACTION = "pref_earfun_double_tap_left_action";
|
||||
public static final String PREF_EARFUN_DOUBLE_TAP_RIGHT_ACTION = "pref_earfun_double_tap_right_action";
|
||||
public static final String PREF_EARFUN_TRIPPLE_TAP_LEFT_ACTION = "pref_earfun_tripple_tap_left_action";
|
||||
public static final String PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION = "pref_earfun_tripple_tap_right_action";
|
||||
public static final String PREF_EARFUN_LONG_TAP_LEFT_ACTION = "pref_earfun_long_tap_left_action";
|
||||
public static final String PREF_EARFUN_LONG_TAP_RIGHT_ACTION = "pref_earfun_long_tap_right_action";
|
||||
public static final String PREF_EARFUN_GAME_MODE = "pref_earfun_game_mode";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_31_5 = "pref_earfun_equalizer_band_31.5";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_63 = "pref_earfun_equalizer_band_63";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_125 = "pref_earfun_equalizer_band_125";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_180 = "pref_earfun_equalizer_band_180";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_250 = "pref_earfun_equalizer_band_250";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_500 = "pref_earfun_equalizer_band_500";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_1000 = "pref_earfun_equalizer_band_1000";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_2000 = "pref_earfun_equalizer_band_2000";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_4000 = "pref_earfun_equalizer_band_4000";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_8000 = "pref_earfun_equalizer_band_8000";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_15000 = "pref_earfun_equalizer_band_15000";
|
||||
public static final String PREF_EARFUN_EQUALIZER_BAND_16000 = "pref_earfun_equalizer_band_16000";
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*;
|
||||
|
||||
public class Equalizer {
|
||||
public enum Band {
|
||||
SIX_BAND_63((byte) 0xA1, (short) 0x00BD, (short) 0x0B33),
|
||||
SIX_BAND_180((byte) 0xA2, (short) 0x021C, (short) 0x0999),
|
||||
SIX_BAND_500((byte) 0xA3, (short) 0x05DC, (short) 0x0999),
|
||||
SIX_BAND_1000((byte) 0xA4, (short) 0x0BB8, (short) 0x0999),
|
||||
SIX_BAND_8000((byte) 0xA5, (short) 0x5DC0, (short) 0x0999),
|
||||
SIX_BAND_15000((byte) 0xA6, (short) 0xAFC8, (short) 0x0999),
|
||||
// channels 7 - 10 are never used by the EarFun Air S, but they need to be set with dummy values
|
||||
SIX_BAND_DUMMY_SEVEN((byte) 0xA7, (short) 0x0063, (short) 0x0800, 4.5),
|
||||
SIX_BAND_DUMMY_EIGHT((byte) 0xA8, (short) 0x0258, (short) 0x0999, -4.5),
|
||||
SIX_BAND_DUMMY_NINE((byte) 0xA9, (short) 0x1770, (short) 0x0E66, 1.5),
|
||||
SIX_BAND_DUMMY_TEN((byte) 0xAA, (short) 0xAFC8, (short) 0x0999, 3.5),
|
||||
|
||||
TEN_BAND_31_5((byte) 0xA1, (short) 0x005E, (short) 0x0B33),
|
||||
TEN_BAND_63((byte) 0xA2, (short) 0x00BD, (short) 0x0B33),
|
||||
TEN_BAND_125((byte) 0xA3, (short) 0x0177, (short) 0x0B33),
|
||||
TEN_BAND_250((byte) 0xA4, (short) 0x02EE, (short) 0x0B33),
|
||||
TEN_BAND_500((byte) 0xA5, (short) 0x05DC, (short) 0x0B33),
|
||||
TEN_BAND_1000((byte) 0xA6, (short) 0x0BB8, (short) 0x0B33),
|
||||
TEN_BAND_2000((byte) 0xA7, (short) 0x1770, (short) 0x0B33),
|
||||
TEN_BAND_4000((byte) 0xA8, (short) 0x2EE0, (short) 0x0B33),
|
||||
TEN_BAND_8000((byte) 0xA9, (short) 0x5DC0, (short) 0x0B33),
|
||||
TEN_BAND_16000((byte) 0xAA, (short) 0xBB80, (short) 0x0B33);
|
||||
|
||||
public final byte bandId;
|
||||
public final short frequency;
|
||||
public final short qFactor;
|
||||
public final double defaultGain;
|
||||
|
||||
Band(byte bandId, short frequency, short qFactor) {
|
||||
this(bandId, frequency, qFactor, 0);
|
||||
}
|
||||
|
||||
Band(byte bandId, short frequency, short qFactor, double defaultGain) {
|
||||
this.bandId = bandId;
|
||||
this.frequency = frequency;
|
||||
this.qFactor = qFactor;
|
||||
this.defaultGain = defaultGain;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BandConfig {
|
||||
public BandConfig(Band band, String key) {
|
||||
this.band = band;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Band band;
|
||||
public String key;
|
||||
}
|
||||
|
||||
public static BandConfig[] SixBandEqualizer = {
|
||||
new BandConfig(Band.SIX_BAND_63, PREF_EARFUN_EQUALIZER_BAND_63),
|
||||
new BandConfig(Band.SIX_BAND_180, PREF_EARFUN_EQUALIZER_BAND_180),
|
||||
new BandConfig(Band.SIX_BAND_500, PREF_EARFUN_EQUALIZER_BAND_500),
|
||||
new BandConfig(Band.SIX_BAND_1000, PREF_EARFUN_EQUALIZER_BAND_1000),
|
||||
new BandConfig(Band.SIX_BAND_8000, PREF_EARFUN_EQUALIZER_BAND_8000),
|
||||
new BandConfig(Band.SIX_BAND_15000, PREF_EARFUN_EQUALIZER_BAND_15000),
|
||||
new BandConfig(Band.SIX_BAND_DUMMY_SEVEN, null),
|
||||
new BandConfig(Band.SIX_BAND_DUMMY_EIGHT, null),
|
||||
new BandConfig(Band.SIX_BAND_DUMMY_NINE, null),
|
||||
new BandConfig(Band.SIX_BAND_DUMMY_TEN, null),
|
||||
};
|
||||
|
||||
public static BandConfig[] TenBandEqualizer = {
|
||||
new BandConfig(Band.TEN_BAND_31_5, PREF_EARFUN_EQUALIZER_BAND_31_5),
|
||||
new BandConfig(Band.TEN_BAND_63, PREF_EARFUN_EQUALIZER_BAND_63),
|
||||
new BandConfig(Band.TEN_BAND_125, PREF_EARFUN_EQUALIZER_BAND_125),
|
||||
new BandConfig(Band.TEN_BAND_250, PREF_EARFUN_EQUALIZER_BAND_250),
|
||||
new BandConfig(Band.TEN_BAND_500, PREF_EARFUN_EQUALIZER_BAND_500),
|
||||
new BandConfig(Band.TEN_BAND_1000, PREF_EARFUN_EQUALIZER_BAND_1000),
|
||||
new BandConfig(Band.TEN_BAND_2000, PREF_EARFUN_EQUALIZER_BAND_2000),
|
||||
new BandConfig(Band.TEN_BAND_4000, PREF_EARFUN_EQUALIZER_BAND_4000),
|
||||
new BandConfig(Band.TEN_BAND_8000, PREF_EARFUN_EQUALIZER_BAND_8000),
|
||||
new BandConfig(Band.TEN_BAND_16000, PREF_EARFUN_EQUALIZER_BAND_16000),
|
||||
};
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.*;
|
||||
|
||||
public class Interactions {
|
||||
public enum InteractionType {
|
||||
SINGLE((byte) 0x01),
|
||||
DOUBLE((byte) 0x02),
|
||||
TRIPLE((byte) 0x03),
|
||||
LONG((byte) 0x11);
|
||||
|
||||
public final byte value;
|
||||
|
||||
InteractionType(byte value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
|
||||
public static String[] interactionPrefs = {
|
||||
PREF_EARFUN_SINGLE_TAP_LEFT_ACTION,
|
||||
PREF_EARFUN_SINGLE_TAP_RIGHT_ACTION,
|
||||
PREF_EARFUN_DOUBLE_TAP_LEFT_ACTION,
|
||||
PREF_EARFUN_DOUBLE_TAP_RIGHT_ACTION,
|
||||
PREF_EARFUN_TRIPPLE_TAP_LEFT_ACTION,
|
||||
PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION,
|
||||
PREF_EARFUN_LONG_TAP_LEFT_ACTION,
|
||||
PREF_EARFUN_LONG_TAP_RIGHT_ACTION
|
||||
};
|
||||
}
|
||||
@@ -3534,6 +3534,100 @@
|
||||
<item>6</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_ambient_sound_control_names">
|
||||
<item>@string/sony_ambient_sound_off</item>
|
||||
<item>@string/sony_ambient_sound_noise_cancelling</item>
|
||||
<item>@string/prefs_active_noise_cancelling_transparency</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_ambient_sound_control_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_transparency_mode_names">
|
||||
<item>@string/pref_default</item>
|
||||
<item>@string/earfun_transparency_mode_natural</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_transparency_mode_values">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_anc_mode_names">
|
||||
<item>@string/earfun_anc_mode_strong</item>
|
||||
<item>@string/earfun_anc_mode_balanced</item>
|
||||
<item>@string/earfun_anc_mode_ai_environment_adaptive</item>
|
||||
<item>@string/earfun_anc_mode_ai_ear_adaptive</item>
|
||||
<item>@string/earfun_anc_mode_wind_noise_cancelling</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_anc_mode_values">
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>4</item>
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_air_s_tap_action_names">
|
||||
<item>@string/none</item>
|
||||
<item>@string/pref_media_volumeup</item>
|
||||
<item>@string/pref_media_volumedown</item>
|
||||
<item>@string/pref_media_next</item>
|
||||
<item>@string/pref_media_previous</item>
|
||||
<item>@string/pref_media_playpause</item>
|
||||
<item>@string/prefs_game_mode</item>
|
||||
<item>@string/pref_title_touch_voice_assistant</item>
|
||||
<item>@string/prefs_ambient_mode</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_air_s_tap_action_values">
|
||||
<item>16</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>5</item>
|
||||
<item>4</item>
|
||||
<item>1</item>
|
||||
<item>7</item>
|
||||
<item>6</item>
|
||||
<item>8</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_air_pro_4_tap_action_names">
|
||||
<item>@string/none</item>
|
||||
<item>@string/pref_media_volumeup</item>
|
||||
<item>@string/pref_media_volumedown</item>
|
||||
<item>@string/pref_media_next</item>
|
||||
<item>@string/pref_media_previous</item>
|
||||
<item>@string/pref_media_playpause</item>
|
||||
<item>@string/prefs_game_mode</item>
|
||||
<item>@string/pref_title_touch_voice_assistant</item>
|
||||
<item>@string/earfun_tap_action_noise_control_all_modes</item>
|
||||
<item>@string/earfun_tap_action_noise_control_normal_wind</item>
|
||||
<item>@string/earfun_tap_action_noise_control_normal_ambient</item>
|
||||
<item>@string/earfun_tap_action_noise_control_wind_ambient</item>
|
||||
<item>@string/earfun_tap_action_redial</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="earfun_air_pro_4_tap_action_values">
|
||||
<item>16</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
<item>5</item>
|
||||
<item>4</item>
|
||||
<item>1</item>
|
||||
<item>7</item>
|
||||
<item>6</item>
|
||||
<item>8</item>
|
||||
<item>9</item>
|
||||
<item>10</item>
|
||||
<item>11</item>
|
||||
<item>13</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="sony_ambient_sound_control_wind_noise_reduction_names">
|
||||
<item>@string/sony_ambient_sound_off</item>
|
||||
<item>@string/sony_ambient_sound_noise_cancelling</item>
|
||||
|
||||
@@ -1956,6 +1956,8 @@
|
||||
<string name="devicetype_colmi_r09">Colmi R09</string>
|
||||
<string name="devicetype_colmi_r10">Colmi R10</string>
|
||||
<string name="devicetype_bandw_pseries">Bowers and Wilkins P series</string>
|
||||
<string name="devicetype_earfun_air_s">EarFun Air S</string>
|
||||
<string name="devicetype_earfun_air_pro_4">EarFun Air Pro 4</string>
|
||||
<string name="choose_auto_export_location">Choose export location</string>
|
||||
<string name="notification_channel_name">General</string>
|
||||
<string name="notification_channel_high_priority_name">High-priority</string>
|
||||
@@ -3703,4 +3705,17 @@
|
||||
<string name="gree_pair_status_success">Pairing success.\n\nBind key: %s</string>
|
||||
<string name="gree_pair_status_failure">Pairing failed: %s</string>
|
||||
<string name="gree_pair_clipboard">Device name: %s\nMac address: %s\nBind key: %s</string>
|
||||
<string name="earfun_transparency_mode">Transparency mode</string>
|
||||
<string name="earfun_transparency_mode_natural">Natural</string>
|
||||
<string name="earfun_anc_mode">ANC mode</string>
|
||||
<string name="earfun_anc_mode_strong">Strong ANC</string>
|
||||
<string name="earfun_anc_mode_balanced">Balanced ANC</string>
|
||||
<string name="earfun_anc_mode_ai_environment_adaptive">AI Environment Adaptive ANC</string>
|
||||
<string name="earfun_anc_mode_ai_ear_adaptive">AI Ear Adaptive ANC</string>
|
||||
<string name="earfun_anc_mode_wind_noise_cancelling">Wind Noise Cancelling</string>
|
||||
<string name="earfun_tap_action_noise_control_all_modes">Noise Control (All Modes)</string>
|
||||
<string name="earfun_tap_action_noise_control_normal_wind">Noise Control (Normal, Wind Noise)</string>
|
||||
<string name="earfun_tap_action_noise_control_normal_ambient">Noise Control (Normal, Ambient Sound)</string>
|
||||
<string name="earfun_tap_action_noise_control_wind_ambient">Noise Control (Wind Noise, Ambient Sound)</string>
|
||||
<string name="earfun_tap_action_redial">Redial</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_10_band_equalizer"
|
||||
android:persistent="false"
|
||||
android:title="@string/pref_header_equalizer">
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_31.5"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="31.5 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_63"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="63 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_125"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="125 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_250"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="250 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_500"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="500 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_1000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="1 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_2000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="2 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_4000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="4 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_8000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="8 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_16000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="16 kHz" />
|
||||
</PreferenceScreen>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,61 @@
|
||||
<?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">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_6_band_equalizer"
|
||||
android:persistent="false"
|
||||
android:title="@string/pref_header_equalizer">
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_63"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="63 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_180"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="180 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_500"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="500 Hz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_1000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="1 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_8000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="8 kHz" />
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:icon="@drawable/ic_graphic_eq"
|
||||
android:key="pref_earfun_equalizer_band_15000"
|
||||
android:max="6"
|
||||
app:min="-6"
|
||||
app:showSeekBarValue="true"
|
||||
android:title="15 kHz" />
|
||||
</PreferenceScreen>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_header_earfun_air_pro_4_system"
|
||||
android:title="@string/pref_header_system">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:key="pref_screen_earfun_touch_options"
|
||||
android:persistent="false"
|
||||
android:title="@string/prefs_galaxy_touch_options">
|
||||
|
||||
<PreferenceCategory android:title="@string/single_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_1"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_single_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_single_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/double_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_double_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_double_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/triple_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_tripple_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_tripple_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/long_press">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_long_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_pro_4_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_pro_4_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_long_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_header_earfun_air_pro_4_ambient_sound_control"
|
||||
android:title="@string/pref_header_sony_sound_control">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/earfun_ambient_sound_control_names"
|
||||
android:entryValues="@array/earfun_ambient_sound_control_values"
|
||||
android:icon="@drawable/ic_hearing"
|
||||
android:key="pref_earfun_ambient_sound_control"
|
||||
android:summary="%s"
|
||||
android:title="@string/pref_header_sony_ambient_sound_control" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/earfun_transparency_mode_names"
|
||||
android:entryValues="@array/earfun_transparency_mode_values"
|
||||
android:icon="@drawable/ic_hearing"
|
||||
android:key="pref_earfun_transparency_mode"
|
||||
android:summary="%s"
|
||||
android:title="@string/earfun_transparency_mode" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/earfun_anc_mode_names"
|
||||
android:entryValues="@array/earfun_anc_mode_values"
|
||||
android:icon="@drawable/ic_hearing"
|
||||
android:key="pref_earfun_anc_mode"
|
||||
android:summary="%s"
|
||||
android:title="@string/earfun_anc_mode" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:icon="@drawable/ic_videogame"
|
||||
android:key="pref_earfun_game_mode"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:summary="@string/prefs_game_mode_summary"
|
||||
android:title="@string/prefs_game_mode" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_header_earfun_air_s_system"
|
||||
android:title="@string/pref_header_system">
|
||||
|
||||
<PreferenceScreen
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:key="pref_screen_earfun_touch_options"
|
||||
android:persistent="false"
|
||||
android:title="@string/prefs_galaxy_touch_options">
|
||||
|
||||
<PreferenceCategory android:title="@string/single_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_1"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_single_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_single_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/double_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_2"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_double_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_double_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/triple_tap">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:icon="@drawable/ic_filter_3"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_tripple_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_tripple_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/long_press">
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:icon="@drawable/ic_touch"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_long_tap_left_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_left" />
|
||||
<ListPreference
|
||||
android:entries="@array/earfun_air_s_tap_action_names"
|
||||
android:entryValues="@array/earfun_air_s_tap_action_values"
|
||||
android:defaultValue="16"
|
||||
android:key="pref_earfun_long_tap_right_action"
|
||||
android:summary="%s"
|
||||
android:title="@string/prefs_right" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
</PreferenceCategory>
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceCategory
|
||||
android:key="pref_header_earfun_air_s_ambient_sound_control"
|
||||
android:title="@string/pref_header_sony_sound_control">
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="0"
|
||||
android:entries="@array/earfun_ambient_sound_control_names"
|
||||
android:entryValues="@array/earfun_ambient_sound_control_values"
|
||||
android:icon="@drawable/ic_hearing"
|
||||
android:key="pref_earfun_ambient_sound_control"
|
||||
android:summary="%s"
|
||||
android:title="@string/pref_header_sony_ambient_sound_control" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:icon="@drawable/ic_videogame"
|
||||
android:key="pref_earfun_game_mode"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:summary="@string/prefs_game_mode_summary"
|
||||
android:title="@string/prefs_game_mode" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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">
|
||||
<EditTextPreference
|
||||
android:icon="@drawable/ic_bluetooth"
|
||||
android:key="pref_earfun_device_name"
|
||||
android:title="@string/prefs_device_name"
|
||||
android:inputType="text"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Equalizer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.Interactions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class EarFunPacketEncoderTest {
|
||||
Prefs mockPrefs = mock(Prefs.class);
|
||||
|
||||
@Test
|
||||
public void testEncodeSetGesture() {
|
||||
when(mockPrefs.getString(anyString(), anyString())).thenReturn("1");
|
||||
|
||||
byte[] expected = new EarFunPacket(EarFunPacket.Command.SET_TOUCH_ACTION, new byte[]{1, 1, 0, 0}).encode();
|
||||
assertArrayEquals(expected, EarFunPacketEncoder.encodeSetGesture(mockPrefs, "anyKey",
|
||||
Interactions.InteractionType.SINGLE,
|
||||
Interactions.Position.LEFT)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSetEqualizerSixBands() {
|
||||
for (Equalizer.BandConfig bandConfig : Equalizer.SixBandEqualizer) {
|
||||
if (bandConfig.key != null) {
|
||||
when(mockPrefs.getInt(bandConfig.key, 0)).thenReturn(5);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] result = EarFunPacketEncoder.encodeSetEqualizerSixBands(mockPrefs);
|
||||
assertNotNull(result);
|
||||
assertEquals(170, result.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSetEqualizerTenBands() {
|
||||
for (Equalizer.BandConfig bandConfig : Equalizer.TenBandEqualizer) {
|
||||
if (bandConfig.key != null) {
|
||||
when(mockPrefs.getInt(bandConfig.key, 0)).thenReturn(-5);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] result = EarFunPacketEncoder.encodeSetEqualizerTenBands(mockPrefs);
|
||||
assertNotNull(result);
|
||||
assertEquals(170, result.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeBatteryReq() {
|
||||
byte[] encoded = EarFunPacketEncoder.encodeBatteryReq();
|
||||
|
||||
assertNotNull(encoded);
|
||||
assertEquals(24, encoded.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeSoundReq() {
|
||||
byte[] encoded = EarFunPacketEncoder.encodeSoundReq();
|
||||
|
||||
assertNotNull(encoded);
|
||||
assertEquals(32, encoded.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeTouchActionReq() {
|
||||
byte[] encoded = EarFunPacketEncoder.encodeTouchActionReq();
|
||||
|
||||
assertNotNull(encoded);
|
||||
assertEquals(8, encoded.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeFirmwareVersionReq() {
|
||||
byte[] encoded = EarFunPacketEncoder.encodeFirmwareVersionReq();
|
||||
|
||||
assertNotNull(encoded);
|
||||
assertEquals(8, encoded.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsMultipleArrays() {
|
||||
byte[] array1 = {1, 2, 3};
|
||||
byte[] array2 = {4, 5, 6};
|
||||
byte[] array3 = {7, 8, 9};
|
||||
byte[] expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets(array1, array2, array3);
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsEmptyArray() {
|
||||
byte[] array1 = {};
|
||||
byte[] array2 = {1, 2, 3};
|
||||
byte[] expected = {1, 2, 3};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets(array1, array2);
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsSingleArray() {
|
||||
byte[] array1 = {1, 2, 3};
|
||||
byte[] expected = {1, 2, 3};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets(array1);
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsNoArrays() {
|
||||
byte[] expected = {};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets();
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsDifferentLengths() {
|
||||
byte[] array1 = {1, 2};
|
||||
byte[] array2 = {3, 4, 5, 6};
|
||||
byte[] array3 = {7};
|
||||
byte[] expected = {1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets(array1, array2, array3);
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinPacketsListInput() {
|
||||
List<byte[]> arrays = Arrays.asList(new byte[]{1, 2}, new byte[]{3, 4, 5}, new byte[]{6, 7, 8, 9});
|
||||
byte[] expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
|
||||
|
||||
byte[] result = EarFunPacketEncoder.joinPackets(arrays);
|
||||
assertArrayEquals(expected, result);
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class EarFunPacketTest {
|
||||
@Test
|
||||
public void testEncode() {
|
||||
EarFunPacket.Command command = EarFunPacket.Command.SET_AMBIENT_SOUND;
|
||||
byte[] payload = {0x01, 0x02, 0x03};
|
||||
EarFunPacket packet = new EarFunPacket(command, payload);
|
||||
byte[] encoded = packet.encode();
|
||||
assertEquals((byte) 0xff, encoded[0]);
|
||||
assertEquals(EarFunPacket.DEFAULT_VERSION, encoded[1]);
|
||||
assertEquals(0x00, encoded[2]);
|
||||
assertEquals(payload.length, encoded[3]);
|
||||
assertEquals(EarFunPacket.DEFAULT_VENDOR_ID, ByteBuffer.wrap(encoded, 4, 2).getShort());
|
||||
assertEquals(command.commandId, ByteBuffer.wrap(encoded, 6, 2).getShort());
|
||||
byte[] decodedPayload = Arrays.copyOfRange(encoded, 8, encoded.length);
|
||||
assertArrayEquals(payload, decodedPayload);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecode() {
|
||||
byte[] payload = {0x01, 0x02, 0x03};
|
||||
ByteBuffer buffer = ByteBuffer.allocate(11)
|
||||
.put((byte) 0xff)
|
||||
.put(EarFunPacket.DEFAULT_VERSION)
|
||||
.put((byte) 0x00)
|
||||
.put((byte) payload.length)
|
||||
.putShort(EarFunPacket.DEFAULT_VENDOR_ID)
|
||||
.putShort(EarFunPacket.Command.REQUEST_CONFIGURATION.commandId)
|
||||
.put(payload);
|
||||
buffer.flip();
|
||||
|
||||
EarFunPacket packet = EarFunPacket.decode(buffer);
|
||||
|
||||
assertNotNull(packet);
|
||||
assertEquals(EarFunPacket.Command.REQUEST_CONFIGURATION, packet.getCommand());
|
||||
assertArrayEquals(payload, packet.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeInvalidPacket() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(2)
|
||||
.put((byte) 0xff)
|
||||
.put(EarFunPacket.DEFAULT_VERSION);
|
||||
buffer.flip();
|
||||
|
||||
EarFunPacket packet = EarFunPacket.decode(buffer);
|
||||
assertNull(packet);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommandById() {
|
||||
EarFunPacket.Command command = EarFunPacket.Command.getCommandById((short) 0x0001);
|
||||
|
||||
assertNotNull(command);
|
||||
assertEquals(EarFunPacket.Command.REQUEST_CONFIGURATION, command);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidCommandId() {
|
||||
EarFunPacket.Command command = EarFunPacket.Command.getCommandById((short) 0x1234);
|
||||
assertNull(command);
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_GAME_MODE;
|
||||
|
||||
public class EarFunProtocolTest {
|
||||
|
||||
private EarFunProtocol protocol;
|
||||
private GBDevice device;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
protocol = new EarFunProtocol(device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeResponse() {
|
||||
byte[] data = {(byte) 0xFF, 0x03, 0x00, 0x02, 0x00, 0x0A, 0x03, 0x07, 0x00, 0x64};
|
||||
|
||||
GBDeviceEvent[] events = protocol.decodeResponse(data);
|
||||
|
||||
assertNotNull(events);
|
||||
assertEquals(1, events.length);
|
||||
assertTrue(events[0] instanceof GBDeviceEventBatteryInfo);
|
||||
GBDeviceEventBatteryInfo batteryInfo = (GBDeviceEventBatteryInfo) events[0];
|
||||
assertEquals(1, batteryInfo.batteryIndex);
|
||||
assertEquals(100, batteryInfo.level);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.earfun;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_AMBIENT_SOUND_CONTROL;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_ANC_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_GAME_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_SINGLE_TAP_LEFT_ACTION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TRANSPARENCY_MODE;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.earfun.prefs.EarFunSettingsPreferenceConst.PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
|
||||
public class EarFunResponseHandlerTest {
|
||||
@Test
|
||||
public void testHandleBatteryInfo() {
|
||||
byte[] payload = new byte[]{0x00, 0x64};
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleBatteryInfo(1, payload);
|
||||
GBDeviceEventBatteryInfo batteryInfo = (GBDeviceEventBatteryInfo) event;
|
||||
assertEquals(1, batteryInfo.batteryIndex);
|
||||
assertEquals(100, batteryInfo.level);
|
||||
assertEquals(BatteryState.BATTERY_NORMAL, batteryInfo.state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleFirmwareVersionInfo() {
|
||||
byte[] payload = "twa200_20250225_3.4.8".getBytes(StandardCharsets.UTF_8);
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleFirmwareVersionInfo(payload);
|
||||
GBDeviceEventVersionInfo versionInfo = (GBDeviceEventVersionInfo) event;
|
||||
assertEquals("3.4.8", versionInfo.fwVersion);
|
||||
assertEquals("twa200 20250225", versionInfo.hwVersion);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleGameModeInfo() {
|
||||
byte[] payload = new byte[]{0x00, 0x01};
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleGameModeInfo(payload);
|
||||
GBDeviceEventUpdatePreferences updateEvent = (GBDeviceEventUpdatePreferences) event;
|
||||
assertTrue(updateEvent.preferences.containsKey(PREF_EARFUN_GAME_MODE));
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_GAME_MODE), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleAmbientSoundInfo() {
|
||||
byte[] payload = new byte[]{0x00, 0x02};
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleAmbientSoundInfo(payload);
|
||||
GBDeviceEventUpdatePreferences updateEvent = (GBDeviceEventUpdatePreferences) event;
|
||||
assertTrue(updateEvent.preferences.containsKey(PREF_EARFUN_AMBIENT_SOUND_CONTROL));
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_AMBIENT_SOUND_CONTROL), "2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleAncModeInfo() {
|
||||
byte[] payload = new byte[]{0x00, 0x04};
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleAncModeInfo(payload);
|
||||
GBDeviceEventUpdatePreferences updateEvent = (GBDeviceEventUpdatePreferences) event;
|
||||
assertTrue(updateEvent.preferences.containsKey(PREF_EARFUN_ANC_MODE));
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_ANC_MODE), "4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTransparencyModeInfo() {
|
||||
byte[] payload = new byte[]{0x00, 0x01};
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleTransparencyModeInfo(payload);
|
||||
GBDeviceEventUpdatePreferences updateEvent = (GBDeviceEventUpdatePreferences) event;
|
||||
assertTrue(updateEvent.preferences.containsKey(PREF_EARFUN_TRANSPARENCY_MODE));
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_TRANSPARENCY_MODE), "1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTouchActionInfo() {
|
||||
byte[] payload = ByteBuffer.allocate(18)
|
||||
.put((byte) 0x01)
|
||||
.putShort((short) 0x0001)
|
||||
.putShort((short) 0x0002)
|
||||
.putShort((short) 0x0003)
|
||||
.putShort((short) 0x0004)
|
||||
.putShort((short) 0x0005)
|
||||
.putShort((short) 0x0006)
|
||||
.putShort((short) 0x0007)
|
||||
.putShort((short) 0x0008)
|
||||
.array();
|
||||
GBDeviceEvent event = EarFunResponseHandler.handleTouchActionInfo(payload);
|
||||
GBDeviceEventUpdatePreferences updateEvent = (GBDeviceEventUpdatePreferences) event;
|
||||
assertEquals(8, updateEvent.preferences.size());
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_SINGLE_TAP_LEFT_ACTION), "1");
|
||||
assertEquals(updateEvent.preferences.get(PREF_EARFUN_TRIPPLE_TAP_RIGHT_ACTION), "6");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user