From bf5eebd411e4fed897f7d0db303b4725ac27d1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sun, 31 Oct 2021 15:01:50 +0000 Subject: [PATCH] Sony WH-1000XM3: Initial Support --- .../DeviceSettingsPreferenceConst.java | 29 + .../DeviceSpecificSettingsFragment.java | 57 ++ .../sony/wh1000xm3/AmbientSoundControl.java | 24 + .../sony/wh1000xm3/AutomaticPowerOff.java | 25 + .../sony/wh1000xm3/EqualizerCustomBands.java | 51 ++ .../sony/wh1000xm3/EqualizerPreset.java | 32 + .../wh1000xm3/SonyWh1000Xm3Coordinator.java | 149 +++++ .../devices/sony/wh1000xm3/SoundPosition.java | 26 + .../devices/sony/wh1000xm3/SurroundMode.java | 25 + .../gadgetbridge/model/DeviceType.java | 1 + .../service/DeviceSupportFactory.java | 5 +- .../service/btclassic/BtClassicIoThread.java | 2 +- .../sony/wh1000xm3/SonyWh1000Xm3IoThread.java | 74 +++ .../sony/wh1000xm3/SonyWh1000Xm3Protocol.java | 585 ++++++++++++++++++ .../sony/wh1000xm3/SonyWh1000Xm3Support.java | 96 +++ .../gadgetbridge/util/DeviceHelper.java | 3 + .../res/drawable/ic_device_headphones.xml | 28 + .../ic_device_headphones_disabled.xml | 28 + app/src/main/res/drawable/ic_speaker.xml | 10 + app/src/main/res/drawable/ic_surround.xml | 10 + app/src/main/res/values-pt/strings.xml | 54 ++ app/src/main/res/values/arrays.xml | 93 +++ app/src/main/res/values/strings.xml | 59 ++ .../res/xml/devicesettings_sony_1000xm3.xml | 261 ++++++++ 24 files changed, 1725 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AmbientSoundControl.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AutomaticPowerOff.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerCustomBands.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerPreset.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SonyWh1000Xm3Coordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SoundPosition.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SurroundMode.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3IoThread.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Protocol.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Support.java create mode 100644 app/src/main/res/drawable/ic_device_headphones.xml create mode 100644 app/src/main/res/drawable/ic_device_headphones_disabled.xml create mode 100644 app/src/main/res/drawable/ic_speaker.xml create mode 100644 app/src/main/res/drawable/ic_surround.xml create mode 100644 app/src/main/res/xml/devicesettings_sony_1000xm3.xml 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 eb5a3fb13..2b6cbbf0d 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 @@ -109,6 +109,35 @@ public class DeviceSettingsPreferenceConst { 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_SONY_WH1000XM3_AMBIENT_SOUND_CONTROL = "pref_sony_wh1000xm3_ambient_sound_control"; + public static final String PREF_SONY_WH1000XM3_FOCUS_VOICE = "pref_sony_wh1000xm3_focus_voice"; + public static final String PREF_SONY_WH1000XM3_AMBIENT_SOUND_LEVEL = "pref_sony_wh1000xm3_ambient_sound_level"; + public static final String PREF_SONY_WH1000XM3_SOUND_POSITION = "pref_sony_wh1000xm3_sound_position"; + public static final String PREF_SONY_WH1000XM3_SURROUND_MODE = "pref_sony_wh1000xm3_surround_mode"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MODE = "pref_sony_wh1000xm3_equalizer_mode"; + public static final String PREF_SONY_WH1000XM3_DSEE_HX = "pref_sony_wh1000xm3_dsee_hx"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_400 = "pref_sony_wh1000xm3_equalizer_manual_band_400"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_1000 = "pref_sony_wh1000xm3_equalizer_manual_band_1000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_2500 = "pref_sony_wh1000xm3_equalizer_manual_band_2500"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_6300 = "pref_sony_wh1000xm3_equalizer_manual_band_6300"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_16000 = "pref_sony_wh1000xm3_equalizer_manual_band_16000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_CLEAR_BASS = "pref_sony_wh1000xm3_equalizer_manual_clear_bass"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_400 = "pref_sony_wh1000xm3_equalizer_custom_1_band_400"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_1000 = "pref_sony_wh1000xm3_equalizer_custom_1_band_1000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_2500 = "pref_sony_wh1000xm3_equalizer_custom_1_band_2500"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_6300 = "pref_sony_wh1000xm3_equalizer_custom_1_band_6300"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_16000 = "pref_sony_wh1000xm3_equalizer_custom_1_band_16000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_CLEAR_BASS = "pref_sony_wh1000xm3_equalizer_custom_1_clear_bass"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_400 = "pref_sony_wh1000xm3_equalizer_custom_2_band_400"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_1000 = "pref_sony_wh1000xm3_equalizer_custom_2_band_1000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_2500 = "pref_sony_wh1000xm3_equalizer_custom_2_band_2500"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_6300 = "pref_sony_wh1000xm3_equalizer_custom_2_band_6300"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_16000 = "pref_sony_wh1000xm3_equalizer_custom_2_band_16000"; + public static final String PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_CLEAR_BASS = "pref_sony_wh1000xm3_equalizer_custom_2_clear_bass"; + public static final String PREF_SONY_WH1000XM3_TOUCH_SENSOR = "pref_sony_wh1000xm3_touch_sensor"; + public static final String PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF = "pref_sony_wh1000xm3_automatic_power_off"; + public static final String PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE = "pref_sony_wh1000xm3_notification_voice_guide"; + 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 9a52525fa..deb94e939 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 @@ -119,6 +119,34 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_ENABLE; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_INEAR; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_CONTROL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_FOCUS_VOICE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_LEVEL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SOUND_POSITION; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SURROUND_MODE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_400; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_1000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_2500; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_6300; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_16000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_CLEAR_BASS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_400; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_1000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_2500; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_6300; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_16000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_CLEAR_BASS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_400; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_1000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_2500; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_6300; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_16000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_CLEAR_BASS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_DSEE_HX; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_TOUCH_SENSOR; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION; @@ -485,6 +513,35 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat { addPreferenceHandlerFor(PREF_GALAXY_BUDS_TOUCH_LEFT); addPreferenceHandlerFor(PREF_GALAXY_BUDS_TOUCH_RIGHT); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_AMBIENT_SOUND_CONTROL); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_FOCUS_VOICE); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_AMBIENT_SOUND_LEVEL); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_SOUND_POSITION); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_SURROUND_MODE); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MODE); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_400); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_1000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_2500); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_6300); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_16000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_CLEAR_BASS); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_400); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_1000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_2500); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_6300); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_16000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_CLEAR_BASS); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_400); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_1000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_2500); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_6300); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_16000); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_CLEAR_BASS); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_DSEE_HX); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_TOUCH_SENSOR); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF); + addPreferenceHandlerFor(PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE); + 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/sony/wh1000xm3/AmbientSoundControl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AmbientSoundControl.java new file mode 100644 index 000000000..e1d4900a9 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AmbientSoundControl.java @@ -0,0 +1,24 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +public enum AmbientSoundControl { + OFF, + NOISE_CANCELLING, + WIND_NOISE_REDUCTION, + AMBIENT_SOUND +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AutomaticPowerOff.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AutomaticPowerOff.java new file mode 100644 index 000000000..c70f33094 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/AutomaticPowerOff.java @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +public enum AutomaticPowerOff { + OFF, + AFTER_5_MIN, + AFTER_30_MIN, + AFTER_1_HOUR, + AFTER_3_HOUR +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerCustomBands.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerCustomBands.java new file mode 100644 index 000000000..d7b73ffa3 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerCustomBands.java @@ -0,0 +1,51 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +import java.util.List; + +public class EqualizerCustomBands { + private List bands; + private int clearBass; + + public EqualizerCustomBands(List bands, int clearBass) { + if (bands.size() != 5) { + throw new IllegalArgumentException("Equalizer needs exactly 5 bands"); + } + + for (Integer band : bands) { + if (band < -10 || band > 10) { + throw new IllegalArgumentException(String.format("Bands should be between -10 and 10, got %d", band)); + } + } + + if (clearBass < -10 || clearBass > 10) { + throw new IllegalArgumentException(String.format("Clear Bass value shoulud be between -10 and 10, got %d", clearBass)); + } + + this.bands = bands; + this.clearBass = clearBass; + } + + public List getBands() { + return bands; + } + + public int getClearBass() { + return clearBass; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerPreset.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerPreset.java new file mode 100644 index 000000000..f597c3a33 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/EqualizerPreset.java @@ -0,0 +1,32 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +public enum EqualizerPreset { + OFF, + BRIGHT, + EXCITED, + MELLOW, + RELAXED, + VOCAL, + TREBLE_BOOST, + BASS_BOOST, + SPEECH, + MANUAL, + CUSTOM_1, + CUSTOM_2 +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SonyWh1000Xm3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SonyWh1000Xm3Coordinator.java new file mode 100644 index 000000000..852cac37e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SonyWh1000Xm3Coordinator.java @@ -0,0 +1,149 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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 SonyWh1000Xm3Coordinator extends AbstractDeviceCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(SonyWh1000Xm3Coordinator.class); + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + if (candidate.getName().contains("WH-1000XM3")) { + return DeviceType.SONY_WH_1000XM3; + } + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.SONY_WH_1000XM3; + } + + @Override + public String getManufacturer() { + return "Sony"; + } + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + } + + @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 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 false; + } + + @Override + public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { + return new int[]{ + R.xml.devicesettings_sony_1000xm3 + }; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SoundPosition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SoundPosition.java new file mode 100644 index 000000000..650afdab7 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SoundPosition.java @@ -0,0 +1,26 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +public enum SoundPosition { + OFF, + FRONT, + FRONT_LEFT, + FRONT_RIGHT, + REAR_LEFT, + REAR_RIGHT +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SurroundMode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SurroundMode.java new file mode 100644 index 000000000..92e9b8240 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/wh1000xm3/SurroundMode.java @@ -0,0 +1,25 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3; + +public enum SurroundMode { + OFF, + ARENA, + CLUB, + OUTDOOR_STAGE, + CONCERT_HALL +} 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 01d60fe27..ced968da1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -102,6 +102,7 @@ public enum DeviceType { 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), + SONY_WH_1000XM3(430, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm3), 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 ad8e5064e..275b489fd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -94,6 +94,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Sup import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wh1000xm3.SonyWh1000Xm3Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch.XWatchSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.zetime.ZeTimeDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -366,7 +367,9 @@ public class DeviceSupportFactory { case GALAXY_BUDS: deviceSupport = new ServiceDeviceSupport(new GalaxyBudsDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; - + case SONY_WH_1000XM3: + deviceSupport = new ServiceDeviceSupport(new SonyWh1000Xm3Support(), 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/btclassic/BtClassicIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java index 55ae751f5..6db2d9832 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/btclassic/BtClassicIoThread.java @@ -161,7 +161,7 @@ public abstract class BtClassicIoThread extends GBDeviceIoThread { mOutStream = mBtSocket.getOutputStream(); setUpdateState(GBDevice.State.CONNECTED); } catch (IOException e) { - LOG.error("Server socket cannot be started."); + LOG.error("Server socket cannot be started.", e); //LOG.error(e.getMessage()); setUpdateState(originalState); mInStream = null; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3IoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3IoThread.java new file mode 100644 index 000000000..823f7bf63 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3IoThread.java @@ -0,0 +1,74 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wh1000xm3; + +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.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class SonyWh1000Xm3IoThread extends BtClassicIoThread { + private static final Logger LOG = LoggerFactory.getLogger(SonyWh1000Xm3IoThread.class); + + public SonyWh1000Xm3IoThread(GBDevice gbDevice, Context context, SonyWh1000Xm3Protocol xm3protocol, SonyWh1000Xm3Support xm3support, BluetoothAdapter roidmiBtAdapter) { + super(gbDevice, context, xm3protocol, xm3support, roidmiBtAdapter); + } + + @Override + protected byte[] parseIncoming(InputStream inputStream) throws IOException { + ByteArrayOutputStream msgStream = new ByteArrayOutputStream(); + byte[] incoming = new byte[1]; + + while (true) { + inputStream.read(incoming); + + if (incoming[0] == SonyWh1000Xm3Protocol.PACKET_HEADER) { + msgStream.reset(); + continue; + } + + if (incoming[0] == SonyWh1000Xm3Protocol.PACKET_TRAILER) { + break; + } + + msgStream.write(incoming); + } + + byte[] msgArray = msgStream.toByteArray(); + LOG.debug("Received: " + GB.hexdump(msgArray, 0, msgArray.length)); + return msgArray; + } + + @NonNull + @Override + protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) { + return UUID.fromString("96CC203E-5068-46ad-B32D-E316F5E069BA"); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Protocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Protocol.java new file mode 100644 index 000000000..8d60a19e0 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Protocol.java @@ -0,0 +1,585 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wh1000xm3; + +import android.content.SharedPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.AmbientSoundControl; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.AutomaticPowerOff; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.EqualizerCustomBands; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.EqualizerPreset; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.SoundPosition; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.SurroundMode; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class SonyWh1000Xm3Protocol extends GBDeviceProtocol { + private static final Logger LOG = LoggerFactory.getLogger(SonyWh1000Xm3Protocol.class); + + /** + * Packet format: + * + * - PACKET_HEADER + * - Command type? - almost always 0x0c? + * - Sequence Number - needs to be updated with the one sent in the responses + * - 4-byte big endian int with number of bytes that will follow + * - N bytes of data + * - Checksum (1-byte sum, excluding header) + * - PACKET_TRAILER + * + * Data between PACKET_HEADER and PACKET_TRAILER is escaped with PACKET_ESCAPE, and the + * following byte masked with PACKET_ESCAPE_MASK. + */ + + public static final byte PACKET_HEADER = 0x3e; + public static final byte PACKET_TRAILER = 0x3c; + public static final byte PACKET_ESCAPE = 0x3d; + public static final byte PACKET_ESCAPE_MASK = (byte) 0b11101111; + + private static final byte MSG_TYPE_ACK = 0x01; + + private static final byte CMD_SOUND_SURROUND = 0x01; + private static final byte CMD_SOUND_POSITION = 0x02; + + private int sequenceNumber = 0; + + public SonyWh1000Xm3Protocol(GBDevice device) { + super(device); + } + + @Override + public GBDeviceEvent[] decodeResponse(byte[] res) { + byte[] message = unescape(res); + String hexdump = GB.hexdump(message, 0, message.length); + + LOG.debug("Received {}", hexdump); + + byte messageChecksum = message[message.length - 1]; + byte expectedChecksum = calcChecksum(message, true); + + if (messageChecksum != expectedChecksum) { + LOG.error("Invalid checksum for {}", hexdump); + return null; + } + + int payloadLength = ((message[2] << 24) & 0xFF000000) | ((message[3] << 16) & 0xFF0000) | ((message[4] << 8) & 0xFF00) | ((message[5] << 24) & 0xFF000000); + if (payloadLength != message.length - 7) { + LOG.error("Unexpected payload length {}, expected {}", message.length - 6, payloadLength); + return null; + } + + sequenceNumber = message[1]; + + if (message[0] == MSG_TYPE_ACK) { + LOG.info("Received ACK: {}", hexdump); + return new GBDeviceEvent[]{}; + } + + LOG.warn("Unknown message: {}", hexdump); + + return null; + } + + @Override + public byte[] encodeSendConfiguration(String config) { + final SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); + EqualizerPreset equalizerPreset = EqualizerPreset.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE, "off").toUpperCase()); + + switch (config) { + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_CONTROL: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_FOCUS_VOICE: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_LEVEL: + String soundControl = prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_CONTROL, "noise_cancelling"); + boolean focusVoice = prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_FOCUS_VOICE, false); + int level = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AMBIENT_SOUND_LEVEL, 0); + return encodeSoundControl(AmbientSoundControl.valueOf(soundControl.toUpperCase()), focusVoice, level); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SOUND_POSITION: + return encodeSoundPosition( + SoundPosition.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SOUND_POSITION, "off").toUpperCase()) + ); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SURROUND_MODE: + return encodeSurroundMode( + SurroundMode.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_SURROUND_MODE, "off").toUpperCase()) + ); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE: + return encodeEqualizerPreset(equalizerPreset); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_400: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_1000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_2500: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_6300: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_16000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_CLEAR_BASS: + int m_band1 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_400, 10) - 10; + int m_band2 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_1000, 10) - 10; + int m_band3 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_2500, 10) - 10; + int m_band4 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_6300, 10) - 10; + int m_band5 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_BAND_16000, 10) - 10; + int m_bass = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MANUAL_CLEAR_BASS, 10) - 10; + + // Set the equalizer preset, since changing the bands will switch it + // TODO: This is not updating the UI once the user returns to the previous screen + prefs.edit().putString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE, EqualizerPreset.MANUAL.toString().toLowerCase()).apply(); + + return encodeEqualizerCustomBands(EqualizerPreset.MANUAL, equalizerPreset, new EqualizerCustomBands(Arrays.asList(m_band1, m_band2, m_band3, m_band4, m_band5), m_bass)); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_400: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_1000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_2500: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_6300: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_16000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_CLEAR_BASS: + int c1_band1 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_400, 10) - 10; + int c1_band2 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_1000, 10) - 10; + int c1_band3 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_2500, 10) - 10; + int c1_band4 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_6300, 10) - 10; + int c1_band5 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_BAND_16000, 10) - 10; + int c1_bass = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_1_CLEAR_BASS, 10) - 10; + + // Set the equalizer preset, since changing the bands will switch it + // TODO: This is not updating the UI once the user returns to the previous screen + prefs.edit().putString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE, EqualizerPreset.CUSTOM_1.toString().toLowerCase()).apply(); + + return encodeEqualizerCustomBands(EqualizerPreset.CUSTOM_1, equalizerPreset, new EqualizerCustomBands(Arrays.asList(c1_band1, c1_band2, c1_band3, c1_band4, c1_band5), c1_bass)); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_400: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_1000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_2500: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_6300: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_16000: + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_CLEAR_BASS: + int c2_band1 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_400, 10) - 10; + int c2_band2 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_1000, 10) - 10; + int c2_band3 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_2500, 10) - 10; + int c2_band4 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_6300, 10) - 10; + int c2_band5 = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_BAND_16000, 10) - 10; + int c2_bass = prefs.getInt(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_CUSTOM_2_CLEAR_BASS, 10) - 10; + + // Set the equalizer preset, since changing the bands will switch it + // TODO: This is not updating the UI once the user returns to the previous screen + prefs.edit().putString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_EQUALIZER_MODE, EqualizerPreset.CUSTOM_2.toString().toLowerCase()).apply(); + + return encodeEqualizerCustomBands(EqualizerPreset.CUSTOM_2, equalizerPreset, new EqualizerCustomBands(Arrays.asList(c2_band1, c2_band2, c2_band3, c2_band4, c2_band5), c2_bass)); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_DSEE_HX: + return encodeDSEEHX(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_DSEE_HX, false)); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_TOUCH_SENSOR: + return encodeTouchSensor(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_TOUCH_SENSOR, true)); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF: + return encodeAutomaticPowerOff( + AutomaticPowerOff.valueOf(prefs.getString(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_AUTOMATIC_POWER_OFF, "off").toUpperCase()) + ); + + case DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE: + return encodeVoiceNotifications(prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_SONY_WH1000XM3_NOTIFICATION_VOICE_GUIDE, true)); + + default: + LOG.warn("Unknown config '{}'", config); + } + + return super.encodeSendConfiguration(config); + } + + public byte[] encodeTriggerNoiseCancellingOptimizer() { + // This successfully triggers the noise cancelling optimizer. However, we don't get the + // optimization progress messages. + + ByteBuffer buf = ByteBuffer.allocate(10); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(4); + + buf.put((byte) 0x84); + buf.put((byte) 0x01); + buf.put((byte) 0x00); + buf.put((byte) 0x01); + + return encodeMessage(buf.array()); + } + + private byte[] encodeSoundControl(AmbientSoundControl ambientSoundControl, boolean focusOnVoice, int ambientSound) { + if (ambientSound < 0 || ambientSound > 19) { + throw new IllegalArgumentException("Level must be between 0 and 19"); + } + + ByteBuffer buf = ByteBuffer.allocate(14); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(8); + + buf.put((byte) 0x68); + buf.put((byte) 0x02); + if (AmbientSoundControl.OFF.equals(ambientSoundControl)) { + buf.put((byte) 0x00); + } else { + buf.put((byte) 0x11); + } + buf.put((byte) 0x01); + + switch (ambientSoundControl) { + case NOISE_CANCELLING: + buf.put((byte) 2); + break; + case WIND_NOISE_REDUCTION: + buf.put((byte) 1); + break; + case OFF: + case AMBIENT_SOUND: + default: + buf.put((byte) 0); + break; + } + + buf.put((byte) 0x01); + buf.put((byte) (focusOnVoice ? 0x01 : 0x00)); + buf.put((byte) (ambientSound + 1)); + + return encodeMessage(buf.array()); + } + + private byte[] encodeSoundPosition(SoundPosition position) { + final ByteBuffer buf = ByteBuffer.allocate(9); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(3); + + buf.put((byte) 0x48); + buf.put(CMD_SOUND_POSITION); + + switch (position) { + case OFF: + buf.put((byte) 0x00); + break; + case FRONT: + buf.put((byte) 0x03); + break; + case FRONT_LEFT: + buf.put((byte) 0x01); + break; + case FRONT_RIGHT: + buf.put((byte) 0x02); + break; + case REAR_LEFT: + buf.put((byte) 0x11); + break; + case REAR_RIGHT: + buf.put((byte) 0x12); + break; + } + + return encodeMessage(buf.array()); + } + + private byte[] encodeSurroundMode(SurroundMode mode) { + final ByteBuffer buf = ByteBuffer.allocate(9); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(3); + + buf.put((byte) 0x48); + buf.put(CMD_SOUND_SURROUND); + + switch (mode) { + case OFF: + buf.put((byte) 0x00); + break; + case ARENA: + buf.put((byte) 0x02); + break; + case CLUB: + buf.put((byte) 0x04); + break; + case OUTDOOR_STAGE: + buf.put((byte) 0x01); + break; + case CONCERT_HALL: + buf.put((byte) 0x03); + break; + } + + return encodeMessage(buf.array()); + } + + private byte[] encodeEqualizerPreset(EqualizerPreset preset) { + final ByteBuffer buf = ByteBuffer.allocate(10); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(4); + + buf.put((byte) 0x58); + buf.put((byte) 0x01); + + switch (preset) { + case OFF: + buf.put((byte) 0x00).put((byte) 0x00); + break; + case BRIGHT: + buf.put((byte) 0x10).put((byte) 0x00); + break; + case EXCITED: + buf.put((byte) 0x11).put((byte) 0x00); + break; + case MELLOW: + buf.put((byte) 0x12).put((byte) 0x00); + break; + case RELAXED: + buf.put((byte) 0x13).put((byte) 0x00); + break; + case VOCAL: + buf.put((byte) 0x14).put((byte) 0x00); + break; + case TREBLE_BOOST: + buf.put((byte) 0x15).put((byte) 0x00); + break; + case BASS_BOOST: + buf.put((byte) 0x16).put((byte) 0x00); + break; + case SPEECH: + buf.put((byte) 0x17).put((byte) 0x00); + break; + case MANUAL: + buf.put((byte) 0xa0).put((byte) 0x00); + break; + case CUSTOM_1: + buf.put((byte) 0xa1).put((byte) 0x00); + break; + case CUSTOM_2: + buf.put((byte) 0xa2).put((byte) 0x00); + break; + } + + return encodeMessage(buf.array()); + } + + private byte[] encodeEqualizerCustomBands(EqualizerPreset preset, EqualizerPreset previousPreset, EqualizerCustomBands equalizer) { + ByteArrayOutputStream cmdStream = new ByteArrayOutputStream(16); + + try { + if (preset != previousPreset) { + // If we're not on the preset that is being changed, we need to swap to it + cmdStream.write(encodeEqualizerPreset(preset)); + } + + cmdStream.write(encodeEqualizerCustomBands(equalizer)); + + if (preset != previousPreset) { + // And then we swap back to the previous preset + // FIXME: this is not working, the new preset stays + //cmdStream.write(encodeEqualizerPreset(previousPreset)); + } + } catch (IOException e) { + LOG.error("This should never happen", e); + } + + return cmdStream.toByteArray(); + } + + private byte[] encodeEqualizerCustomBands(EqualizerCustomBands equalizer) { + final ByteBuffer buf = ByteBuffer.allocate(16); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(10); + + buf.put((byte) 0x58); + buf.put((byte) 0x01); + buf.put((byte) 0xff); + buf.put((byte) 0x06); + + buf.put((byte) (equalizer.getClearBass() + 10)); + for (final Integer band : equalizer.getBands()) { + buf.put((byte) (band + 10)); + } + + return encodeMessage(buf.array()); + } + + private byte[] encodeDSEEHX(boolean enabled) { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(4); + + buf.put((byte) 0xe8); + buf.put((byte) 0x02); + buf.put((byte) 0x00); + buf.put((byte) (enabled ? 0x01 : 0x00)); + + return encodeMessage(buf.array()); + } + + private byte[] encodeTouchSensor(boolean enabled) { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(4); + + buf.put((byte) 0xd8); + buf.put((byte) 0xd2); + buf.put((byte) 0x01); + buf.put((byte) (enabled ? 0x01 : 0x00)); + + return encodeMessage(buf.array()); + } + + private byte[] encodeAutomaticPowerOff(AutomaticPowerOff automaticPowerOff) { + ByteBuffer buf = ByteBuffer.allocate(11); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0c); + buf.put((byte) sequenceNumber++); + buf.putInt(5); + + buf.put((byte) 0xf8); + buf.put((byte) 0x04); + buf.put((byte) 0x01); + + switch (automaticPowerOff) { + case OFF: + buf.put((byte) 0x11).put((byte) 0x00); + break; + case AFTER_5_MIN: + buf.put((byte) 0x00).put((byte) 0x00); + break; + case AFTER_30_MIN: + buf.put((byte) 0x01).put((byte) 0x01); + break; + case AFTER_1_HOUR: + buf.put((byte) 0x02).put((byte) 0x02); + break; + case AFTER_3_HOUR: + buf.put((byte) 0x03).put((byte) 0x03); + break; + } + + return encodeMessage(buf.array()); + } + + private byte[] encodeVoiceNotifications(boolean enabled) { + ByteBuffer buf = ByteBuffer.allocate(10); + buf.order(ByteOrder.BIG_ENDIAN); + + buf.put((byte) 0x0e); + buf.put((byte) sequenceNumber++); + buf.putInt(4); + + buf.put((byte) 0x48); + buf.put((byte) 0x01); + buf.put((byte) 0x01); + buf.put((byte) (enabled ? 0x01 : 0x00)); + + return encodeMessage(buf.array()); + } + + private byte[] encodeMessage(byte[] message) { + ByteArrayOutputStream cmdStream = new ByteArrayOutputStream(message.length + 2); + + cmdStream.write(PACKET_HEADER); + + byte checksum = calcChecksum(message, false); + + try { + cmdStream.write(escape(message)); + cmdStream.write(escape(new byte[]{checksum})); + } catch (IOException e) { + LOG.error("This should never happen", e); + } + + cmdStream.write(PACKET_TRAILER); + + return cmdStream.toByteArray(); + } + + private byte[] escape(byte[] bytes) { + ByteArrayOutputStream escapedStream = new ByteArrayOutputStream(bytes.length); + + for (byte b : bytes) { + switch (b) { + case PACKET_HEADER: + case PACKET_TRAILER: + case PACKET_ESCAPE: + escapedStream.write(PACKET_ESCAPE); + escapedStream.write(b & PACKET_ESCAPE_MASK); + break; + default: + escapedStream.write(b); + break; + } + } + + return escapedStream.toByteArray(); + } + + private byte[] unescape(byte[] bytes) { + ByteArrayOutputStream unescapedStream = new ByteArrayOutputStream(bytes.length); + + for (int i = 0; i < bytes.length; i++) { + byte b = bytes[i]; + if (b == PACKET_ESCAPE) { + if (++i >= bytes.length) { + throw new IllegalArgumentException("Invalid escape character at end of array"); + } + unescapedStream.write(b & ~PACKET_ESCAPE_MASK); + } else { + unescapedStream.write(b); + } + } + + return unescapedStream.toByteArray(); + } + + public byte calcChecksum(byte[] message, boolean ignoreLastByte) { + int chk = 0; + for (int i = 0; i < message.length - (ignoreLastByte ? 1 : 0); i++) { + chk += message[i] & 255; + } + return (byte) chk; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Support.java new file mode 100644 index 000000000..ff9b7decc --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/wh1000xm3/SonyWh1000Xm3Support.java @@ -0,0 +1,96 @@ +/* Copyright (C) 2021 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wh1000xm3; + +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 SonyWh1000Xm3Support extends AbstractSerialDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(SonyWh1000Xm3Support.class); + + @Override + public boolean connect() { + getDeviceIOThread().start(); + + return true; + } + + @Override + protected GBDeviceProtocol createDeviceProtocol() { + return new SonyWh1000Xm3Protocol(getDevice()); + } + + @Override + protected GBDeviceIoThread createDeviceIOThread() { + return new SonyWh1000Xm3IoThread(getDevice(), getContext(), (SonyWh1000Xm3Protocol) getDeviceProtocol(), SonyWh1000Xm3Support.this, getBluetoothAdapter()); + } + + @Override + public synchronized SonyWh1000Xm3IoThread getDeviceIOThread() { + return (SonyWh1000Xm3IoThread) super.getDeviceIOThread(); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + @Override + public void onInstallApp(Uri uri) { + // Nothing to do + } + + @Override + public void onAppConfiguration(UUID uuid, String config, Integer id) { + // Nothing to do + } + + @Override + public void onHeartRateTest() { + // Nothing to do + } + + @Override + public void onSetConstantVibration(int intensity) { + // Nothing to do + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + // Nothing to do + } + + @Override + public void onSetAlarms(ArrayList alarms) { + // Nothing to do + } + + @Override + public void onReadConfiguration(String config) { + // Nothing to do + } +} 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 b15d80c07..c7da8fc78 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -112,6 +112,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordin import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.wh1000xm3.SonyWh1000Xm3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeCoordinator; import nodomain.freeyourgadget.gadgetbridge.entities.Device; @@ -311,6 +312,8 @@ public class DeviceHelper { result.add(new FitProDeviceCoordinator()); result.add(new Ear1Coordinator()); result.add(new GalaxyBudsDeviceCoordinator()); + result.add(new SonyWh1000Xm3Coordinator()); + return result; } diff --git a/app/src/main/res/drawable/ic_device_headphones.xml b/app/src/main/res/drawable/ic_device_headphones.xml new file mode 100644 index 000000000..f56513664 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_headphones.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_device_headphones_disabled.xml b/app/src/main/res/drawable/ic_device_headphones_disabled.xml new file mode 100644 index 000000000..15026d7d2 --- /dev/null +++ b/app/src/main/res/drawable/ic_device_headphones_disabled.xml @@ -0,0 +1,28 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_speaker.xml b/app/src/main/res/drawable/ic_speaker.xml new file mode 100644 index 000000000..c5fd13d52 --- /dev/null +++ b/app/src/main/res/drawable/ic_speaker.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_surround.xml b/app/src/main/res/drawable/ic_surround.xml new file mode 100644 index 000000000..7b89bbae5 --- /dev/null +++ b/app/src/main/res/drawable/ic_surround.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 7ccb7db4c..0f2c39aae 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1166,4 +1166,58 @@ \nNota: não precisa instalar .res e .gps se esses forem exatamente os mesmos que os instalados anteriormente. \n \nPROSSIGA AO SEU PRÓPRIO RISCO! + Controlo Som Ambiente + Modo + Desligado + Cancelamento de ruído + Redução de ruído do vento + Som ambiente + Foco na voz + Nível de som ambiente + Posição do som + Desligado + Frente + Frente Esquerda + Frente Direita + Traseira Esquerda + Traseira Direita + Modo Surround + Desligado + Arena + Discoteca + Palco exterior + Sala de concertos + Aviso: As preferencias de posição de som / modo surround são mutuamente exclusivas. + Aviso: Estas preferencias só funcionam com o codec de audio SBC. + Equalizador + Desligado + Brilhante + Excitado + Suave + Relaxado + Vocal + Amplificação de agudos + Amplificação de graves + Discurso + Manual + Pessoal 1 + Pessoal 2 + Predefinição Manual + Predefinição Pessoal 1 + Predefinição Pessoal 2 + 400 + 1k + 2.5k + 6.3k + 16k + Clear Bass + DSEE HX + Controlo com sensor tátil + Notificações & Guia de voz + Desligar automaticamente + Não desligar + 5 minutos + 30 minutos + 1 hora + 3 horas \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 52a92358b..ac55e397d 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -1833,5 +1833,98 @@ 3 + + @string/sony_ambient_sound_off + @string/sony_ambient_sound_noise_cancelling + @string/sony_ambient_sound_wind_noise_reduction + @string/sony_ambient_sound_ambient_sound + + + + off + noise_cancelling + wind_noise_reduction + ambient_sound + + + + @string/sony_sound_position_off + @string/sony_sound_position_front + @string/sony_sound_position_front_left + @string/sony_sound_position_front_right + @string/sony_sound_position_rear_left + @string/sony_sound_position_rear_right + + + + off + front + front_left + front_right + rear_left + rear_right + + + + @string/sony_surround_mode_off + @string/sony_surround_mode_arena + @string/sony_surround_mode_club + @string/sony_surround_mode_outdoor_stage + @string/sony_surround_mode_concert_hall + + + + off + arena + club + outdoor_stage + concert_hall + + + + @string/sony_equalizer_preset_off + @string/sony_equalizer_preset_bright + @string/sony_equalizer_preset_excited + @string/sony_equalizer_preset_mellow + @string/sony_equalizer_preset_relaxed + @string/sony_equalizer_preset_vocal + @string/sony_equalizer_preset_treble_boost + @string/sony_equalizer_preset_bass_boost + @string/sony_equalizer_preset_speech + @string/sony_equalizer_preset_manual + @string/sony_equalizer_preset_custom_1 + @string/sony_equalizer_preset_custom_2 + + + + off + bright + excited + mellow + relaxed + vocal + treble_boost + bass_boost + speech + manual + custom_1 + custom_2 + + + + @string/sony_automatic_power_off_off + @string/sony_automatic_power_off_5_min + @string/sony_automatic_power_off_30_min + @string/sony_automatic_power_off_1_hour + @string/sony_automatic_power_off_3_hour + + + + off + after_5_min + after_30_min + after_1_hour + after_3_hour + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index edd0fdcf1..8b6222fa3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -101,6 +101,9 @@ Settings General settings + Other + System + Equalizer Connect to Gadgetbridge device when Bluetooth is turned on Start automatically Reconnect automatically @@ -888,6 +891,7 @@ SMA-Q2 OSS FitPro + Sony WH-1000XM3 Choose export location General High-priority @@ -1312,4 +1316,59 @@ Battery case Left earbud Right earbud + Ambient Sound Control + Mode + Off + Noise Cancelling + Wind Noise Reduction + Ambient Sound + Focus on Voice + Ambient Sound Level + Sound Position + Off + Front + Front Left + Front Right + Rear Left + Rear Right + Surround Mode + Off + Arena + Club + Outdoor Stage + Concert Hall + Warning: The sound position / surround mode settings are mutually exclusive. + Warning: These settings only work for the SBC audio codec. + Equalizer + Off + Bright + Excited + Mellow + Relaxed + Vocal + Treble Boost + Bass Boost + Speech + Manual + Custom 1 + Custom 2 + Manual Preset + Custom Preset 1 + Custom Preset 2 + 400 + 1k + 2.5k + 6.3k + 16k + Clear Bass + DSEE HX + Touch sensor control + Notifications & Voice Guide + Automatic Power Off + Do not turn off + 5 minutes + 30 minutes + 1 hour + 3 hours + diff --git a/app/src/main/res/xml/devicesettings_sony_1000xm3.xml b/app/src/main/res/xml/devicesettings_sony_1000xm3.xml new file mode 100644 index 000000000..f2f0fb161 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_1000xm3.xml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +