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 2e7c2619c..6ee6769e5 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 @@ -106,6 +106,11 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_DEVICE_INTERNET_ACCESS = "device_internet_access"; public static final String PREF_DEVICE_INTENTS = "device_intents"; + public static final String PREF_ACTIVE_NOISE_CANCELLING_TOGGLE = "active_noise_cancelling_toggle"; + public static final String PREF_BANDW_PSERIES_VPT_ENABLED = "bandw_pseries_vpt_enabled"; + public static final String PREF_BANDW_PSERIES_VPT_LEVEL = "bandw_pseries_vpt_level"; + public static final String PREF_BANDW_PSERIES_GUI_VPT_LEVEL = "bandw_pseries_gui_vpt_level"; + public static final String PREF_BANGLEJS_TEXT_BITMAP = "banglejs_text_bitmap"; public static final String PREF_BANGLEJS_TEXT_BITMAP_SIZE = "banglejs_txt_bitmap_size"; public static final String PREF_BANGLEJS_WEBVIEW_URL = "banglejs_webview_url"; 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 ec5b491ac..76039bf91 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 @@ -611,6 +611,9 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i addPreferenceHandlerFor(PREF_SLEEP_MODE_SLEEP_SCREEN); addPreferenceHandlerFor(PREF_SLEEP_MODE_SMART_ENABLE); + addPreferenceHandlerFor(PREF_ACTIVE_NOISE_CANCELLING_TOGGLE); + addPreferenceHandlerFor(PREF_BANDW_PSERIES_GUI_VPT_LEVEL); + addPreferenceHandlerFor(PREF_HYBRID_HR_DRAW_WIDGET_CIRCLES); addPreferenceHandlerFor(PREF_HYBRID_HR_FORCE_WHITE_COLOR); addPreferenceHandlerFor(PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bandwpseries/BandWPSeriesDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bandwpseries/BandWPSeriesDeviceCoordinator.java index 46d65b1ae..7ceefd7df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bandwpseries/BandWPSeriesDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/bandwpseries/BandWPSeriesDeviceCoordinator.java @@ -5,7 +5,7 @@ import androidx.annotation.NonNull; import java.util.regex.Pattern; import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; @@ -13,7 +13,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.BatteryConfig; import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.bandwpseries.BandWPSeriesDeviceSupport; -public class BandWPSeriesDeviceCoordinator extends AbstractDeviceCoordinator { +public class BandWPSeriesDeviceCoordinator extends AbstractBLEDeviceCoordinator { @Override public int getDeviceNameResource() { return R.string.devicetype_bandw_pseries; @@ -51,5 +51,12 @@ public class BandWPSeriesDeviceCoordinator extends AbstractDeviceCoordinator { return new BatteryConfig[]{battery0, battery1, battery2}; } + @Override + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { + return new int[] { + R.xml.devicesettings_active_noise_cancelling_toggle, + R.xml.devicesettings_bandw_pseries + }; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWBLEProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWBLEProfile.java index 2acb24252..a612021cf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWBLEProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWBLEProfile.java @@ -18,12 +18,18 @@ public class BandWBLEProfile extends Abstra public static final String ACTION_DEVICE_INFO = ACTION_PREFIX + "DEVICE_INFO"; public static final String EXTRA_DEVICE_INFO = "DEVICE_INFO"; + public static final byte ANC_MODE_OFF = 0x01; + public static final byte ANC_MODE_ON = 0x03; + public static final UUID UUID_RPC_REQUEST_CHARACTERISTIC = UUID.fromString("ada50ce9-67b8-4a97-9d8e-37e1d083156c"); public BandWBLEProfile(final T support) { super(support); } + public void requestAncModeState(final TransactionBuilder builder) { + sendRequest(builder, (byte) 0x03, (byte) 0x01); + } public void requestDeviceName(final TransactionBuilder builder) { sendRequest(builder, (byte) 0x05, (byte) 0x01); } @@ -36,6 +42,29 @@ public class BandWBLEProfile extends Abstra sendRequest(builder, (byte) 0x08, (byte) 0x17); } + public void requestVptEnabled(final TransactionBuilder builder) { + sendRequest(builder, (byte) 0x03, (byte) 0x05); + } + + public void requestVptLevel(final TransactionBuilder builder) { + sendRequest(builder, (byte) 0x03, (byte) 0x03); + } + + public void setAncModeState(final TransactionBuilder builder, final boolean mode) throws IOException { + BandWPSeriesRequest req = new BandWPSeriesRequest((byte) 0x03, (byte) 0x02).addToPayload(mode ? ANC_MODE_ON : ANC_MODE_OFF); + builder.write(getCharacteristic(UUID_RPC_REQUEST_CHARACTERISTIC), req.finishAndGetBytes()); + } + + public void setVptLevel(final TransactionBuilder builder, final int level) throws IOException { + BandWPSeriesRequest req = new BandWPSeriesRequest((byte) 0x03, (byte) 0x04).addToPayload(level); + builder.write(getCharacteristic(UUID_RPC_REQUEST_CHARACTERISTIC), req.finishAndGetBytes()); + } + + public void setVptEnabled(final TransactionBuilder builder, final boolean mode) throws IOException { + BandWPSeriesRequest req = new BandWPSeriesRequest((byte) 0x03, (byte) 0x06).addToPayload(mode); + builder.write(getCharacteristic(UUID_RPC_REQUEST_CHARACTERISTIC), req.finishAndGetBytes()); + } + private void sendRequest(final TransactionBuilder builder, byte namespace, byte commandID) { BandWPSeriesRequest req; try { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesDeviceSupport.java index 23cb6d68e..d81f89b65 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesDeviceSupport.java @@ -1,15 +1,25 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.bandwpseries; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ACTIVE_NOISE_CANCELLING_TOGGLE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BANDW_PSERIES_GUI_VPT_LEVEL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BANDW_PSERIES_VPT_ENABLED; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BANDW_PSERIES_VPT_LEVEL; import static nodomain.freeyourgadget.gadgetbridge.impl.GBDevice.BATTERY_UNKNOWN; +import static nodomain.freeyourgadget.gadgetbridge.service.devices.bandwpseries.BandWBLEProfile.ANC_MODE_ON; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGatt; +import android.content.SharedPreferences.Editor; +import android.widget.Toast; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.Arrays; import java.util.UUID; +import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; @@ -18,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.util.GB; public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { @@ -25,6 +36,7 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { private static final UUID UUID_RPC_SERVICE = UUID.fromString("85ba93a5-09ac-439a-8cc4-1c3f0cb4f29f"); private static final UUID UUID_RPC_RESPONSE_CHARACTERISTIC = UUID.fromString("cb909093-3559-4b0c-9a7f-3f1773122fdc"); + private static final UUID UUID_RPC_NOTIFICATION_CHARACTERISTIC = UUID.fromString("df55d475-9a32-457a-9e20-38cf14e853fb"); private final BandWBLEProfile BandWBLEProfile; private final GBDeviceEventBatteryInfo[] batteryInfo = new GBDeviceEventBatteryInfo[3]; @@ -59,9 +71,13 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); builder.notify(getCharacteristic(UUID_RPC_RESPONSE_CHARACTERISTIC), true); + builder.notify(getCharacteristic(UUID_RPC_NOTIFICATION_CHARACTERISTIC), true); BandWBLEProfile.requestFirmware(builder); BandWBLEProfile.requestDeviceName(builder); BandWBLEProfile.requestBatteryLevels(builder); + BandWBLEProfile.requestAncModeState(builder); + BandWBLEProfile.requestVptEnabled(builder); + BandWBLEProfile.requestVptLevel(builder); return builder; } @@ -70,7 +86,7 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { UUID characteristicUUID = characteristic.getUuid(); - if (UUID_RPC_RESPONSE_CHARACTERISTIC.equals(characteristicUUID)) { + if (UUID_RPC_RESPONSE_CHARACTERISTIC.equals(characteristicUUID) || UUID_RPC_NOTIFICATION_CHARACTERISTIC.equals(characteristicUUID)) { return handleRPCResponse(characteristic); } return false; @@ -91,6 +107,20 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { if (response.commandId == 0x01) { return handleFirmwareVersionResponse(response); } + } else if (response.namespace == 0x03) { + switch (response.commandId) { + case 0x01: + return handleGetAncModeStateResponse(response); + case 0x02: + case 0x04: + return getIntResponseStatus(response); + case 0x03: + return handleGetVptLevelResponse(response); + case 0x05: + return handleGetVptEnabledResponse(response); + case 0x06: + return getBooleanResponseStatus(response); + } } else if (response.namespace == 0x05) { if (response.commandId == 0x01) { return handleDeviceNameResponse(response); @@ -103,6 +133,24 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { return true; } + private boolean handleGetAncModeStateResponse(BandWPSeriesResponse response) { + if (!response.messageType.hasPayload) { + GB.toast("No payload in response!", Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + int payloadValue; + try { + payloadValue = response.payloadUnpacker.unpackInt(); + } catch (IOException e) { + GB.toast("Could not extract ancMode from payload: " + Arrays.toString(response.payload), Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + Editor editor = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).edit(); + editor.putBoolean(PREF_ACTIVE_NOISE_CANCELLING_TOGGLE, payloadValue == ANC_MODE_ON); + editor.apply(); + return true; + } + private boolean handleBatteryLevels(BandWPSeriesResponse response) { int[] levels = response.getPayloadFixArray(); if (levels == null) { @@ -146,8 +194,92 @@ public class BandWPSeriesDeviceSupport extends AbstractBTLEDeviceSupport { return true; } + private boolean handleGetVptEnabledResponse(BandWPSeriesResponse response) { + if (!response.messageType.hasPayload) { + GB.toast("No payload in response!", Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + boolean payloadValue; + try { + payloadValue = response.payloadUnpacker.unpackBoolean(); + } catch (IOException e) { + GB.toast("Could not extract vptEnabled from payload: " + Arrays.toString(response.payload), Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + int vptLevel = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getInt(PREF_BANDW_PSERIES_VPT_LEVEL, 0); + Editor editor = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).edit(); + editor.putBoolean(PREF_BANDW_PSERIES_VPT_ENABLED, payloadValue); + editor.putInt(PREF_BANDW_PSERIES_GUI_VPT_LEVEL, payloadValue ? vptLevel + 1 : 0); + editor.apply(); + return true; + } + + private boolean handleGetVptLevelResponse(BandWPSeriesResponse response) { + if (!response.messageType.hasPayload) { + GB.toast("No payload in response!", Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + int payloadValue; + try { + payloadValue = response.payloadUnpacker.unpackInt(); + } catch (IOException e) { + GB.toast("Could not extract vptLevel from payload: " + Arrays.toString(response.payload), Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + boolean vptEnabled = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean(PREF_BANDW_PSERIES_VPT_ENABLED, false); + Editor editor = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).edit(); + editor.putInt(PREF_BANDW_PSERIES_VPT_LEVEL, payloadValue); + editor.putInt(PREF_BANDW_PSERIES_GUI_VPT_LEVEL, vptEnabled ? payloadValue + 1 : 0); + editor.apply(); + return true; + } + + public void onSendConfiguration(String config) { + try { + TransactionBuilder builder = performInitialized("sendConfig"); + switch (config) { + case PREF_ACTIVE_NOISE_CANCELLING_TOGGLE: + boolean ancMode = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean(PREF_ACTIVE_NOISE_CANCELLING_TOGGLE, true); + BandWBLEProfile.setAncModeState(builder, ancMode); + break; + case PREF_BANDW_PSERIES_GUI_VPT_LEVEL: + int level = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getInt(PREF_BANDW_PSERIES_GUI_VPT_LEVEL, 0); + BandWBLEProfile.setVptEnabled(builder, level != 0); + if (level != 0) { + BandWBLEProfile.setVptLevel(builder, level - 1); + } + break; + } + performImmediately(builder); + } catch (IOException e) { + GB.toast("Failed to send settings update", Toast.LENGTH_SHORT, GB.ERROR); + } + } + @Override public boolean useAutoConnect() { return true; } + + private boolean getBooleanResponseStatus(BandWPSeriesResponse response) { + boolean payloadValue; + try { + payloadValue = response.payloadUnpacker.unpackBoolean(); + } catch (IOException e) { + GB.toast("Could not extract response from payload: " + Arrays.toString(response.payload), Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + return payloadValue; + } + + private boolean getIntResponseStatus(BandWPSeriesResponse response) { + int payloadValue; + try { + payloadValue = response.payloadUnpacker.unpackInt(); + } catch (IOException e) { + GB.toast("Could not extract response from payload: " + Arrays.toString(response.payload), Toast.LENGTH_SHORT, GB.ERROR); + return false; + } + return payloadValue == 0; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesRequest.java index 362994252..a0e233962 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/bandwpseries/BandWPSeriesRequest.java @@ -21,7 +21,6 @@ public class BandWPSeriesRequest { messageType = BandWMessageType.REQUEST_WITHOUT_PAYLOAD; namespace = mNamespace; commandId = mCommandId; - payloadPacker.packInt(0); } public BandWPSeriesRequest addToPayload(int value) throws IOException { @@ -36,6 +35,12 @@ public class BandWPSeriesRequest { return this; } + public BandWPSeriesRequest addToPayload(boolean value) throws IOException { + payloadPacker.packBoolean(value); + messageType = BandWMessageType.REQUEST_WITH_PAYLOAD; + return this; + } + public BandWPSeriesRequest addToPayload(String value) throws IOException { payloadPacker.packString(value); messageType = BandWMessageType.REQUEST_WITH_PAYLOAD; @@ -43,13 +48,15 @@ public class BandWPSeriesRequest { } public byte[] finishAndGetBytes() { - byte len = (byte) ((this.messageType == BandWMessageType.REQUEST_WITHOUT_PAYLOAD) ? 4 : 4 + payloadPacker.getBufferSize()); + byte[] payload = payloadPacker.toByteArray(); + byte len = (byte) ((this.messageType == BandWMessageType.REQUEST_WITHOUT_PAYLOAD) ? 4 : 6 + payload.length); byte[] out = addMessageType(new byte[len+1], messageType.value); out[0] = len; out[3] = commandId; out[4] = namespace; if (messageType == BandWMessageType.REQUEST_WITH_PAYLOAD) { - System.arraycopy(payloadPacker.toByteArray(), 0, out, 5, len - 5); + addShort(out, 5, payload.length); + System.arraycopy(payload, 0, out, 7, payload.length); } try { payloadPacker.close(); @@ -60,11 +67,14 @@ public class BandWPSeriesRequest { } private byte[] addMessageType(byte[] target, int value) { - byte valueLo = (byte) (value & 0xff); - byte valueHi = (byte) (value >> 8); - target[1] = valueLo; - target[2] = valueHi; - return target; + return addShort(target, 1, value); } + private byte[] addShort(byte[] target, int position, int value) { + byte valueLo = (byte) (value & 0xff); + byte valueHi = (byte) (value >> 8); + target[position] = valueLo; + target[position+1] = valueHi; + return target; + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c0d89187..2260f939f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2569,6 +2569,9 @@ 5 seconds 10 seconds 15 seconds + Passthrough + Allow external sounds to pass through to your ears + Passthrough level Sleep All-day monitoring Heart rate alerts diff --git a/app/src/main/res/xml/devicesettings_active_noise_cancelling_toggle.xml b/app/src/main/res/xml/devicesettings_active_noise_cancelling_toggle.xml new file mode 100644 index 000000000..426cb3e20 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_active_noise_cancelling_toggle.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/devicesettings_bandw_pseries.xml b/app/src/main/res/xml/devicesettings_bandw_pseries.xml new file mode 100644 index 000000000..96fb3c0de --- /dev/null +++ b/app/src/main/res/xml/devicesettings_bandw_pseries.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file