diff --git a/CHANGELOG.md b/CHANGELOG.md index 326c83453..79ac7bdf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ### Changelog +### {next} +* Support for Galaxy Buds 2019 + ### 0.61.0 * Initial support for Nothing Ear(1) * Amazfit Bip U/Pro: Fix flashing firmware and watchfaces diff --git a/app/src/main/assets/galaxy_buds.svg b/app/src/main/assets/galaxy_buds.svg new file mode 100644 index 000000000..1c3dd358b --- /dev/null +++ b/app/src/main/assets/galaxy_buds.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/ic_device_galaxy_buds.svg b/app/src/main/assets/ic_device_galaxy_buds.svg new file mode 100644 index 000000000..29adf6f78 --- /dev/null +++ b/app/src/main/assets/ic_device_galaxy_buds.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/assets/ic_device_galaxy_buds_disabled.svg b/app/src/main/assets/ic_device_galaxy_buds_disabled.svg new file mode 100644 index 000000000..4254aedfe --- /dev/null +++ b/app/src/main/assets/ic_device_galaxy_buds_disabled.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java index d13f86859..eb5a3fb13 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java @@ -98,6 +98,17 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_NOTHING_EAR1_INEAR = "pref_nothing_inear_detection"; public static final String PREF_NOTHING_EAR1_AUDIOMODE = "pref_nothing_audiomode"; + public static final String PREF_GALAXY_BUDS_AMBIENT_MODE = "pref_galaxy_buds_ambient_mode"; + public static final String PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS = "pref_galaxy_buds_ambient_voice_focus"; + public static final String PREF_GALAXY_BUDS_AMBIENT_VOLUME = "pref_galaxy_buds_ambient_volume"; + public static final String PREF_GALAXY_BUDS_LOCK_TOUCH = "pref_galaxy_buds_lock_touch"; + public static final String PREF_GALAXY_BUDS_GAME_MODE = "pref_galaxy_buds_game_mode"; + public static final String PREF_GALAXY_BUDS_EQUALIZER = "pref_galaxy_buds_equalizer"; + public static final String PREF_GALAXY_BUDS_EQUALIZER_DOLBY = "pref_galaxy_buds_equalizer_dolby"; + public static final String PREF_GALAXY_BUDS_EQUALIZER_MODE = "pref_galaxy_buds_equalizer_mode"; + public static final String PREF_GALAXY_BUDS_TOUCH_LEFT = "pref_galaxy_buds_touch_left"; + public static final String PREF_GALAXY_BUDS_TOUCH_RIGHT = "pref_galaxy_buds_touch_right"; + public static final String PREF_SOUNDS = "sounds"; public static final String PREF_AUTH_KEY = "authkey"; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index 71ee4bfbe..9a52525fa 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -72,6 +72,16 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_START; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FAKE_RING_DURATION; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_FIND_PHONE_ENABLED; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_MODE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_DOLBY; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_MODE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_GAME_MODE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_LOCK_TOUCH; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_LEFT; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_RIGHT; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_FORCE_WHITE_COLOR; @@ -464,6 +474,17 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat { addPreferenceHandlerFor(PREF_NOTHING_EAR1_INEAR); addPreferenceHandlerFor(PREF_NOTHING_EAR1_AUDIOMODE); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_AMBIENT_MODE); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_AMBIENT_VOLUME); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_LOCK_TOUCH); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_GAME_MODE); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_EQUALIZER); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_EQUALIZER_DOLBY); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_EQUALIZER_MODE); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_TOUCH_LEFT); + addPreferenceHandlerFor(PREF_GALAXY_BUDS_TOUCH_RIGHT); + String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF); boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/galaxy_buds/GalaxyBudsDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/galaxy_buds/GalaxyBudsDeviceCoordinator.java new file mode 100644 index 000000000..4188628b7 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/galaxy_buds/GalaxyBudsDeviceCoordinator.java @@ -0,0 +1,142 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.galaxy_buds; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class GalaxyBudsDeviceCoordinator extends AbstractDeviceCoordinator { + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + + String name = candidate.getName(); + + if (name != null && ( + name.startsWith("Galaxy Buds") + )) { + return DeviceType.GALAXY_BUDS; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.GALAXY_BUDS; + } + + //@Override + //public int getBatteryCount() { + // return 2; + //} + + @Nullable + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public SampleProvider getSampleProvider(GBDevice + device, DaoSession session) { + return null; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public int getAlarmSlotCount() { + return 0; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public String getManufacturer() { + return "Samsung"; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } + + @Override + public boolean supportsFindDevice() { + return true; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device + device, @NonNull DaoSession session) throws GBException { + + } + + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[]{ + R.xml.devicesettings_galaxy_buds, + }; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 9aa428005..01d60fe27 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -101,6 +101,7 @@ public enum DeviceType { UM25(350, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_um25), DOMYOS_T540(400, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_domyos_t540), NOTHING_EAR1(410, R.drawable.ic_device_nothingear, R.drawable.ic_device_nothingear_disabled, R.string.devicetype_nothingear1), + GALAXY_BUDS(420, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_galaxybuds), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index 39572d05e..ad8e5064e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.banglejs.BangleJSDev import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGB6900DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.casio.CasioGBX100DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.domyos.DomyosT540Support; +import nodomain.freeyourgadget.gadgetbridge.service.devices.galaxy_buds.GalaxyBudsDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.hplus.HPlusSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitband5.AmazfitBand5Support; @@ -362,6 +363,10 @@ public class DeviceSupportFactory { case NOTHING_EAR1: deviceSupport = new ServiceDeviceSupport(new Ear1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case GALAXY_BUDS: + deviceSupport = new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + } if (deviceSupport != null) { deviceSupport.setContext(gbDevice, mBtAdapter, mContext); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsDeviceSupport.java new file mode 100644 index 000000000..4dd3e18c2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsDeviceSupport.java @@ -0,0 +1,90 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.galaxy_buds; + +import android.net.Uri; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; +import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; + +public class GalaxyBudsDeviceSupport extends AbstractSerialDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(GalaxyBudsDeviceSupport.class); + + + @Override + public void onSendConfiguration(String config) { + super.onSendConfiguration(config); + } + + @Override + public void onSetAlarms(ArrayList alarms) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onSetConstantVibration(int integer) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onReadConfiguration(String config) { + + } + + @Override + public void onTestNewFunction() { + super.onTestNewFunction(); + } + + @Override + public boolean connect() { + getDeviceIOThread().start(); + return true; + } + + @Override + public synchronized GalaxyBudsIOThread getDeviceIOThread() { + return (GalaxyBudsIOThread) super.getDeviceIOThread(); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + protected GBDeviceProtocol createDeviceProtocol() { + return new GalaxyBudsProtocol(getDevice()); + } + + @Override + protected GBDeviceIoThread createDeviceIOThread() { + return new GalaxyBudsIOThread(getDevice(), getContext(), (GalaxyBudsProtocol) getDeviceProtocol(), + GalaxyBudsDeviceSupport.this, getBluetoothAdapter()); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsIOThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsIOThread.java new file mode 100644 index 000000000..53610d1e8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsIOThread.java @@ -0,0 +1,46 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.galaxy_buds; + +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 GalaxyBudsIOThread extends BtClassicIoThread { + private static final Logger LOG = LoggerFactory.getLogger(GalaxyBudsIOThread.class); + + private final GalaxyBudsProtocol galaxyBudsProtocol; + + @NonNull + protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) { + return galaxyBudsProtocol.UUID_DEVICE_CTRL; + } + + public GalaxyBudsIOThread(GBDevice device, Context context, GalaxyBudsProtocol deviceProtocol, + GalaxyBudsDeviceSupport galaxyBudsDeviceSupport, BluetoothAdapter bluetoothAdapter) { + super(device, context, deviceProtocol, galaxyBudsDeviceSupport, bluetoothAdapter); + galaxyBudsProtocol = deviceProtocol; + } + + @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); + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsProtocol.java new file mode 100644 index 000000000..1deaaef9f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/galaxy_buds/GalaxyBudsProtocol.java @@ -0,0 +1,248 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.galaxy_buds; + +import static nodomain.freeyourgadget.gadgetbridge.util.CheckSums.crc16_ccitt; +import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump; + +import android.content.SharedPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; + +public class GalaxyBudsProtocol extends GBDeviceProtocol { + private static final Logger LOG = LoggerFactory.getLogger(GalaxyBudsProtocol.class); + + final UUID UUID_DEVICE_CTRL = UUID.fromString("00001102-0000-1000-8000-00805f9b34fd"); + private static final byte SOM = (byte) 0xFE; + private static final byte EOM = (byte) 0xEE; + private boolean isFirstExchange = true; + + //incoming + private static final byte battery_status = (byte) 0x60; + private static final byte battery_status2 = (byte) 0x61; + + //outgoing + private static final byte find_device_start = (byte) 0xa0; + private static final byte find_device_stop = (byte) 0xa1; + + private static final byte set_ambient_mode = (byte) 0x80; //0x0/0x1 + private static final byte set_ambient_volume = (byte) 0x84; // 0x1-0x5 + private static final byte set_ambient_voice_focus = (byte) 0x85; // 0x0/0x1 + + private static final byte set_lock_touch = (byte) 0x90; // 0x0/0x1 + private static final byte set_game_mode = (byte) 0x87; // 0x0/0x2 no idea if this is doing anything + private static final byte set_equalizer = (byte) 0x86; // 0x0/0x1 + + private static final byte set_reset = (byte) 0x50; + + private static final byte set_touchpad_options = (byte) 0x92; + + private static final byte get_debug_build_info = (byte) 0x28; + private static final byte get_serial_number = (byte) 0x29; + private static final byte get_debug_get_all_data = (byte) 0x26; + private static final byte get_debug_get_version = (byte) 0x24; + + @Override + public GBDeviceEvent[] decodeResponse(byte[] responseData) { + List devEvts = new ArrayList<>(); + LOG.debug("received data: " + hexdump(responseData)); + LOG.debug("received data length: " + responseData.length); + + if (isFirstExchange) { + isFirstExchange = false; + devEvts.add(new GBDeviceEventVersionInfo()); //TODO: this is a weird hack to make the DBHelper happy. Replace with proper + detection + } + + ByteBuffer incoming = ByteBuffer.wrap(responseData); + incoming.order(ByteOrder.LITTLE_ENDIAN); + + byte sof = incoming.get(); + if (sof != SOM) { + LOG.error("Error in message, wrong start of frame: " + hexdump(responseData)); + return null; + } + byte type = incoming.get(); + int length = (int) (incoming.get() & 0xff); + byte message_id = incoming.get(); + byte[] payload; + try { + payload = Arrays.copyOfRange(responseData, incoming.position(), incoming.position() + length); + } catch (Exception e) { + LOG.error("Error getting payload data: " + length + " , " + e); + return null; + } + + switch (message_id) { + case battery_status: + devEvts.addAll(handleBatteryInfo(Arrays.copyOfRange(payload, 1, 3))); + break; + case battery_status2: + devEvts.addAll(handleBatteryInfo(Arrays.copyOfRange(payload, 2, 4))); + break; + default: + LOG.debug("Unhandled: " + hexdump(responseData)); + + } + return devEvts.toArray(new GBDeviceEvent[devEvts.size()]); + } + + + byte[] encodeMessage(byte command) { + ByteBuffer msgBuf = ByteBuffer.allocate(7); + msgBuf.order(ByteOrder.LITTLE_ENDIAN); + msgBuf.put(SOM); + msgBuf.put((byte) 0x0); //0x0 for sending + msgBuf.put((byte) 0x3); //size + msgBuf.put((byte) command); //command id + msgBuf.putShort((short) crc16_ccitt(new byte[]{command})); + msgBuf.put(EOM); + LOG.debug("DEBUG: " + hexdump(msgBuf.array())); + return msgBuf.array(); + } + + byte[] encodeMessage(byte command, byte parameter) { + ByteBuffer msgBuf = ByteBuffer.allocate(8); + msgBuf.order(ByteOrder.LITTLE_ENDIAN); + msgBuf.put(SOM); + msgBuf.put((byte) 0x0); //0x0 for sending + msgBuf.put((byte) 0x4); //size + msgBuf.put((byte) command); //command id + msgBuf.put((byte) parameter); + msgBuf.putShort((short) crc16_ccitt(new byte[]{command, parameter})); + msgBuf.put(EOM); + LOG.debug("DEBUG: " + hexdump(msgBuf.array())); + return msgBuf.array(); + } + + byte[] encodeMessage(byte command, byte parameter, byte value) { + ByteBuffer msgBuf = ByteBuffer.allocate(9); + msgBuf.order(ByteOrder.LITTLE_ENDIAN); + msgBuf.put(SOM); + msgBuf.put((byte) 0x0); //0x0 for sending + msgBuf.put((byte) 0x5); //size + msgBuf.put((byte) command); + msgBuf.put((byte) parameter); + msgBuf.put((byte) value); + msgBuf.putShort((short) crc16_ccitt(new byte[]{command, parameter, value})); + msgBuf.put(EOM); + LOG.debug("DEBUG: " + hexdump(msgBuf.array())); + return msgBuf.array(); + } + + @Override + public byte[] encodeFindDevice(boolean start) { + byte command = (byte) (start ? find_device_start : find_device_stop); + return encodeMessage(command); + } + + @Override + public byte[] encodeReset(int reset) { + if (reset == RESET_FLAGS_FACTORY_RESET) { + return encodeMessage(set_reset); + } + return null; + } + + @Override + public byte[] encodeTestNewFunction() { + //return encodeMessage(get_debug_build_info); + return null; + } + + @Override + public byte[] encodeSendConfiguration(String config) { + + SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); + + switch (config) { + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_MODE: + byte enable_ambient = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_MODE, false) ? 0x01 : 0x00); + return encodeMessage(set_ambient_mode, enable_ambient); + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS: + byte enable_voice = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOICE_FOCUS, false) ? 0x01 : 0x00); + return encodeMessage(set_ambient_voice_focus, enable_voice); + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME: + String ambient_volume = prefs.getString(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME, "1"); + byte ambient_volume_byte = (byte) Integer.parseInt(ambient_volume); + return encodeMessage(set_ambient_volume, ambient_volume_byte); + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_LOCK_TOUCH: + byte set_lock = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_LOCK_TOUCH, false) ? 0x01 : 0x00); + return encodeMessage(set_lock_touch, set_lock); + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_GAME_MODE: + byte game_mode = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_GAME_MODE, false) ? 0x2 : 0x00); + return encodeMessage(set_game_mode, game_mode); + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER: + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_DOLBY: + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_MODE: + byte equalizer = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER, false) ? 0x1 : 0x00); + boolean equalizer_dolby = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_DOLBY, false); + int dolby = 0; + if (equalizer_dolby) { + dolby = 5; + } + String equalizer_mode = prefs.getString(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_EQUALIZER_MODE, "0"); + byte mode = (byte) (Integer.parseInt(equalizer_mode) + dolby); + return encodeMessage(set_equalizer, equalizer, mode); + + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_LEFT: + case DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_RIGHT: + String touch_left = prefs.getString(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_LEFT, "1"); + String touch_right = prefs.getString(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_TOUCH_RIGHT, "1"); + byte touchmode_left = (byte) Integer.parseInt(touch_left); + byte touchmode_right = (byte) Integer.parseInt(touch_right); + return encodeMessage(set_touchpad_options, touchmode_left, touchmode_right); + + default: + LOG.debug("CONFIG: " + config); + } + return super.encodeSendConfiguration(config); + } + + + private List handleBatteryInfo(byte[] payload) { + List deviceEvents = new ArrayList<>(); + LOG.debug("Battery payload: " + hexdump(payload)); + LOG.debug("pl: " + payload.length); + LOG.debug("p0: " + payload[0]); + LOG.debug("p1: " + payload[1]); + + int batteryLevel1 = payload[0]; + int batteryLevel2 = payload[1]; + + GBDeviceEventBatteryInfo evBattery1 = new GBDeviceEventBatteryInfo(); + //evBattery1.batteryIndex = 0; + evBattery1.level = GBDevice.BATTERY_UNKNOWN; + evBattery1.level = (batteryLevel1 > 0) ? (short) batteryLevel1 : GBDevice.BATTERY_UNKNOWN; + evBattery1.state = (batteryLevel1 > 0) ? BatteryState.BATTERY_NORMAL : BatteryState.UNKNOWN; + deviceEvents.add(evBattery1); + + GBDeviceEventBatteryInfo evBattery2 = new GBDeviceEventBatteryInfo(); + //evBattery2.batteryIndex = 1; + evBattery2.level = GBDevice.BATTERY_UNKNOWN; + evBattery2.level = (batteryLevel2 > 0) ? (short) batteryLevel2 : GBDevice.BATTERY_UNKNOWN; + evBattery2.state = (batteryLevel2 > 0) ? BatteryState.BATTERY_NORMAL : BatteryState.UNKNOWN; + //deviceEvents.add(evBattery2); + + return deviceEvents; + } + + protected GalaxyBudsProtocol(GBDevice device) { + super(device); + + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java index a21d5420e..166cc9b01 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java @@ -84,6 +84,7 @@ public class Ear1Support extends AbstractSerialDeviceSupport { @Override protected GBDeviceIoThread createDeviceIOThread() { - return new NothingIOThread(getDevice(), getContext(), (NothingProtocol) getDeviceProtocol(), Ear1Support.this, getBluetoothAdapter()); + return new NothingIOThread(getDevice(), getContext(), (NothingProtocol) getDeviceProtocol(), + Ear1Support.this, getBluetoothAdapter()); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java index 0c803be4e..415ac4972 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java @@ -29,7 +29,8 @@ public class NothingIOThread extends BtClassicIoThread { return mNothingProtocol.UUID_DEVICE_CTRL; } - public NothingIOThread(GBDevice device, Context context, NothingProtocol deviceProtocol, Ear1Support ear1Support, BluetoothAdapter bluetoothAdapter) { + public NothingIOThread(GBDevice device, Context context, NothingProtocol deviceProtocol, + Ear1Support ear1Support, BluetoothAdapter bluetoothAdapter) { super(device, context, deviceProtocol, ear1Support, bluetoothAdapter); mNothingProtocol = deviceProtocol; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java index 28c91189c..9b25a994e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java @@ -115,4 +115,39 @@ public class CheckSums { } return out.toByteArray(); } + + // https://github.com/ThePBone/GalaxyBudsClient/blob/master/GalaxyBudsClient/Utils/CRC16.cs + + private static int[] Crc16Tab = + {0, 4129, 8258, 12387, 16516, 20645, 24774, 28903, 33032, 37161, 41290, + 45419, 49548, 53677, 57806, 61935, 4657, 528, 12915, 8786, 21173, 17044, 29431, 25302, + 37689, 33560, 45947, 41818, 54205, 50076, 62463, 58334, 9314, 13379, 1056, 5121, 25830, + 29895, 17572, 21637, 42346, 46411, 34088, 38153, 58862, 62927, 50604, 54669, 13907, + 9842, 5649, 1584, 30423, 26358, 22165, 18100, 46939, 42874, 38681, 34616, 63455, 59390, + 55197, 51132, 18628, 22757, 26758, 30887, 2112, 6241, 10242, 14371, 51660, 55789, + 59790, 63919, 35144, 39273, 43274, 47403, 23285, 19156, 31415, 27286, 6769, 2640, + 14899, 10770, 56317, 52188, 64447, 60318, 39801, 35672, 47931, 43802, 27814, 31879, + 19684, 23749, 11298, 15363, 3168, 7233, 60846, 64911, 52716, 56781, 44330, 48395, + 36200, 40265, 32407, 28342, 24277, 20212, 15891, 11826, 7761, 3696, 65439, 61374, + 57309, 53244, 48923, 44858, 40793, 36728, 37256, 33193, 45514, 41451, 53516, 49453, + 61774, 57711, 4224, 161, 12482, 8419, 20484, 16421, 28742, 24679, 33721, 37784, 41979, + 46042, 49981, 54044, 58239, 62302, 689, 4752, 8947, 13010, 16949, 21012, 25207, 29270, + 46570, 42443, 38312, 34185, 62830, 58703, 54572, 50445, 13538, 9411, 5280, 1153, 29798, + 25671, 21540, 17413, 42971, 47098, 34713, 38840, 59231, 63358, 50973, 55100, 9939, + 14066, 1681, 5808, 26199, 30326, 17941, 22068, 55628, 51565, 63758, 59695, 39368, + 35305, 47498, 43435, 22596, 18533, 30726, 26663, 6336, 2273, 14466, 10403, 52093, + 56156, 60223, 64286, 35833, 39896, 43963, 48026, 19061, 23124, 27191, 31254, 2801, + 6864, 10931, 14994, 64814, 60687, 56684, 52557, 48554, 44427, 40424, 36297, 31782, + 27655, 23652, 19525, 15522, 11395, 7392, 3265, 61215, 65342, 53085, 57212, 44955, + 49082, 36825, 40952, 28183, 32310, 20053, 24180, 11923, 16050, 3793, 7920}; + + // // https://github.com/ThePBone/GalaxyBudsClient/blob/master/GalaxyBudsClient/Utils/CRC16.cs + public static int crc16_ccitt(byte[] data) { + + int i2 = 0; + for (int i3 = 0; i3 < data.length; i3++) + i2 = Crc16Tab[((i2 >> 8) ^ data[i3]) & 255] ^ (i2 << 8); + + return 65535 & i2; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 431df5ccf..4ea75b6c6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -49,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSCoordinator import nodomain.freeyourgadget.gadgetbridge.devices.casio.gb6900.CasioGB6900DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.casio.gbx100.CasioGBX100DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.domyos.DomyosT540Cooridnator; +import nodomain.freeyourgadget.gadgetbridge.devices.galaxy_buds.GalaxyBudsDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator; @@ -308,6 +309,7 @@ public class DeviceHelper { result.add(new DomyosT540Cooridnator()); result.add(new FitProDeviceCoordinator()); result.add(new Ear1Coordinator()); + result.add(new GalaxyBudsDeviceCoordinator()); return result; } diff --git a/app/src/main/res/drawable/ic_device_galaxy_buds.xml b/app/src/main/res/drawable/ic_device_galaxy_buds.xml new file mode 100644 index 000000000..65e2585b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_galaxy_buds.xml @@ -0,0 +1,38 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_device_galaxy_buds_disabled.xml b/app/src/main/res/drawable/ic_device_galaxy_buds_disabled.xml new file mode 100644 index 000000000..830d4657e --- /dev/null +++ b/app/src/main/res/drawable/ic_device_galaxy_buds_disabled.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_equalizer.xml b/app/src/main/res/drawable/ic_equalizer.xml new file mode 100644 index 000000000..5ff52042d --- /dev/null +++ b/app/src/main/res/drawable/ic_equalizer.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_graphic_eq.xml b/app/src/main/res/drawable/ic_graphic_eq.xml new file mode 100644 index 000000000..c650a0849 --- /dev/null +++ b/app/src/main/res/drawable/ic_graphic_eq.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_hearing.xml b/app/src/main/res/drawable/ic_hearing.xml new file mode 100644 index 000000000..7a0167232 --- /dev/null +++ b/app/src/main/res/drawable/ic_hearing.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_music_note.xml b/app/src/main/res/drawable/ic_music_note.xml new file mode 100644 index 000000000..0e1b03e47 --- /dev/null +++ b/app/src/main/res/drawable/ic_music_note.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_touch.xml b/app/src/main/res/drawable/ic_touch.xml new file mode 100644 index 000000000..e93d7f975 --- /dev/null +++ b/app/src/main/res/drawable/ic_touch.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_videogame.xml b/app/src/main/res/drawable/ic_videogame.xml new file mode 100644 index 000000000..c50c195af --- /dev/null +++ b/app/src/main/res/drawable/ic_videogame.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_voice.xml b/app/src/main/res/drawable/ic_voice.xml new file mode 100644 index 000000000..85151bcde --- /dev/null +++ b/app/src/main/res/drawable/ic_voice.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/drawable/ic_volume_up.xml b/app/src/main/res/drawable/ic_volume_up.xml new file mode 100644 index 000000000..88bd805db --- /dev/null +++ b/app/src/main/res/drawable/ic_volume_up.xml @@ -0,0 +1,3 @@ + + + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 2532882fe..7f9e02e9c 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1601,6 +1601,13 @@ FORWARD REWIND BROADCAST + Voice Assistant + Quick Ambient Sound + Volume + Ambient Sound + Spotify + Other Left + Other Right @string/pref_button_action_disabled @@ -1788,5 +1795,44 @@ false + + 1 + 2 + 3 + 4 + 5 + + + + @string/pref_title_equalizer_bass_boost + @string/pref_title_equalizer_soft + @string/pref_title_equalizer_dynamic + @string/pref_title_equalizer_clear + @string/pref_title_equalizer_trebble + + + + 0 + 1 + 2 + 3 + 4 + + + + + @string/pref_title_touch_quick_ambient + + @string/pref_title_touch_ambient + + + + + + 1 + + 3 + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f71c26433..779dc62b4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1282,7 +1282,32 @@ Take measurements during sleep Frequency of measurements Nothing Ear (1) + Galaxy Buds Play/pause the music depending if you wear the earbuds In-Ear detection Audio mode + Equalizer Preset + Bass boost + Soft + Dynamic + Clear + Treble boost + Dolby Mode + Equalizer + Enable or disable equalizer + Dolby preset for equalizer + Game mode + Only if your phone supports game mode + Touch Lock + Disable touch events + Experimental + Ambient volume + pref_galaxy_buds_ambient_volume + Voice Focus + Make voice stand out + Ambient Sound + Ambient Mode + Left + Right + Touch Options diff --git a/app/src/main/res/xml/devicesettings_galaxy_buds.xml b/app/src/main/res/xml/devicesettings_galaxy_buds.xml new file mode 100644 index 000000000..d42db9f1f --- /dev/null +++ b/app/src/main/res/xml/devicesettings_galaxy_buds.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +