When the In-Ear detection state changes on the device (via a 2nd
connected phone) handle the change indicated by the buds and update the
value accordingly.
When the buds change their audio / ANC mode (via buds touch, or a 2nd
connected phone) they send the current state using a different command.
Handle that so the state is updated properly.
Newer buds send 6 bytes for ANC status instead of 3. Since the relevant
information is in the first 3 bytes and identical allow extra trailing
bytes when parsing.
Confirmed on the Buds 2 Plus. Likely also applies to Ear(a) (see #3994),
possibly other models as well.
Some Buds support additional ANC modes "medium" and "adaptive".
Add them to the available modes.
- Buds 2 Plus
- Ear(a) (see #3994)
Likely also applies to other models.
For Garmin ANT devices the extended ANT device number and the filtered serial number result in the same name.
This link between ANT device number and serial number is not always true for non-Garmin devices.
FIT file for testing:
https://github.com/ThomasKuehne/FIT-test-files/blob/main/Activity/2018/Activity_20180324_005_000_Swimming_Fr935_ae9c05519ca8c802f33a8ffd90b15915.fit
``
Error saving specific samplesjava.lang.IllegalArgumentException: Infinity is not a valid double value as per JSON specification. To override this behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.
at com.google.gson.Gson.checkValidFloatingPoint(Gson.java:508)
at com.google.gson.Gson$1.write(Gson.java:470)
at com.google.gson.Gson$1.write(Gson.java:453)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$2.write(ReflectiveTypeAdapterFactory.java:251)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:491)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:100)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:64)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$2.write(ReflectiveTypeAdapterFactory.java:251)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:491)
at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:181)
at com.google.gson.typeadapters.RuntimeTypeAdapterFactory$1.write(RuntimeTypeAdapterFactory.java:309)
at com.google.gson.TypeAdapter$NullSafeTypeAdapter.write(TypeAdapter.java:304)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:225)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:161)
at com.google.gson.Gson.toJson(Gson.java:943)
at com.google.gson.Gson.toJson(Gson.java:898)
at com.google.gson.Gson.toJson(Gson.java:847)
at com.google.gson.Gson.toJson(Gson.java:824)
at nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData.toJson(ActivitySummaryData.java:189)
at nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData.toString(ActivitySummaryData.java:185)
at nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminWorkoutParser.updateSummary(GarminWorkoutParser.java:1080)
at nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitImporter.persistWorkout(FitImporter.java:687)
``
Example pace calculation when speed is exactly 0.0 m/s.
``
Error saving specific samplesjava.lang.IllegalArgumentException: Infinity is not a valid double value as per JSON specification. To override this behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.
at com.google.gson.Gson.checkValidFloatingPoint(Gson.java:508)
at com.google.gson.Gson$1.write(Gson.java:470)
at com.google.gson.Gson$1.write(Gson.java:453)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$2.write(ReflectiveTypeAdapterFactory.java:251)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:491)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:100)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:64)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$2.write(ReflectiveTypeAdapterFactory.java:251)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:491)
at com.google.gson.TypeAdapter.toJsonTree(TypeAdapter.java:181)
at com.google.gson.typeadapters.RuntimeTypeAdapterFactory$1.write(RuntimeTypeAdapterFactory.java:309)
at com.google.gson.TypeAdapter$NullSafeTypeAdapter.write(TypeAdapter.java:304)
at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:73)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:225)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:161)
at com.google.gson.Gson.toJson(Gson.java:943)
at com.google.gson.Gson.toJson(Gson.java:898)
at com.google.gson.Gson.toJson(Gson.java:847)
at com.google.gson.Gson.toJson(Gson.java:824)
at nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData.toJson(ActivitySummaryData.java:195)
at nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryData.toString(ActivitySummaryData.java:191)
at nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminWorkoutParser.updateSummary(GarminWorkoutParser.java:1079)
``
- add fallback sources for HR_AVG, TOTAL_ASCENT, TOTAL_DESCENT, AVG_POWER, BODY_ENERGY_AT_START and BODY_ENERGY_AT_END
- fallback to lap average speed if the lap enhanced average speed isn't available
- ensure that paces/speeds are only rounded by WorkoutValueFormatter and not also by GarminWorkoutParser
- add diving lap summary for apnea
- add scuba tank summary
- fix diving summary gaps when CNS, N2 load or O2 toxicity values are 0
This is a saner default than 12:00, which cuts off sleep sessions that
go past noon. We keep the previous noon-to-noon setting as an option, in
case that is a better option for some users.