diff --git a/.drone.yml b/.drone.yml deleted file mode 100644 index 8ec542f6d..000000000 --- a/.drone.yml +++ /dev/null @@ -1,8 +0,0 @@ -kind: pipeline -name: android - -steps: -- name: build - image: androidsdk/android-29 - commands: - - ./gradlew assembleDebug --stacktrace diff --git a/.woodpecker.yml b/.woodpecker.yml deleted file mode 100644 index 7bd3afee0..000000000 --- a/.woodpecker.yml +++ /dev/null @@ -1,6 +0,0 @@ -pipeline: - test: - image: androidsdk/android-30 - commands: - - pwd #bump - - ./gradlew assembleDebug --stacktrace diff --git a/.woodpecker/can_master_build.yml b/.woodpecker/can_master_build.yml new file mode 100644 index 000000000..1c6846dfd --- /dev/null +++ b/.woodpecker/can_master_build.yml @@ -0,0 +1,13 @@ +pipeline: + build: + image: androidsdk/android-30 + commands: + - pwd #bump + - ./gradlew assembleDebug --stacktrace + +#this doesn't work yet: +#https://github.com/woodpecker-ci/woodpecker/issues/687 +#when: + #repo: Freeyourgadget/Gadgetbridge + #branch: master + diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8b2b239..2b9500277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ ### Changelog +### Next +* Sony WH-1000XM4: Initial Support +* Sony WH-1000XM3: Disable equalizer, surround and sound position while in SBC codec +* Improve Sony Headphones initialization on connection +* Fixed accidentally disabled time synchronization and pairing of new Casio GBX/GBD-series watches +* Fossil HR: improved Device Applications List handling. Added ability to change activity recognition settings on the watch +* Fossil HR: Make width of custom widget configurable. Disable non-configurable buttons preferences +* Add Support for Bip U series China Variant +* Add icon for VESC devices +* Add commit id into About screen +* Make debug activity notification test to persist text while switching apps +* Add Portuguese to the list of language options +* Update configuration button icon in app notification settings + ### 0.64.0 * Initial support for VESC NRF/HM10 devices * Initial support vor Bose QC35 diff --git a/README.md b/README.md index 96fa952ea..7a8513aff 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,8 @@ vendor's servers. - Casio GB-5600B - Casio STB-1000 - Casio GBX-100 + - Casio GBD-100 + - Casio GBD-200 - [FitPro](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/FitPro) - Fossil - [Hybrid HR](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Fossil-Hybrid-HR) [**\[!\]**](#special-pairing-procedures) @@ -79,7 +81,7 @@ vendor's servers. - PineTime (InfiniTime Firmware) - Roidmi, Roidmi 3, Mojietu 3 (Bluetooth FM Transmitters) - [SMA](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SMA) Q2 (SMA-Q2-OSS Firmware) -- Sony WH-1000XM3, WF-SP800N +- [Sony WH-1000XM3, WH-1000XM4, WF-SP800N](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Sony-Headphones) - Teclast H10, H30 - TLW64 - Vibratissimo (Experimental) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index a03b8e9e2..38bdc1047 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -101,6 +101,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.WidgetPreferenceStorage; public class DebugActivity extends AbstractGBActivity { private static final Logger LOG = LoggerFactory.getLogger(DebugActivity.class); + private static Bundle dataLossSave; + private static final String EXTRA_REPLY = "reply"; private static final String ACTION_REPLY = "nodomain.freeyourgadget.gadgetbridge.DebugActivity.action.reply"; @@ -508,6 +510,29 @@ public class DebugActivity extends AbstractGBActivity { } + + + @Override + protected void onPause() { + super.onPause(); + if (dataLossSave != null ) { + dataLossSave.clear(); + dataLossSave = null ; + } + dataLossSave = new Bundle(); + dataLossSave.putString("editContent", editContent.getText().toString()); + } + + @Override + protected void onResume() { + super.onResume(); + if (dataLossSave != null ) { + editContent.setText(dataLossSave.getString("editContent", "")); + }else{ + editContent.setText("Test"); + } + } + private void deleteWidgetsPrefs() { WidgetPreferenceStorage widgetPreferenceStorage = new WidgetPreferenceStorage(); widgetPreferenceStorage.deleteWidgetsPrefs(DebugActivity.this); 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 828ad7b9e..5bf18d994 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 @@ -121,8 +121,13 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_SONY_AMBIENT_SOUND_CONTROL = "pref_sony_ambient_sound_control"; public static final String PREF_SONY_FOCUS_VOICE = "pref_sony_focus_voice"; public static final String PREF_SONY_AMBIENT_SOUND_LEVEL = "pref_sony_ambient_sound_level"; + public static final String PREF_SONY_NOISE_OPTIMIZER_START = "pref_sony_noise_optimizer_start"; + public static final String PREF_SONY_NOISE_OPTIMIZER_CANCEL = "pref_sony_noise_optimizer_cancel"; + public static final String PREF_SONY_NOISE_OPTIMIZER_STATUS = "pref_sony_noise_optimizer_status"; + public static final String PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE = "pref_sony_noise_optimizer_state_pressure"; public static final String PREF_SONY_SOUND_POSITION = "pref_sony_sound_position"; public static final String PREF_SONY_SURROUND_MODE = "pref_sony_surround_mode"; + public static final String PREF_SONY_EQUALIZER = "pref_sony_equalizer"; public static final String PREF_SONY_EQUALIZER_MODE = "pref_sony_equalizer_mode"; public static final String PREF_SONY_AUDIO_UPSAMPLING = "pref_sony_audio_upsampling"; public static final String PREF_SONY_EQUALIZER_BAND_400 = "pref_sony_equalizer_band_400"; @@ -137,6 +142,11 @@ public class DeviceSettingsPreferenceConst { public static final String PREF_SONY_BUTTON_MODE_RIGHT = "pref_sony_button_mode_right"; public static final String PREF_SONY_AUTOMATIC_POWER_OFF = "pref_sony_automatic_power_off"; public static final String PREF_SONY_NOTIFICATION_VOICE_GUIDE = "pref_sony_notification_voice_guide"; + public static final String PREF_SONY_SPEAK_TO_CHAT = "pref_sony_speak_to_chat"; + public static final String PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY = "pref_sony_speak_to_chat_sensitivity"; + public static final String PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE = "pref_sony_speak_to_chat_focus_on_voice"; + public static final String PREF_SONY_SPEAK_TO_CHAT_TIMEOUT = "pref_sony_speak_to_chat_timeout"; + public static final String PREF_SONY_CONNECT_TWO_DEVICES = "pref_sony_connect_two_devices"; public static final String PREF_QC35_NOISE_CANCELLING_LEVEL = "qc35_noise_cancelling_level"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java index bf8548dd8..3a8ac1323 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java @@ -16,11 +16,23 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.activities.devicesettings; +import android.os.Parcelable; + +import androidx.preference.Preference; + /** * A device-specific preference handler, that allows for concrete implementations to customize the preferences in * the {@link DeviceSpecificSettingsFragment}. */ -public interface DeviceSpecificSettingsCustomizer { +public interface DeviceSpecificSettingsCustomizer extends Parcelable { + /** + * Called when a {@link Preference} changes, not caused by user input (so the preference change listener is not called). + * + * @param preference the {@link Preference} preference that changed + * @param handler the {@link DeviceSpecificSettingsHandler} + */ + void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler); + /** * Customize the settings on the {@link DeviceSpecificSettingsFragment}. * 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 b61545ee6..ccdc0e024 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 @@ -56,108 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference; import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ALTITUDE_CALIBRATE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AMPM_ENABLED; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_ANTILOST_ENABLED; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_NOTIFICATIONS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BT_CONNECTED_ADVERTISEMENT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOLIGHT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOREMOVE_MESSAGE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_BUTTON_BP_CALIBRATE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DISCONNECTNOTIF_NOSHED; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DO_NOT_DISTURB_NOAUTO_END; -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_GALAXY_BUDS_LIVE_ANC; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_PRESSURE_RELIEF; -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; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_SAVE_RAW_ACTIVITY_FILES; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_RUNNING; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_BIKING; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_WALKING; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYBRID_HR_ACTIVITY_RECOGNITION_ROWING; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_PERIOD; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_HYDRATION_SWITCH; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_KEY_VIBRATION; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LANGUAGE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LEFUN_INTERFACE_LANGUAGE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LIFTWRIST_NOSHED; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_PERIOD; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_SWITCH; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_START; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_LONGSIT_END; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SWITCH; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_SLEEP; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_INTERVAL; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_START; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_AUTOHEARTRATE_END; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTIFICATION_ENABLE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_OPERATING_SOUNDS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_POWER_MODE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_ORIENTATION; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_END; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SLEEP_TIME_START; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_LOW_VIBRATION; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_SMART_INTERVAL; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONYSWR12_STAMINA; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_PAUSE_WHEN_TAKEN_OFF; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SOUNDS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_STRENGH_PERCENTAGE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION; -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_AMBIENT_SOUND_CONTROL; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_FOCUS_VOICE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_400; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_1000; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_2500; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_6300; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_16000; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_UPSAMPLING; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_TOUCH_SENSOR; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_MODE_RIGHT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_BUTTON_MODE_LEFT; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUTOMATIC_POWER_OFF; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOTIFICATION_VOICE_GUIDE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_DISTANCE; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_SLEEP; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD_STEPS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS; -import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_QC35_NOISE_CANCELLING_LEVEL; - +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*; 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; @@ -194,18 +93,14 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.PR public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat implements DeviceSpecificSettingsHandler { - private final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer; - - public DeviceSpecificSettingsFragment(final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer) { - this.deviceSpecificSettingsCustomizer = deviceSpecificSettingsCustomizer; - } - private static final Logger LOG = LoggerFactory.getLogger(DeviceSpecificSettingsFragment.class); static final String FRAGMENT_TAG = "DEVICE_SPECIFIC_SETTINGS_FRAGMENT"; private final SharedPreferencesChangeHandler sharedPreferencesChangeHandler = new SharedPreferencesChangeHandler(); + private DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer; + private void setSettingsFileSuffix(String settingsFileSuffix, @NonNull int[] supportedSettings, String[] supportedLanguages) { Bundle args = new Bundle(); args.putString("settingsFileSuffix", settingsFileSuffix); @@ -214,6 +109,11 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp setArguments(args); } + private void setDeviceSpecificSettingsCustomizer(final DeviceSpecificSettingsCustomizer customizer) { + final Bundle args = getArguments() != null ? getArguments() : new Bundle(); + args.putParcelable("deviceSpecificSettingsCustomizer", customizer); + setArguments(args); + } @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { @@ -224,6 +124,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp String settingsFileSuffix = arguments.getString("settingsFileSuffix", null); int[] supportedSettings = arguments.getIntArray("supportedSettings"); String[] supportedLanguages = arguments.getStringArray("supportedLanguages"); + this.deviceSpecificSettingsCustomizer = arguments.getParcelable("deviceSpecificSettingsCustomizer"); if (settingsFileSuffix == null || supportedSettings == null) { return; @@ -294,6 +195,19 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp getListView().post(runnable); } + /* + * delayed execution so that the preferences are applied first + */ + @Override + public void notifyPreferenceChanged(final String preferenceKey) { + invokeLater(new Runnable() { + @Override + public void run() { + GBApplication.deviceService().onSendConfiguration(preferenceKey); + } + }); + } + private void setChangeListener() { final Prefs prefs = new Prefs(getPreferenceManager().getSharedPreferences()); String disconnectNotificationState = prefs.getString(PREF_DISCONNECT_NOTIFICATION, PREF_DO_NOT_DISTURB_OFF); @@ -305,12 +219,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp disconnectNotificationStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_START); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_START); return true; } }); @@ -322,12 +231,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp disconnectNotificationEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION_END); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION_END); return true; } }); @@ -342,12 +246,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(disconnectNotificationStart).setEnabled(scheduled); Objects.requireNonNull(disconnectNotificationEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISCONNECT_NOTIFICATION); - } - }); + notifyPreferenceChanged(PREF_DISCONNECT_NOTIFICATION); return true; } }); @@ -363,12 +262,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp nightModeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_START); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE_START); return true; } }); @@ -380,12 +274,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp nightModeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE_END); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE_END); return true; } }); @@ -402,12 +291,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(nightModeStart).setEnabled(scheduled); Objects.requireNonNull(nightModeEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_NIGHT_MODE); - } - }); + notifyPreferenceChanged(PREF_NIGHT_MODE); return true; } }); @@ -423,12 +307,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp doNotDisturbStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_START); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB_START); return true; } }); @@ -440,12 +319,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp doNotDisturbEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB_END); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB_END); return true; } }); @@ -461,12 +335,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp Objects.requireNonNull(doNotDisturbStart).setEnabled(scheduled); Objects.requireNonNull(doNotDisturbEnd).setEnabled(scheduled); - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DO_NOT_DISTURB); - } - }); + notifyPreferenceChanged(PREF_DO_NOT_DISTURB); return true; } }); @@ -577,6 +446,11 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp addPreferenceHandlerFor(PREF_SONY_BUTTON_MODE_RIGHT); addPreferenceHandlerFor(PREF_SONY_AUTOMATIC_POWER_OFF); addPreferenceHandlerFor(PREF_SONY_NOTIFICATION_VOICE_GUIDE); + addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT); + addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY); + addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE); + addPreferenceHandlerFor(PREF_SONY_SPEAK_TO_CHAT_TIMEOUT); + addPreferenceHandlerFor(PREF_SONY_CONNECT_TWO_DEVICES); addPreferenceHandlerFor(PREF_QC35_NOISE_CANCELLING_LEVEL); @@ -589,12 +463,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME); return true; } }); @@ -606,12 +475,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_START); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME_START); return true; } }); @@ -623,12 +487,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp sleepTimeEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME_END); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME_END); return true; } }); @@ -645,12 +504,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (sleepTimeInfo != null) { //sleepTimeInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString())); } - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_SLEEP_TIME); - } - }); + notifyPreferenceChanged(PREF_SLEEP_TIME); return true; } }); @@ -664,12 +518,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp rotateWristCycleInfo.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); - } - }); + notifyPreferenceChanged(PREF_MI2_ROTATE_WRIST_TO_SWITCH_INFO); return true; } }); @@ -681,12 +530,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp displayOnLiftStart.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_START); - } - }); + notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_START); return true; } }); @@ -698,12 +542,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp displayOnLiftEnd.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_DISPLAY_ON_LIFT_END); - } - }); + notifyPreferenceChanged(PREF_DISPLAY_ON_LIFT_END); return true; } }); @@ -720,12 +559,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (rotateWristCycleInfo != null) { rotateWristCycleInfo.setEnabled(!PREF_DO_NOT_DISTURB_OFF.equals(newVal.toString())); } - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(PREF_ACTIVATE_DISPLAY_ON_LIFT); - } - }); + notifyPreferenceChanged(PREF_ACTIVATE_DISPLAY_ON_LIFT); return true; } }); @@ -852,8 +686,9 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp @NonNull int[] supportedSettings, String[] supportedLanguages, DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer) { - DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment(deviceSpecificSettingsCustomizer); + final DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment(); fragment.setSettingsFileSuffix(settingsFileSuffix, supportedSettings, supportedLanguages); + fragment.setDeviceSpecificSettingsCustomizer(deviceSpecificSettingsCustomizer); return fragment; } @@ -895,12 +730,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp if (pref != null) { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newVal) { - invokeLater(new Runnable() { - @Override - public void run() { - GBApplication.deviceService().onSendConfiguration(preferenceKey); - } - }); + notifyPreferenceChanged(preferenceKey); if (extraListener != null) { return extraListener.onPreferenceChange(preference, newVal); @@ -962,7 +792,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp final Preference preference = findPreference(key); if (preference == null) { - LOG.warn("Preference {} not found, ignoring", key); + LOG.warn("Preference {} not found", key); return; } @@ -976,11 +806,18 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp } else if (preference instanceof ListPreference) { final ListPreference listPreference = (ListPreference) preference; listPreference.setValue(prefs.getString(key, listPreference.getValue())); + } else if (preference instanceof EditTextPreference) { + final EditTextPreference editTextPreference = (EditTextPreference) preference; + editTextPreference.setText(prefs.getString(key, editTextPreference.getText())); } else if (preference instanceof PreferenceScreen) { // Ignoring } else { LOG.warn("Unknown preference class {}, ignoring", preference.getClass()); } + + if (deviceSpecificSettingsCustomizer != null) { + deviceSpecificSettingsCustomizer.onPreferenceChange(preference, DeviceSpecificSettingsFragment.this); + } } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java index cb2b6ef9c..47d4a4095 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java @@ -39,6 +39,13 @@ public interface DeviceSpecificSettingsHandler { */ void addPreferenceHandlerFor(final String preferenceKey); + /** + * Notify the device that a preference changed. + * + * @param preferenceKey the preference key. + */ + void notifyPreferenceChanged(final String preferenceKey); + /** * Adds a preference handler for a preference key. On change, this handler calls the provided extra listener, and then sends the preference to the device. * diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java index 9f3fd5c19..8c17cca77 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/DeviceCandidateAdapter.java @@ -18,6 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.adapter; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -25,6 +26,7 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; +import java.util.ArrayList; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; @@ -65,27 +67,24 @@ public class DeviceCandidateAdapter extends ArrayAdapter { deviceAddressLabel.setText(device.getMacAddress()); deviceImageView.setImageResource(device.getDeviceType().getIcon()); - String status = ""; + final List statusLines = new ArrayList<>(); if (device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) { - status += getContext().getString(R.string.device_is_currently_bonded); + statusLines.add(getContext().getString(R.string.device_is_currently_bonded)); if (!GBApplication.getPrefs().getBoolean("ignore_bonded_devices", true)) { // This could be passed to the constructor instead deviceImageView.setImageResource(device.getDeviceType().getDisabledIcon()); } } if (!device.getDeviceType().isSupported()) { - status += " UNSUPPORTED"; + statusLines.add(getContext().getString(R.string.device_unsupported)); } DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device); if (coordinator.getBondingStyle() == DeviceCoordinator.BONDING_STYLE_REQUIRE_KEY) { - if (device.getDevice().getBondState() == BluetoothDevice.BOND_BONDED) { - status += "\n"; - } - status += getContext().getString(R.string.device_requires_key); + statusLines.add(getContext().getString(R.string.device_requires_key)); } - deviceStatus.setText(status); + deviceStatus.setText(TextUtils.join("\n", statusLines)); return view; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java index 696dae035..72c7acefb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiService.java @@ -164,6 +164,8 @@ public class HuamiService { public static final byte[] COMMAND_DISABLE_DISCONNECT_NOTIFCATION = new byte[]{ENDPOINT_DISPLAY, 0x0c, 0x00, 0x00, 0, 0, 0, 0}; public static final byte[] COMMAND_REQUEST_ALARMS = new byte[]{0x0d}; + public static final byte[] COMMAND_REQUEST_ALARMS_WITH_TIMES = new byte[]{(byte) 0xff,0x01,0x00,0x00,0x00}; + public static final byte[] COMMAND_REQUEST_GPS_VERSION = new byte[]{0x0e}; // The third byte controls the threshold, in minutes diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopCoordinator.java new file mode 100644 index 000000000..b56b44e30 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopCoordinator.java @@ -0,0 +1,62 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpop; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbipu.AmazfitBipUCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class AmazfitPopCoordinator extends AmazfitBipUCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(AmazfitPopCoordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.AMAZFITPOP; + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && (name.equalsIgnoreCase("Amazfit Pop"))) { + return DeviceType.AMAZFITPOP; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + AmazfitPopFWInstallHandler handler = new AmazfitPopFWInstallHandler(uri, context); + return handler.isValid() ? handler : null; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWHelper.java new file mode 100644 index 000000000..7fa6bb82d --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWHelper.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpop; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpoppro.AmazfitPopProFirmwareInfo; + +public class AmazfitPopFWHelper extends HuamiFWHelper { + + public AmazfitPopFWHelper(Uri uri, Context context) throws IOException { + super(uri, context); + } + + @Override + protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { + firmwareInfo = new AmazfitPopProFirmwareInfo(wholeFirmwareBytes); + if (!firmwareInfo.isHeaderValid()) { + throw new IllegalArgumentException("Not an Amazfit Pop Pro firmware"); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWInstallHandler.java new file mode 100644 index 000000000..5473b748e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpop/AmazfitPopFWInstallHandler.java @@ -0,0 +1,49 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpop; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +class AmazfitPopFWInstallHandler extends AbstractMiBandFWInstallHandler { + AmazfitPopFWInstallHandler(Uri uri, Context context) { + super(uri, context); + } + + @Override + protected String getFwUpgradeNotice() { + return mContext.getString(R.string.fw_upgrade_notice_amazfitbip, helper.getHumanFirmwareVersion()); + } + + @Override + protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException { + return new AmazfitPopFWHelper(uri, context); + } + + @Override + protected boolean isSupportedDeviceType(GBDevice device) { + return device.getType() == DeviceType.AMAZFITPOP; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProCoordinator.java new file mode 100644 index 000000000..ceb953ae4 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProCoordinator.java @@ -0,0 +1,62 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpoppro; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitbipupro.AmazfitBipUProCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class AmazfitPopProCoordinator extends AmazfitBipUProCoordinator { + private static final Logger LOG = LoggerFactory.getLogger(AmazfitPopProCoordinator.class); + + @Override + public DeviceType getDeviceType() { + return DeviceType.AMAZFITPOPPRO; + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + try { + BluetoothDevice device = candidate.getDevice(); + String name = device.getName(); + if (name != null && (name.equalsIgnoreCase("Amazfit Pop Pro"))) { + return DeviceType.AMAZFITPOPPRO; + } + } catch (Exception ex) { + LOG.error("unable to check device support", ex); + } + return DeviceType.UNKNOWN; + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + AmazfitPopProFWInstallHandler handler = new AmazfitPopProFWInstallHandler(uri, context); + return handler.isValid() ? handler : null; + } + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWHelper.java new file mode 100644 index 000000000..46497dd88 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWHelper.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpoppro; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpoppro.AmazfitPopProFirmwareInfo; + +public class AmazfitPopProFWHelper extends HuamiFWHelper { + + public AmazfitPopProFWHelper(Uri uri, Context context) throws IOException { + super(uri, context); + } + + @Override + protected void determineFirmwareInfo(byte[] wholeFirmwareBytes) { + firmwareInfo = new AmazfitPopProFirmwareInfo(wholeFirmwareBytes); + if (!firmwareInfo.isHeaderValid()) { + throw new IllegalArgumentException("Not an Amazfit Pop Pro firmware"); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWInstallHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWInstallHandler.java new file mode 100644 index 000000000..494425586 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitpoppro/AmazfitPopProFWInstallHandler.java @@ -0,0 +1,49 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpoppro; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.AbstractMiBandFWInstallHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +class AmazfitPopProFWInstallHandler extends AbstractMiBandFWInstallHandler { + AmazfitPopProFWInstallHandler(Uri uri, Context context) { + super(uri, context); + } + + @Override + protected String getFwUpgradeNotice() { + return mContext.getString(R.string.fw_upgrade_notice_amazfitbip, helper.getHumanFirmwareVersion()); + } + + @Override + protected AbstractMiBandFWHelper createHelper(Uri uri, Context context) throws IOException { + return new AmazfitPopProFWHelper(uri, context); + } + + @Override + protected boolean isSupportedDeviceType(GBDevice device) { + return device.getType() == DeviceType.AMAZFITPOPPRO; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java index ead718bab..0a3b5cd87 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java @@ -394,6 +394,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem widgets.add(new HybridHRWatchfaceWidget(widgetName, layoutItem.getJSONObject("pos").getInt("x"), layoutItem.getJSONObject("pos").getInt("y"), + layoutItem.getJSONObject("size").getInt("w"), + layoutItem.getJSONObject("size").getInt("h"), widgetColor, widgetTimezone)); } catch (JSONException e) { @@ -408,6 +410,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem widgets.add(new HybridHRWatchfaceWidget(widgetName, layoutItem.getJSONObject("pos").getInt("x"), layoutItem.getJSONObject("pos").getInt("y"), + layoutItem.getJSONObject("size").getInt("w"), + layoutItem.getJSONObject("size").getInt("h"), widgetColor, widgetUpdateTimeout, widgetTimeoutHideText, @@ -416,6 +420,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem widgets.add(new HybridHRWatchfaceWidget(widgetName, layoutItem.getJSONObject("pos").getInt("x"), layoutItem.getJSONObject("pos").getInt("y"), + layoutItem.getJSONObject("size").getInt("w"), + layoutItem.getJSONObject("size").getInt("h"), widgetColor)); } } @@ -603,6 +609,13 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem posY.setText("120"); } }); + // Set widget size + final LinearLayout sizeLayout = layout.findViewById(R.id.watchface_widget_size_layout); + sizeLayout.setVisibility(View.GONE); + final EditText widgetWidth = layout.findViewById(R.id.watchface_widget_width); + if ((widget != null) && (widget.getWidth() >= 0)) { + widgetWidth.setText(Integer.toString(widget.getWidth())); + } // Populate timezone spinner String[] timezonesList = TimeZone.getAvailableIDs(); final Spinner tzSpinner = layout.findViewById(R.id.watchface_widget_timezone_spinner); @@ -641,8 +654,10 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem timezoneLayout.setVisibility(View.GONE); } if (selectedType.equals("widgetCustom")) { + sizeLayout.setVisibility(View.VISIBLE); updateTimeoutLayout.setVisibility(View.VISIBLE); } else { + sizeLayout.setVisibility(View.GONE); updateTimeoutLayout.setVisibility(View.GONE); } } @@ -678,6 +693,12 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem if (selectedPosX > 240) selectedPosX = 240; if (selectedPosY < 1) selectedPosY = 1; if (selectedPosY > 240) selectedPosY = 240; + int selectedWidth = 76; + try { + selectedWidth = Integer.parseInt(widgetWidth.getText().toString()); + } catch (NumberFormatException e) { + LOG.warn("Error parsing input", e); + } String selectedType = widgetTypesArray.get(typeSpinner.getSelectedItemPosition()); String selectedTZ = tzSpinner.getSelectedItem().toString(); int selectedUpdateTimeout = 0; @@ -694,11 +715,11 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem boolean selectedTimeoutShowCircle = timeoutShowCircle.isChecked(); HybridHRWatchfaceWidget widgetConfig; if (selectedType.equals("widget2ndTZ")) { - widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition(), selectedTZ); + widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, 76, 76, colorSpinner.getSelectedItemPosition(), selectedTZ); } else if (selectedType.equals("widgetCustom")) { - widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition(), selectedUpdateTimeout, selectedTimeoutHideText, selectedTimeoutShowCircle); + widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, selectedWidth, 76, colorSpinner.getSelectedItemPosition(), selectedUpdateTimeout, selectedTimeoutHideText, selectedTimeoutShowCircle); } else { - widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, colorSpinner.getSelectedItemPosition()); + widgetConfig = new HybridHRWatchfaceWidget(selectedType, selectedPosX, selectedPosY, 76, 76, colorSpinner.getSelectedItemPosition()); } if (index >= 0) { widgets.set(index, widgetConfig); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java index 2ab3ffd8c..b0cdfd227 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceFactory.java @@ -105,8 +105,8 @@ public class HybridHRWatchfaceFactory { return; } JSONObject size = new JSONObject(); - size.put("w", 76); - size.put("h", 76); + size.put("w", widgetDesc.getWidth()); + size.put("h", widgetDesc.getHeight()); widget.put("size", size); JSONObject pos = new JSONObject(); pos.put("x", widgetDesc.getPosX()); @@ -336,8 +336,8 @@ public class HybridHRWatchfaceFactory { complicationContent.put("inversion", "#$e"); dimension = new JSONObject(); dimension.put("type", "rigid"); - dimension.put("width", 76); - dimension.put("height", 76); + dimension.put("width", "#size.w"); + dimension.put("height", "#size.h"); complicationContent.put("dimension", dimension); placement = new JSONObject(); placement.put("type", "relative"); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceWidget.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceWidget.java index 921966dda..63aa3fe2e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceWidget.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceWidget.java @@ -31,7 +31,9 @@ public class HybridHRWatchfaceWidget { private String widgetType; private int posX; private int posY; - private int color = 0; + private int width; + private int height; + private int color; private String timezone; private int updateTimeout = -1; private boolean timeoutHideText = true; @@ -40,18 +42,20 @@ public class HybridHRWatchfaceWidget { public static int COLOR_WHITE = 0; public static int COLOR_BLACK = 1; - public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color) { + public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color) { this.widgetType = widgetType; this.posX = posX; this.posY = posY; + this.width = width; + this.height = height; this.color = color; } - public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color, String timezone) { - this(widgetType, posX, posY, color); + public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color, String timezone) { + this(widgetType, posX, posY, width, height, color); this.timezone = timezone; } - public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int color, int updateTimeout, boolean timeoutHideText, boolean timeoutShowCircle) { - this(widgetType, posX, posY, color); + public HybridHRWatchfaceWidget(String widgetType, int posX, int posY, int width, int height, int color, int updateTimeout, boolean timeoutHideText, boolean timeoutShowCircle) { + this(widgetType, posX, posY, width, height, color); this.updateTimeout = updateTimeout; this.timeoutHideText = timeoutHideText; this.timeoutShowCircle = timeoutShowCircle; @@ -100,6 +104,20 @@ public class HybridHRWatchfaceWidget { this.posY = posY; } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + + public void setWidth(int width) { + this.width = width; + } + public void setHeight(int height) { + this.height = height; + } + public int getColor() { return color; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java index 1834e462e..10e6a2ea1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesCoordinator.java @@ -41,7 +41,7 @@ public abstract class SonyHeadphonesCoordinator extends AbstractDeviceCoordinato @Override public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { - return new SonyHeadphonesSettingsCustomizer(); + return new SonyHeadphonesSettingsCustomizer(device); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java index 7cb85c9af..3bb3148dc 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java @@ -18,18 +18,101 @@ package nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_CONTROL; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_AUDIO_CODEC; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_1000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_16000; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_2500; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_400; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BAND_6300; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_BASS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_EQUALIZER_MODE; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_FOCUS_VOICE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_SURROUND_MODE; + +import android.app.AlertDialog; +import android.content.Context; +import android.os.Parcel; + +import androidx.preference.EditTextPreference; + +import android.app.ProgressDialog; +import android.content.DialogInterface; import androidx.preference.ListPreference; import androidx.preference.Preference; +import java.util.Arrays; +import java.util.List; import java.util.Locale; +import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.prefs.AmbientSoundControl; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus; public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsCustomizer { + private ProgressDialog ancOptimizerProgressDialog; + + final GBDevice device; + + public SonyHeadphonesSettingsCustomizer(final GBDevice device) { + this.device = device; + } + + @Override + public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) { + // Disable equalizer, sound position and surround mode if not in SBC codec, for WH-1000XM3 + // TODO: Should the coordinator be responsible for this compatibility check? + if (preference.getKey().equals(PREF_SONY_AUDIO_CODEC) && device.getType().equals(DeviceType.SONY_WH_1000XM3)) { + final boolean isSbcCodec = ((EditTextPreference) preference).getText().equalsIgnoreCase("sbc"); + + final List prefsToDisable = Arrays.asList( + handler.findPreference(PREF_SONY_EQUALIZER), + handler.findPreference(PREF_SONY_EQUALIZER_MODE), + handler.findPreference(PREF_SONY_EQUALIZER_BAND_400), + handler.findPreference(PREF_SONY_EQUALIZER_BAND_1000), + handler.findPreference(PREF_SONY_EQUALIZER_BAND_2500), + handler.findPreference(PREF_SONY_EQUALIZER_BAND_6300), + handler.findPreference(PREF_SONY_EQUALIZER_BAND_16000), + handler.findPreference(PREF_SONY_EQUALIZER_BASS), + handler.findPreference(PREF_SONY_SOUND_POSITION), + handler.findPreference(PREF_SONY_SURROUND_MODE) + ); + + for (Preference pref : prefsToDisable) { + if (pref != null) { + pref.setEnabled(isSbcCodec); + } + } + } + + // Handle ANC Optimizer status + if (preference.getKey().equals(PREF_SONY_NOISE_OPTIMIZER_STATUS)) { + final EditTextPreference optimizerStatusPreference = (EditTextPreference) preference; + final NoiseCancellingOptimizerStatus optimizerStatus = NoiseCancellingOptimizerStatus.valueOf(optimizerStatusPreference.getText().toUpperCase(Locale.ROOT)); + + if (ancOptimizerProgressDialog != null) { + switch (optimizerStatus) { + case FINISHED: + case NOT_RUNNING: + ancOptimizerProgressDialog.dismiss(); + ancOptimizerProgressDialog = null; + break; + default: + ancOptimizerProgressDialog.setMessage(optimizerStatus.i18n(preference.getContext())); + } + } + } + } + @Override public void customizeSettings(final DeviceSpecificSettingsHandler handler) { // Only enable the focus on voice check and voice level slider if the ambient sound control mode is ambient sound @@ -52,5 +135,76 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC ambientSoundControlPrefListener.onPreferenceChange(ambientSoundControl, ambientSoundControl.getValue()); handler.addPreferenceHandlerFor(PREF_SONY_AMBIENT_SOUND_CONTROL, ambientSoundControlPrefListener); } + + // ANC Optimizer + + final Preference ancOptimizer = handler.findPreference("pref_sony_anc_optimizer"); + + if (ancOptimizer != null) { + ancOptimizer.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + if (ancOptimizerProgressDialog != null) { + // Already optimizing + return true; + } + + final Context context = preference.getContext(); + + new AlertDialog.Builder(context) + .setTitle(R.string.sony_anc_optimize_confirmation_title) + .setMessage(R.string.sony_anc_optimize_confirmation_description) + .setIcon(R.drawable.ic_hearing) + .setPositiveButton(R.string.start, new DialogInterface.OnClickListener() { + public void onClick(final DialogInterface dialog, final int whichButton) { + handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_START); + + ancOptimizerProgressDialog = new ProgressDialog(context); + ancOptimizerProgressDialog.setCancelable(false); + ancOptimizerProgressDialog.setMessage(context.getString(R.string.sony_anc_optimizer_status_starting)); + ancOptimizerProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + ancOptimizerProgressDialog.setProgress(0); + ancOptimizerProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.Cancel), new DialogInterface.OnClickListener() { + @Override + public void onClick(final DialogInterface dialog, final int which) { + dialog.dismiss(); + ancOptimizerProgressDialog = null; + handler.notifyPreferenceChanged(PREF_SONY_NOISE_OPTIMIZER_CANCEL); + } + }); + + ancOptimizerProgressDialog.show(); + } + }) + .setNegativeButton(android.R.string.cancel, null) + .show(); + + return true; + } + }); + } + } + + public static final Creator CREATOR = new Creator() { + @Override + public SonyHeadphonesSettingsCustomizer createFromParcel(final Parcel in) { + final GBDevice device = in.readParcelable(SonyHeadphonesSettingsCustomizer.class.getClassLoader()); + return new SonyHeadphonesSettingsCustomizer(device); + } + + @Override + public SonyHeadphonesSettingsCustomizer[] newArray(final int size) { + return new SonyHeadphonesSettingsCustomizer[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + dest.writeParcelable(device, 0); } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWFSP800NCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWFSP800NCoordinator.java index 54cf59d81..806f39e31 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWFSP800NCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWFSP800NCoordinator.java @@ -69,8 +69,9 @@ public class SonyWFSP800NCoordinator extends SonyHeadphonesCoordinator { R.xml.devicesettings_header_system, R.xml.devicesettings_sony_headphones_button_modes_left_right, R.xml.devicesettings_sony_headphones_pause_when_taken_off, - R.xml.devicesettings_sony_wf_sp800n, - R.xml.devicesettings_sony_headphones_notifications_voice_guide + R.xml.devicesettings_automatic_power_off_when_taken_off, + R.xml.devicesettings_sony_headphones_notifications_voice_guide, + R.xml.devicesettings_sony_headphones_device_info }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java index ab4d375e6..16a9ea508 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM3Coordinator.java @@ -27,7 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator { @NonNull @Override - public DeviceType getSupportedType(GBDeviceCandidate candidate) { + public DeviceType getSupportedType(final GBDeviceCandidate candidate) { if (candidate.getName().contains("WH-1000XM3")) { return DeviceType.SONY_WH_1000XM3; } @@ -43,17 +43,19 @@ public class SonyWH1000XM3Coordinator extends SonyHeadphonesCoordinator { @Override public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { return new int[]{ - R.xml.devicesettings_sony_warning_wh1000xm3, R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction, + R.xml.devicesettings_sony_headphones_anc_optimizer, R.xml.devicesettings_header_other, + R.xml.devicesettings_sony_warning_wh1000xm3, R.xml.devicesettings_sony_headphones_equalizer, R.xml.devicesettings_sony_headphones_sound_position, R.xml.devicesettings_sony_headphones_surround_mode, R.xml.devicesettings_sony_headphones_audio_upsampling, R.xml.devicesettings_header_system, R.xml.devicesettings_sony_headphones_touch_sensor_single, - R.xml.devicesettings_automatic_power_off, - R.xml.devicesettings_sony_headphones_notifications_voice_guide + R.xml.devicesettings_automatic_power_off_by_time, + R.xml.devicesettings_sony_headphones_notifications_voice_guide, + R.xml.devicesettings_sony_headphones_device_info }; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java new file mode 100644 index 000000000..fac10a307 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/coordinators/SonyWH1000XM4Coordinator.java @@ -0,0 +1,62 @@ +/* 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.headphones.coordinators; + +import androidx.annotation.NonNull; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.SonyHeadphonesCoordinator; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class SonyWH1000XM4Coordinator extends SonyHeadphonesCoordinator { + @NonNull + @Override + public DeviceType getSupportedType(final GBDeviceCandidate candidate) { + if (candidate.getName().contains("WH-1000XM4")) { + return DeviceType.SONY_WH_1000XM4; + } + + return DeviceType.UNKNOWN; + } + + @Override + public DeviceType getDeviceType() { + return DeviceType.SONY_WH_1000XM4; + } + + @Override + public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { + return new int[]{ + // TODO: Function of [CUSTOM] button + R.xml.devicesettings_sony_headphones_ambient_sound_control_wind_noise_reduction, + R.xml.devicesettings_sony_headphones_anc_optimizer, + R.xml.devicesettings_header_other, + R.xml.devicesettings_sony_headphones_equalizer, + R.xml.devicesettings_sony_headphones_audio_upsampling, + R.xml.devicesettings_header_system, + // TODO R.xml.devicesettings_connect_two_devices, + // TODO R.xml.devicesettings_sony_headphones_speak_to_chat_with_settings, + R.xml.devicesettings_sony_headphones_touch_sensor_single, + R.xml.devicesettings_sony_headphones_pause_when_taken_off, + R.xml.devicesettings_automatic_power_off_when_taken_off, + R.xml.devicesettings_sony_headphones_notifications_voice_guide, + R.xml.devicesettings_sony_headphones_device_info + }; + } +} 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 9dc4a9dc7..841a9161b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -54,7 +54,7 @@ public enum DeviceType { AMAZFITGTS2(27, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_gts2), AMAZFITBIPU(28, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipu), AMAZFITVERGEL(29, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_vergel), - AMAZFITBIPUPRO(30, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipu), + AMAZFITBIPUPRO(30, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_bipupro), AMAZFITNEO(31, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_neo), AMAZFITGTS2_MINI(32, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_gts2_mini), ZEPP_E(33, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_zepp_e), @@ -63,6 +63,8 @@ public enum DeviceType { AMAZFITX(36, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_amazfit_x), MIBAND6(37, R.drawable.ic_device_miband2, R.drawable.ic_device_miband2_disabled, R.string.devicetype_miband6), AMAZFITTREXPRO(38, R.drawable.ic_device_zetime, R.drawable.ic_device_zetime_disabled, R.string.devicetype_amazfit_trex_pro), + AMAZFITPOP(39, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_pop), + AMAZFITPOPPRO(10040, R.drawable.ic_device_amazfit_bip, R.drawable.ic_device_amazfit_bip_disabled, R.string.devicetype_amazfit_pop_pro), HPLUS(40, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_hplus), MAKIBESF68(41, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_makibes_f68), EXRIZUK8(42, R.drawable.ic_device_hplus, R.drawable.ic_device_hplus_disabled, R.string.devicetype_exrizu_k8), @@ -105,6 +107,7 @@ public enum DeviceType { 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), SONY_WF_SP800N(431, R.drawable.ic_device_galaxy_buds, R.drawable.ic_device_galaxy_buds_disabled, R.string.devicetype_sony_wf_sp800n), + SONY_WH_1000XM4(432, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_sony_wh_1000xm4), BOSE_QC35(440, R.drawable.ic_device_headphones, R.drawable.ic_device_headphones_disabled, R.string.devicetype_bose_qc35), VESC_NRF(500, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc), VESC_HM10(501, R.drawable.ic_device_vesc, R.drawable.ic_device_vesc_disabled, R.string.devicetype_vesc), 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 b35e55c97..936a4ee9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -59,6 +59,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.Am import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitgts2.AmazfitGTS2eSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitneo.AmazfitNeoSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpop.AmazfitPopSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitpoppro.AmazfitPopProSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrex.AmazfitTRexSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfittrexpro.AmazfitTRexProSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitvergel.AmazfitVergeLSupport; @@ -204,6 +206,12 @@ public class DeviceSupportFactory { case AMAZFITBIPUPRO: deviceSupport = new ServiceDeviceSupport(new AmazfitBipUProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case AMAZFITPOP: + deviceSupport = new ServiceDeviceSupport(new AmazfitPopSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; + case AMAZFITPOPPRO: + deviceSupport = new ServiceDeviceSupport(new AmazfitPopProSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case AMAZFITGTR: deviceSupport = new ServiceDeviceSupport(new AmazfitGTRSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; @@ -375,6 +383,9 @@ public class DeviceSupportFactory { case SONY_WH_1000XM3: deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; + case SONY_WH_1000XM4: + deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + break; case SONY_WF_SP800N: deviceSupport = new ServiceDeviceSupport(new SonyHeadphonesSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/CasioGBX100DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/CasioGBX100DeviceSupport.java index a65cafff5..8ee737e4c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/CasioGBX100DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/CasioGBX100DeviceSupport.java @@ -424,6 +424,7 @@ public class CasioGBX100DeviceSupport extends CasioSupport implements SharedPref public void writeCurrentTime(TransactionBuilder builder) { byte[] arr = new byte[11]; + arr[0] = CasioConstants.characteristicToByte.get("CASIO_CURRENT_TIME"); byte[] tmp = prepareCurrentTime(); System.arraycopy(tmp, 0, arr, 1, 10); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java index e2d487e2a..5ac91e11f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiDeviceEvent.java @@ -31,5 +31,6 @@ public class HuamiDeviceEvent { public static final byte TICK_30MIN = 0x0e; // unsure public static final byte FIND_PHONE_STOP = 0x0f; public static final byte MTU_REQUEST = 0x16; + public static final byte ALARM_CHANGED = 0x1a; public static final byte MUSIC_CONTROL = (byte) 0xfe; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index 654927db9..2b0845609 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -1473,7 +1473,8 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { processDeviceEvent(HuamiDeviceEvent.START_NONWEAR); break; case HuamiDeviceEvent.ALARM_TOGGLED: - LOG.info("An alarm was toggled"); + case HuamiDeviceEvent.ALARM_CHANGED: + LOG.info("An alarm was toggled or changed"); TransactionBuilder builder = new TransactionBuilder("requestAlarms"); requestAlarms(builder); builder.queue(getQueue()); @@ -1810,40 +1811,78 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { gbDevice.setFirmwareVersion2(gpsVersion); } else if (value[1] == 0x0d) { LOG.info("got alarms from watch"); - decodeAndUpdateAlarmStatus(value); + decodeAndUpdateAlarmStatus(value, false); } else { LOG.warn("got configuration info we do not handle yet " + GB.hexdump(value, 3, -1)); } + } else if (value[0] == ((byte) 0x80) && value[1] == 0x01 && value[2] == (byte) 0xc0 && value[3] == 0x00 && + value[4] == 0x01 && value[5] == 0x00 && value[6] == 0x00 && value[7] == 0x00 && value[8] == 0x01) { + LOG.info("got full alarm configuration."); // TODO: with low mtu they come in chunks + byte[] alarmData = new byte[value.length - 9]; + System.arraycopy(value, 9, alarmData, 0, alarmData.length); + decodeAndUpdateAlarmStatus(alarmData, true); } else { - LOG.warn("error received from configuration request " + GB.hexdump(value, 0, -1)); + LOG.warn("unknown response got from configuration request " + GB.hexdump(value, 0, -1)); } } - private void decodeAndUpdateAlarmStatus(byte[] response) { + private void decodeAndUpdateAlarmStatus(byte[] response, boolean withTimes) { List alarms = DBHelper.getAlarms(gbDevice); int maxAlarms = 10; + + //FIXME: we can rather have a full struct here probably boolean[] alarmsInUse = new boolean[maxAlarms]; boolean[] alarmsEnabled = new boolean[maxAlarms]; - int nr_alarms = response[8]; + byte[] alarmsMinute = new byte[maxAlarms]; + byte[] alarmsHour = new byte[maxAlarms]; + byte[] alarmsRepetition = new byte[maxAlarms]; + + int nr_alarms; + byte enable_flag; + if (withTimes) { + nr_alarms = response.length / 4; + enable_flag = (byte) 0x80; + } else { + nr_alarms = response[8]; + enable_flag = (byte) 0x10; + } for (int i = 0; i < nr_alarms; i++) { - byte alarm_data = response[9 + i]; + int offset; + if (withTimes) { + offset = i * 4; + } else { + offset = 9 + i; + } + byte alarm_data = response[offset]; int index = alarm_data & 0xf; if (index >= maxAlarms) { GB.toast("Unexpected alarm index from device, ignoring: " + index, Toast.LENGTH_SHORT, GB.ERROR); return; } alarmsInUse[index] = true; - boolean enabled = (alarm_data & 0x10) == 0x10; + boolean enabled = (alarm_data & enable_flag) == enable_flag; alarmsEnabled[index] = enabled; + if (withTimes) { + alarmsHour[index] = response[offset + 1]; + alarmsMinute[index] = response[offset + 2]; + alarmsRepetition[index] = response[offset + 3]; + } + LOG.info("alarm " + index + " is enabled:" + enabled); } for (nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm : alarms) { - boolean enabled = alarmsEnabled[alarm.getPosition()]; - boolean unused = !alarmsInUse[alarm.getPosition()]; - if (alarm.getEnabled() != enabled || alarm.getUnused() != unused) { - LOG.info("updating alarm index " + alarm.getPosition() + " unused=" + unused + ", enabled=" + enabled); + int pos = alarm.getPosition(); + boolean enabled = alarmsEnabled[pos]; + boolean unused = !alarmsInUse[pos]; + if (alarm.getEnabled() != enabled || alarm.getUnused() != unused || (withTimes && !unused && (alarm.getHour() != alarmsHour[pos] || alarm.getMinute() != alarmsMinute[pos] || alarm.getRepetition() != alarmsRepetition[pos]))) { + LOG.info("updating alarm index " + pos + " unused=" + unused + ", enabled=" + enabled); alarm.setEnabled(enabled); alarm.setUnused(unused); + if (withTimes && !unused) { + alarm.setHour(alarmsHour[pos]); + alarm.setMinute(alarmsMinute[pos]); + alarm.setRepetition(alarmsRepetition[pos]); + } DBHelper.store(alarm); Intent intent = new Intent(DeviceService.ACTION_SAVE_ALARMS); LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); @@ -3140,7 +3179,9 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { private HuamiSupport requestAlarms(TransactionBuilder builder) { LOG.info("Requesting alarms"); - writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS); + //FIXME: on older devices only the first one works, and on newer only the last is sufficiant + writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS); + writeToConfiguration(builder, HuamiService.COMMAND_REQUEST_ALARMS_WITH_TIMES); return this; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopFirmwareInfo.java new file mode 100644 index 000000000..1b23131e2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopFirmwareInfo.java @@ -0,0 +1,81 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpop; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4FirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + +public class AmazfitPopFirmwareInfo extends HuamiFirmwareInfo { + + private static Map crcToVersion = new HashMap<>(); + + static { + // fw + + // resources + + // font + } + + public AmazfitPopFirmwareInfo(byte[] bytes) { + super(bytes); + } + + @Override + protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { + + if (ArrayUtils.equals(bytes, MiBand4FirmwareInfo.FW_HEADER, MiBand4FirmwareInfo.FW_HEADER_OFFSET)) { + if (searchString32BitAligned(bytes, "\0\0Amazfit Pop")) { + return HuamiFirmwareType.FIRMWARE; + } + return HuamiFirmwareType.INVALID; + } + + if (ArrayUtils.startsWith(bytes,NEWRES_HEADER)) { + return HuamiFirmwareType.RES; + } + + if (ArrayUtils.startsWith(bytes, UIHH_HEADER) && (bytes[4] == 1 || bytes[4] == 2)) { + return HuamiFirmwareType.WATCHFACE; + } + + if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) { + if (bytes[10] == 0x01 || bytes[10] == 0x06 || bytes[10] == 0x03) { + return HuamiFirmwareType.FONT; + } + } + + return HuamiFirmwareType.INVALID; + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return isHeaderValid() && device.getType() == DeviceType.AMAZFITPOP; + } + + @Override + protected Map getCrcMap() { + return crcToVersion; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopSupport.java new file mode 100644 index 000000000..5d0a113ca --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpop/AmazfitPopSupport.java @@ -0,0 +1,33 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpop; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpop.AmazfitPopFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbipu.AmazfitBipUSupport; + +public class AmazfitPopSupport extends AmazfitBipUSupport { + @Override + public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + return new AmazfitPopFWHelper(uri, context); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProFirmwareInfo.java new file mode 100644 index 000000000..28ddfc4d2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProFirmwareInfo.java @@ -0,0 +1,81 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpoppro; + +import java.util.HashMap; +import java.util.Map; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiFirmwareType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband4.MiBand4FirmwareInfo; +import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; + +public class AmazfitPopProFirmwareInfo extends HuamiFirmwareInfo { + + private static Map crcToVersion = new HashMap<>(); + + static { + // fw + + // resources + + // font + } + + public AmazfitPopProFirmwareInfo(byte[] bytes) { + super(bytes); + } + + @Override + protected HuamiFirmwareType determineFirmwareType(byte[] bytes) { + + if (ArrayUtils.equals(bytes, MiBand4FirmwareInfo.FW_HEADER, MiBand4FirmwareInfo.FW_HEADER_OFFSET)) { + if (searchString32BitAligned(bytes, "\0\0Amazfit Pop Pro")) { + return HuamiFirmwareType.FIRMWARE; + } + return HuamiFirmwareType.INVALID; + } + + if (ArrayUtils.startsWith(bytes,NEWRES_HEADER)) { + return HuamiFirmwareType.RES; + } + + if (ArrayUtils.startsWith(bytes, UIHH_HEADER) && (bytes[4] == 1 || bytes[4] == 2)) { + return HuamiFirmwareType.WATCHFACE; + } + + if (ArrayUtils.startsWith(bytes, NEWFT_HEADER)) { + if (bytes[10] == 0x01 || bytes[10] == 0x06 || bytes[10] == 0x03) { + return HuamiFirmwareType.FONT; + } + } + + return HuamiFirmwareType.INVALID; + } + + @Override + public boolean isGenerallyCompatibleWith(GBDevice device) { + return isHeaderValid() && device.getType() == DeviceType.AMAZFITPOPPRO; + } + + @Override + protected Map getCrcMap() { + return crcToVersion; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProSupport.java new file mode 100644 index 000000000..e8ef61315 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitpoppro/AmazfitPopProSupport.java @@ -0,0 +1,33 @@ +/* Copyright (C) 2022 Andreas Shimokawa + + 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.huami.amazfitpoppro; + +import android.content.Context; +import android.net.Uri; + +import java.io.IOException; + +import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpoppro.AmazfitPopProFWHelper; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitbipupro.AmazfitBipUProSupport; + +public class AmazfitPopProSupport extends AmazfitBipUProSupport { + @Override + public HuamiFWHelper createFWHelper(Uri uri, Context context) throws IOException { + return new AmazfitPopProFWHelper(uri, context); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesIoThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesIoThread.java index 975543a1b..88d8b6bcf 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesIoThread.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesIoThread.java @@ -41,8 +41,7 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread { private final SonyHeadphonesProtocol mProtocol; - // Track whether we got the first reply - private boolean firstReply = false; + // Track whether we got the first init reply private final Handler handler = new Handler(); private int initRetries = 0; @@ -53,7 +52,7 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread { private final Runnable initSendRunnable = new Runnable() { public void run() { // If we still haven't got any reply, re-send the init - if (!firstReply) { + if (!mProtocol.hasProtocolImplementation()) { if (initRetries++ < 2) { LOG.warn("Init retry {}", initRetries); @@ -103,8 +102,6 @@ public class SonyHeadphonesIoThread extends BtClassicIoThread { msgStream.write(incoming); } while (incoming[0] != Message.MESSAGE_TRAILER); - firstReply = true; - LOG.trace("Raw message: {}", GB.hexdump(msgStream.toByteArray())); return msgStream.toByteArray(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java index a0e253d7c..4e19a517a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/SonyHeadphonesProtocol.java @@ -104,9 +104,10 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { } else if (message.getPayload().length == 6) { LOG.warn("Sony Headphones protocol v2 is not yet supported"); return null; + } else { + LOG.error("Unexpected init response payload length: {}", message.getPayload().length); + return null; } - - return null; } } @@ -151,6 +152,12 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { case DeviceSettingsPreferenceConst.PREF_SONY_AMBIENT_SOUND_LEVEL: configRequest = protocolImpl.setAmbientSoundControl(AmbientSoundControl.fromPreferences(prefs)); break; + case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_START: + configRequest = protocolImpl.startNoiseCancellingOptimizer(true); + break; + case DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_CANCEL: + configRequest = protocolImpl.startNoiseCancellingOptimizer(false); + break; case DeviceSettingsPreferenceConst.PREF_SONY_SOUND_POSITION: configRequest = protocolImpl.setSoundPosition(SoundPosition.fromPreferences(prefs)); break; @@ -187,7 +194,15 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { case DeviceSettingsPreferenceConst.PREF_SONY_NOTIFICATION_VOICE_GUIDE: configRequest = protocolImpl.setVoiceNotifications(VoiceNotifications.fromPreferences(prefs)); break; - + case DeviceSettingsPreferenceConst.PREF_SONY_CONNECT_TWO_DEVICES: + LOG.warn("Connection to two devices not implemented ('{}')", config); + return super.encodeSendConfiguration(config); + case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT: + case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_SENSITIVITY: + case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_FOCUS_ON_VOICE: + case DeviceSettingsPreferenceConst.PREF_SONY_SPEAK_TO_CHAT_TIMEOUT: + LOG.warn("Speak-to-chat is not implemented ('{}')", config); + return super.encodeSendConfiguration(config); default: LOG.warn("Unknown config '{}'", config); return super.encodeSendConfiguration(config); @@ -202,10 +217,6 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { public byte[] encodeTestNewFunction() { //return Request.fromHex(MessageType.COMMAND_1, "c40100").encode(sequenceNumber); - if (protocolImpl != null) { - return protocolImpl.startNoiseCancellingOptimizer().encode(sequenceNumber); - } - return null; } @@ -252,6 +263,10 @@ public class SonyHeadphonesProtocol extends GBDeviceProtocol { return requestQueue.remove().encode(sequenceNumber); } + public boolean hasProtocolImplementation() { + return protocolImpl != null; + } + private GBDeviceEvent handleAck() { pendingAcks--; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java index 3af5befc4..a1be9a21e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/AbstractSonyProtocolImpl.java @@ -50,6 +50,8 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request setAmbientSoundControl(final AmbientSoundControl config); + public abstract Request getNoiseCancellingOptimizerState(); + public abstract Request getAudioCodec(); public abstract Request getBattery(final BatteryType batteryType); @@ -94,7 +96,7 @@ public abstract class AbstractSonyProtocolImpl { public abstract Request setVoiceNotifications(final VoiceNotifications config); - public abstract Request startNoiseCancellingOptimizer(); + public abstract Request startNoiseCancellingOptimizer(final boolean start); public abstract Request powerOff(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java index c79b410d5..881407c7a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/PayloadType.java @@ -53,7 +53,12 @@ public enum PayloadType { AMBIENT_SOUND_CONTROL_SET(MessageType.COMMAND_1, 0x68), AMBIENT_SOUND_CONTROL_NOTIFY(MessageType.COMMAND_1, 0x69), - NOISE_CANCELLING_OPTIMIZER_START_REQUEST(MessageType.COMMAND_1, 0x84), + NOISE_CANCELLING_OPTIMIZER_START(MessageType.COMMAND_1, 0x84), + NOISE_CANCELLING_OPTIMIZER_STATUS(MessageType.COMMAND_1, 0x85), + + NOISE_CANCELLING_OPTIMIZER_STATE_GET(MessageType.COMMAND_1, 0x86), + NOISE_CANCELLING_OPTIMIZER_STATE_RET(MessageType.COMMAND_1, 0x87), + NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY(MessageType.COMMAND_1, 0x89), TOUCH_SENSOR_GET(MessageType.COMMAND_1, 0xd6), TOUCH_SENSOR_RET(MessageType.COMMAND_1, 0xd7), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java index b72cdd4e4..9f46600df 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/SonyProtocolImplV1.java @@ -16,6 +16,9 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE; +import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SONY_NOISE_OPTIMIZER_STATUS; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.prot import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.AbstractSonyProtocolImpl; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.AudioCodec; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.BatteryType; +import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.NoiseCancellingOptimizerStatus; import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params.VirtualSoundParam; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -130,6 +134,17 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { return new Request(PayloadType.AMBIENT_SOUND_CONTROL_SET.getMessageType(), buf.array()); } + @Override + public Request getNoiseCancellingOptimizerState() { + return new Request( + PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getMessageType(), + new byte[]{ + PayloadType.NOISE_CANCELLING_OPTIMIZER_STATE_GET.getCode(), + (byte) 0x01 + } + ); + } + @Override public Request getAudioCodec() { return new Request( @@ -399,14 +414,14 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { } @Override - public Request startNoiseCancellingOptimizer() { + public Request startNoiseCancellingOptimizer(final boolean start) { return new Request( - PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getMessageType(), + PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getMessageType(), new byte[]{ - PayloadType.NOISE_CANCELLING_OPTIMIZER_START_REQUEST.getCode(), + PayloadType.NOISE_CANCELLING_OPTIMIZER_START.getCode(), (byte) 0x01, (byte) 0x00, - (byte) 0x01 + (byte) (start ? 0x01 : 0x00) } ); } @@ -447,6 +462,11 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { case AMBIENT_SOUND_CONTROL_RET: case AMBIENT_SOUND_CONTROL_NOTIFY: return handleAmbientSoundControl(payload); + case NOISE_CANCELLING_OPTIMIZER_STATUS: + return handleNoiseCancellingOptimizerStatus(payload); + case NOISE_CANCELLING_OPTIMIZER_STATE_RET: + case NOISE_CANCELLING_OPTIMIZER_STATE_NOTIFY: + return handleNoiseCancellingOptimizerState(payload); case TOUCH_SENSOR_RET: case TOUCH_SENSOR_NOTIFY: return handleTouchSensor(payload); @@ -486,6 +506,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { capabilityRequests.add(getBattery(BatteryType.SINGLE)); capabilityRequests.add(getAudioCodec()); capabilityRequests.add(getAmbientSoundControl()); + capabilityRequests.add(getNoiseCancellingOptimizerState()); capabilityRequests.add(getAudioUpsampling()); capabilityRequests.add(getVoiceNotifications()); capabilityRequests.add(getAutomaticPowerOff()); @@ -494,6 +515,19 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { capabilityRequests.add(getSoundPosition()); capabilityRequests.add(getEqualizer()); break; + case SONY_WH_1000XM4: + capabilityRequests.add(getFirmwareVersion()); + capabilityRequests.add(getBattery(BatteryType.SINGLE)); + capabilityRequests.add(getAudioCodec()); + capabilityRequests.add(getAmbientSoundControl()); + capabilityRequests.add(getNoiseCancellingOptimizerState()); + capabilityRequests.add(getAudioUpsampling()); + capabilityRequests.add(getVoiceNotifications()); + capabilityRequests.add(getAutomaticPowerOff()); + capabilityRequests.add(getTouchSensor()); + capabilityRequests.add(getEqualizer()); + capabilityRequests.add(getPauseWhenTakenOff()); + break; case SONY_WF_SP800N: capabilityRequests.add(getFirmwareVersion()); capabilityRequests.add(getBattery(BatteryType.DUAL)); @@ -580,6 +614,50 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { return Collections.singletonList(eventUpdatePreferences); } + public List handleNoiseCancellingOptimizerStatus(final byte[] payload) { + if (payload.length != 4) { + LOG.warn("Unexpected payload length {}", payload.length); + return Collections.emptyList(); + } + + final NoiseCancellingOptimizerStatus status = NoiseCancellingOptimizerStatus.fromCode(payload[3]); + + if (status == null) { + LOG.warn("Unable to determine noise cancelling opptimizer status from {}", GB.hexdump(payload)); + return Collections.emptyList(); + } + + LOG.info("Noise Cancelling Optimizer status: {}", status); + + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() + .withPreference(PREF_SONY_NOISE_OPTIMIZER_STATUS, status.name().toLowerCase(Locale.ROOT)); + + return Collections.singletonList(event); + } + + public List handleNoiseCancellingOptimizerState(final byte[] payload) { + // 89 01 01 01 01 0A + if (payload.length != 6) { + LOG.warn("Unexpected payload length {}", payload.length); + return Collections.emptyList(); + } + + final float pressure = payload[5] / 10.0f; + + if (pressure <= 0 || pressure > 1.0f) { + LOG.warn("Invalid Noise Cancelling Optimizer pressure: {} atm, ignoring", pressure); + return Collections.emptyList(); + } + + LOG.info("Noise Cancelling Optimizer pressure: {} atm", pressure); + + final GBDeviceEventUpdatePreferences event = new GBDeviceEventUpdatePreferences() + .withPreference(PREF_SONY_NOISE_OPTIMIZER_STATE_PRESSURE, String.format(Locale.getDefault(), "%.2f atm", pressure)); + + return Collections.singletonList(event); + } + + public List handleAudioUpsampling(final byte[] payload) { if (payload.length != 4) { LOG.warn("Unexpected payload length {}", payload.length); @@ -1004,6 +1082,7 @@ public class SonyProtocolImplV1 extends AbstractSonyProtocolImpl { switch (deviceType) { case SONY_WH_1000XM3: + case SONY_WH_1000XM4: return true; case SONY_WF_SP800N: return false; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/AudioCodec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/AudioCodec.java index c3e042783..803d1ab30 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/AudioCodec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/AudioCodec.java @@ -17,6 +17,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.sony.headphones.protocol.impl.v1.params; public enum AudioCodec { + UNKNOWN(0x00), SBC(0x01), AAC(0x02), LDAC(0x10), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java new file mode 100644 index 000000000..c41ec4925 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sony/headphones/protocol/impl/v1/params/NoiseCancellingOptimizerStatus.java @@ -0,0 +1,56 @@ +/* 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.headphones.protocol.impl.v1.params; + +import android.content.Context; + +import java.util.Locale; + +public enum NoiseCancellingOptimizerStatus { + NOT_RUNNING(0x00), + WEARING_CONDITION(0x01), + ATMOSPHERIC_PRESSURE(0x02), + ANALYZING(0x10), + FINISHED(0x11), + ; + + private final byte code; + + NoiseCancellingOptimizerStatus(final int code) { + this.code = (byte) code; + } + + public byte getCode() { + return this.code; + } + + public static NoiseCancellingOptimizerStatus fromCode(final byte code) { + for (final NoiseCancellingOptimizerStatus audioCodec : values()) { + if (audioCodec.code == code) { + return audioCodec; + } + } + + return null; + } + + public String i18n(final Context context) { + final String stringName = String.format("sony_anc_optimizer_status_%s", name().toLowerCase(Locale.ROOT)); + final int stringId = context.getResources().getIdentifier(stringName, "string", context.getPackageName()); + return context.getString(stringId); + } +} 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 8c0baf1a5..bf7753e61 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -71,6 +71,8 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitcor2.AmazfitCor import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr.AmazfitGTRCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr.AmazfitGTRLiteCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgtr2.AmazfitGTR2Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpop.AmazfitPopCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitpoppro.AmazfitPopProCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfittrexpro.AmazfitTRexProCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitx.AmazfitXCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.huami.miband6.MiBand6Coordinator; @@ -108,6 +110,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyWH1000XM4Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator; @@ -269,6 +272,8 @@ public class DeviceHelper { result.add(new AmazfitBipSLiteCoordinator()); result.add(new AmazfitBipUCoordinator()); result.add(new AmazfitBipUProCoordinator()); + result.add(new AmazfitPopCoordinator()); + result.add(new AmazfitPopProCoordinator()); result.add(new AmazfitBand5Coordinator()); result.add(new AmazfitNeoCoordinator()); result.add(new MiBand3Coordinator()); @@ -319,6 +324,7 @@ public class DeviceHelper { result.add(new GalaxyBudsLiveDeviceCoordinator()); result.add(new VescCoordinator()); result.add(new SonyWH1000XM3Coordinator()); + result.add(new SonyWH1000XM4Coordinator()); result.add(new SonyWFSP800NCoordinator()); result.add(new QC35Coordinator()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java index 84ae71688..6675492f0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/LanguageUtils.java @@ -115,6 +115,9 @@ public class LanguageUtils { put('ř',"r"); put('ě',"e"); put('ý',"y"); put('á',"a"); put('í',"i"); put('é',"e"); put('ó',"o"); put('ú',"u"); put('ů',"u"); put('ď',"d"); put('ť',"t"); put('ň',"n"); + // Turkish + put('ı',"i"); + //TODO: these must be configurable. If someone wants to transliterate cyrillic it does not mean his device has no German umlauts // all or nothing is really bad here } diff --git a/app/src/main/res/drawable/ic_auto_awesome.xml b/app/src/main/res/drawable/ic_auto_awesome.xml new file mode 100644 index 000000000..d900f58fb --- /dev/null +++ b/app/src/main/res/drawable/ic_auto_awesome.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_pressure.xml b/app/src/main/res/drawable/ic_pressure.xml new file mode 100644 index 000000000..0b293a8b8 --- /dev/null +++ b/app/src/main/res/drawable/ic_pressure.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_discovery.xml b/app/src/main/res/layout/activity_discovery.xml index 58d4cabdd..a9980b208 100644 --- a/app/src/main/res/layout/activity_discovery.xml +++ b/app/src/main/res/layout/activity_discovery.xml @@ -29,7 +29,7 @@ + android:text="@string/discovery_bluetooth_scan" /> + android:text="@string/discovery_bluetooth_le_scan" /> + + + + + + android:inputType="number" + android:text="60"/> + app:srcCompat="@drawable/ic_settings" /> \ 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 6c6c5f114..f6ee16d4f 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -20,6 +20,7 @@ Español Français Polski + Português Русский Tiếng Việt Türkçe @@ -39,6 +40,7 @@ es fr pl + pt ru vi tr @@ -2020,16 +2022,42 @@ after_3_hour - + @string/sony_automatic_power_off_off @string/sony_automatic_power_off_when_taken_off - + off when_taken_off + + @string/sony_speak_to_chat_sensitivity_auto + @string/sony_speak_to_chat_sensitivity_high + @string/sony_speak_to_chat_sensitivity_low + + + + auto + high + low + + + + @string/sony_speak_to_chat_timeout_off + @string/sony_speak_to_chat_timeout_short + @string/sony_speak_to_chat_timeout_standard + @string/sony_speak_to_chat_timeout_long + + + + off + short + standard + long + + @string/sony_button_mode_off @string/sony_button_mode_ambient_sound_control diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 030166f53..36c1434ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -355,6 +355,7 @@ Connected Unknown state (unknown) + Unknown Test Test notification This is a test notification from Gadgetbridge @@ -379,6 +380,8 @@ Device discovery Stop scanning Start discovery + Bluetooth scan: + Bluetooth LE scan: Connect new device %1$s (%2$s) Pair device @@ -769,6 +772,7 @@ Cancel Delete OK + Start Set Export/Import directory content Show Export/Import directory content @@ -889,6 +893,8 @@ Amazfit Bip S Lite Amazfit Bip U Amazfit Bip U Pro + Amazfit Pop + Amazfit Pop Pro Amazfit GTR 2 Amazfit GTR 2e Amazfit GTS 2 @@ -925,12 +931,13 @@ Bangle.js TLW64 PineTime (JF Firmware) - Sony SWR12 + Sony SWR12 Wasp-os SMA-Q2 OSS FitPro - + Domyos T540 Sony WH-1000XM3 + Sony WH-1000XM4 Sony WF-SP800N Choose export location General @@ -1101,6 +1108,7 @@ Starting the background service failed because… ALREADY BONDED KEY REQUIRED + UNSUPPORTED Starting the background service failed because of an exception. Error: Check permission status Check and ask for missing permissions even when they might not be instantly needed. Disable this only if your devices actually doesn\'t support any of these features. Not granting a permission might cause issues! @@ -1379,7 +1387,9 @@ Battery case Left earbud Right earbud + Audio Codec Ambient Sound Control + Device Information Mode Off Noise Cancelling @@ -1387,6 +1397,12 @@ Ambient Sound Focus on Voice Ambient Sound Level + Noise Cancelling Optimizer + Optimize + Click to start the noise cancelling optimizer. + Noise Cancelling Optimizer + Use the headphones as you normally would. If the wearing condition or atmospheric pressure change, run the optimizer again. + Atmospheric pressure Sound Position Off Front @@ -1402,6 +1418,12 @@ Concert Hall Warning: The equalizer, audio position and surround settings only work for the SBC audio codec. Equalizer + Starting… + Not Running + Measuring wearing condition… + Measuring atmostpheric pressure… + Analyzing… + Finishing… Off Bright Excited @@ -1425,6 +1447,18 @@ Clear Bass Audio Upsampling Touch sensor control + Speak-to-chat + Voice Detection Sensitivity + Automatic + High + Low + Focus on Voice + Timeout + Off + Short (15s) + Standard (30s) + Long (1m) + Connect to 2 devices simultaneously Notifications & Voice Guide Automatic Power Off Do not turn off @@ -1480,4 +1514,5 @@ Menu Some buttons cannot be configured because their functions are hard-coded in the watch firmware.\n\nWarning: long-pressing the upper button when a watchface from the official Fossil app is installed will also toggle between showing/hiding widgets. + Width: diff --git a/app/src/main/res/xml/changelog_master.xml b/app/src/main/res/xml/changelog_master.xml index 06a837aa5..67f7e5429 100644 --- a/app/src/main/res/xml/changelog_master.xml +++ b/app/src/main/res/xml/changelog_master.xml @@ -1,5 +1,19 @@ + + Sony WH-1000XM4: Initial Support + Sony WH-1000XM3: Disable equalizer, surround and sound position while in SBC codec + Improve Sony Headphones initialization on connection + Fixed accidentally disabled time synchronization and pairing of new Casio GBX/GBD-series watches + Fossil HR: improved Device Applications List handling. Added ability to change activity recognition settings on the watch + Fossil HR: Make width of custom widget configurable. Disable non-configurable buttons preferences + Add Support for Bip U series China Variant + Add icon for VESC devices + Add commit id into About screen + Make debug activity notification test to persist text while switching apps + Add Portuguese to the list of language options + Update configuration button icon in app notification settings + Initial support for VESC NRF/HM10 devices Initial support vor Bose QC35 diff --git a/app/src/main/res/xml/devicesettings_automatic_power_off.xml b/app/src/main/res/xml/devicesettings_automatic_power_off_by_time.xml similarity index 100% rename from app/src/main/res/xml/devicesettings_automatic_power_off.xml rename to app/src/main/res/xml/devicesettings_automatic_power_off_by_time.xml diff --git a/app/src/main/res/xml/devicesettings_sony_wf_sp800n.xml b/app/src/main/res/xml/devicesettings_automatic_power_off_when_taken_off.xml similarity index 72% rename from app/src/main/res/xml/devicesettings_sony_wf_sp800n.xml rename to app/src/main/res/xml/devicesettings_automatic_power_off_when_taken_off.xml index 815a3612b..f7f98ec4b 100644 --- a/app/src/main/res/xml/devicesettings_sony_wf_sp800n.xml +++ b/app/src/main/res/xml/devicesettings_automatic_power_off_when_taken_off.xml @@ -2,8 +2,8 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml b/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml new file mode 100644 index 000000000..860e19302 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_anc_optimizer.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml b/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml new file mode 100644 index 000000000..0997efcfb --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_device_info.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_equalizer.xml b/app/src/main/res/xml/devicesettings_sony_headphones_equalizer.xml index abc973b2f..f223aeb96 100644 --- a/app/src/main/res/xml/devicesettings_sony_headphones_equalizer.xml +++ b/app/src/main/res/xml/devicesettings_sony_headphones_equalizer.xml @@ -3,7 +3,7 @@ diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_simple.xml b/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_simple.xml new file mode 100644 index 000000000..539d52c0c --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_simple.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_with_settings.xml b/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_with_settings.xml new file mode 100644 index 000000000..37394ed32 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_sony_headphones_speak_to_chat_with_settings.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/devicesettings_sony_wh_1000xm3.xml b/app/src/main/res/xml/devicesettings_sony_wh_1000xm3.xml deleted file mode 100644 index 7e3ad4a1a..000000000 --- a/app/src/main/res/xml/devicesettings_sony_wh_1000xm3.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - -