From 0cf73a212012b0e576a808cb9ebc866a8e39c452 Mon Sep 17 00:00:00 2001 From: Daniele Gobbetti Date: Thu, 16 May 2024 14:24:32 +0200 Subject: [PATCH] Remove all the legacy vivomove HR code. Also replaces support for device through the new garmin code path. The Entities are preserved to enable on-demand database migration in case some user had previously synchronized data. --- .../vivomove/GarminVivomoveHrCoordinator.java | 18 + .../devices/vivomovehr/VivomoveConstants.java | 98 -- .../vivomovehr/VivomoveHrCoordinator.java | 158 --- .../gadgetbridge/model/DeviceType.java | 4 +- .../communicator/v1/CommunicatorV1.java | 3 +- .../communicator/v2/CommunicatorV2.java | 8 +- .../vivomovehr/ChecksumCalculator.java | 38 - .../vivomovehr/GarminDeviceSetting.java | 29 - .../devices/vivomovehr/GarminMessageType.java | 44 - .../vivomovehr/GarminMusicControlCommand.java | 29 - .../vivomovehr/GarminSystemEventType.java | 37 - .../devices/vivomovehr/GarminTimeUtils.java | 40 - .../devices/vivomovehr/GfdiPacketParser.java | 191 --- .../vivomovehr/RealTimeActivityHandler.java | 179 --- .../vivomovehr/VivomoveHrCommunicator.java | 107 -- .../devices/vivomovehr/VivomoveHrSupport.java | 1074 ----------------- .../devices/vivomovehr/ams/AmsEntity.java | 23 - .../vivomovehr/ams/AmsEntityAttribute.java | 58 - .../devices/vivomovehr/ancs/AncsAction.java | 22 - .../vivomovehr/ancs/AncsAndroidAction.java | 48 - .../vivomovehr/ancs/AncsAppAttribute.java | 21 - .../vivomovehr/ancs/AncsAttribute.java | 65 - .../vivomovehr/ancs/AncsAttributeRequest.java | 27 - .../devices/vivomovehr/ancs/AncsCategory.java | 33 - .../devices/vivomovehr/ancs/AncsCommand.java | 47 - .../vivomovehr/ancs/AncsControlCommand.java | 137 --- .../devices/vivomovehr/ancs/AncsEvent.java | 23 - .../vivomovehr/ancs/AncsEventFlag.java | 25 - .../ancs/AncsGetAppAttributesCommand.java | 30 - .../AncsGetNotificationAttributeCommand.java | 30 - ...AncsGetNotificationAttributesResponse.java | 39 - .../ancs/AncsPerformAndroidAction.java | 30 - .../ancs/AncsPerformNotificationAction.java | 28 - .../vivomovehr/ancs/GncsDataSourceQueue.java | 113 -- .../vivomovehr/downloads/DirectoryData.java | 54 - .../vivomovehr/downloads/DirectoryEntry.java | 41 - .../downloads/FileDownloadListener.java | 25 - .../downloads/FileDownloadQueue.java | 188 --- .../devices/vivomovehr/fit/FitBool.java | 23 - .../devices/vivomovehr/fit/FitDbImporter.java | 70 -- .../vivomovehr/fit/FitFieldBaseType.java | 69 -- .../vivomovehr/fit/FitImportProcessor.java | 23 - .../devices/vivomovehr/fit/FitImporter.java | 288 ----- .../fit/FitLocalFieldDefinition.java | 29 - .../fit/FitLocalMessageDefinition.java | 29 - .../devices/vivomovehr/fit/FitMessage.java | 181 --- .../vivomovehr/fit/FitMessageDefinition.java | 71 -- .../vivomovehr/fit/FitMessageDefinitions.java | 662 ---------- .../fit/FitMessageFieldDefinition.java | 51 - .../devices/vivomovehr/fit/FitParser.java | 301 ----- .../devices/vivomovehr/fit/FitSerializer.java | 274 ----- .../vivomovehr/fit/FitWeatherConditions.java | 207 ---- .../messages/AuthNegotiationMessage.java | 45 - .../AuthNegotiationResponseMessage.java | 42 - .../messages/BatteryStatusMessage.java | 39 - .../messages/ConfigurationMessage.java | 50 - .../messages/CreateFileRequestMessage.java | 44 - .../messages/CreateFileResponseMessage.java | 54 - .../messages/CurrentTimeRequestMessage.java | 32 - .../CurrentTimeRequestResponseMessage.java | 44 - .../messages/DeviceInformationMessage.java | 62 - .../DeviceInformationResponseMessage.java | 47 - .../DirectoryFileFilterRequestMessage.java | 42 - .../DirectoryFileFilterResponseMessage.java | 39 - .../messages/DownloadRequestMessage.java | 44 - .../DownloadRequestResponseMessage.java | 47 - .../vivomovehr/messages/FileReadyMessage.java | 60 - .../messages/FileTransferDataMessage.java | 62 - .../FileTransferDataResponseMessage.java | 64 - .../messages/FindMyPhoneRequestMessage.java | 33 - .../vivomovehr/messages/FitDataMessage.java | 40 - .../messages/FitDataResponseMessage.java | 43 - .../messages/FitDefinitionMessage.java | 40 - .../FitDefinitionResponseMessage.java | 43 - .../messages/GenericResponseMessage.java | 38 - .../messages/GncsControlPointMessage.java | 33 - .../GncsControlPointResponseMessage.java | 50 - .../messages/GncsDataSourceMessage.java | 41 - .../GncsDataSourceResponseMessage.java | 42 - .../GncsNotificationSourceMessage.java | 51 - .../vivomovehr/messages/MessageReader.java | 100 -- .../vivomovehr/messages/MessageWriter.java | 93 -- .../MusicControlCapabilitiesMessage.java | 32 - ...sicControlCapabilitiesResponseMessage.java | 44 - .../MusicControlEntityUpdateMessage.java | 40 - .../devices/vivomovehr/messages/NOTES.txt | 144 --- ...otificationServiceSubscriptionMessage.java | 48 - ...ionServiceSubscriptionResponseMessage.java | 41 - .../messages/ProtobufRequestMessage.java | 62 - .../ProtobufRequestResponseMessage.java | 53 - .../vivomovehr/messages/RequestMessage.java | 37 - .../vivomovehr/messages/ResponseMessage.java | 58 - .../messages/SetDeviceSettingsMessage.java | 60 - .../SetDeviceSettingsResponseMessage.java | 36 - .../SupportedFileTypesRequestMessage.java | 36 - .../SupportedFileTypesResponseMessage.java | 64 - .../messages/SyncRequestMessage.java | 61 - .../messages/SystemEventMessage.java | 45 - .../messages/SystemEventResponseMessage.java | 36 - .../messages/UploadRequestMessage.java | 40 - .../UploadRequestResponseMessage.java | 52 - .../messages/WeatherRequestMessage.java | 41 - .../WeatherRequestResponseMessage.java | 41 - .../notifications/NotificationData.java | 166 --- .../notifications/NotificationStorage.java | 117 -- .../vivomovehr/uploads/FileUploadQueue.java | 215 ---- 106 files changed, 25 insertions(+), 8450 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/vivomove/GarminVivomoveHrCoordinator.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveConstants.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveHrCoordinator.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ChecksumCalculator.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminDeviceSetting.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMessageType.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMusicControlCommand.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminSystemEventType.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminTimeUtils.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GfdiPacketParser.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/RealTimeActivityHandler.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrCommunicator.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrSupport.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntity.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntityAttribute.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAction.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAndroidAction.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAppAttribute.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttribute.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttributeRequest.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCategory.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCommand.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsControlCommand.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEvent.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEventFlag.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetAppAttributesCommand.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributeCommand.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributesResponse.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformAndroidAction.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformNotificationAction.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/GncsDataSourceQueue.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryData.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryEntry.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadListener.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadQueue.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitBool.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitDbImporter.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitFieldBaseType.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImportProcessor.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImporter.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalFieldDefinition.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalMessageDefinition.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinition.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinitions.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageFieldDefinition.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitParser.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitSerializer.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitWeatherConditions.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/BatteryStatusMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ConfigurationMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileReadyMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FindMyPhoneRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GenericResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsNotificationSourceMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageReader.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageWriter.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlEntityUpdateMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NOTES.txt delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/RequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SyncRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestResponseMessage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationData.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationStorage.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/uploads/FileUploadQueue.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/vivomove/GarminVivomoveHrCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/vivomove/GarminVivomoveHrCoordinator.java new file mode 100644 index 000000000..9f471a13e --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/vivomove/GarminVivomoveHrCoordinator.java @@ -0,0 +1,18 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivomove; + +import java.util.regex.Pattern; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminCoordinator; + +public class GarminVivomoveHrCoordinator extends GarminCoordinator { + @Override + protected Pattern getSupportedDeviceName() { + return Pattern.compile("vívomove HR"); + } + + @Override + public int getDeviceNameResource() { + return R.string.devicetype_garmin_vivomove_hr; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveConstants.java deleted file mode 100644 index 19f945d3b..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveConstants.java +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import java.text.SimpleDateFormat; -import java.util.Locale; -import java.util.Set; -import java.util.UUID; - -public class VivomoveConstants { - public static final UUID UUID_SERVICE_GARMIN_GFDI = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66"); - public static final UUID UUID_SERVICE_GARMIN_REALTIME = UUID.fromString("6A4E2500-667B-11E3-949A-0800200C9A66"); - - public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_SEND = UUID.fromString("6a4e4c80-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE = UUID.fromString("6a4ecd28-667b-11e3-949a-0800200c9a66"); - - public static final UUID UUID_CHARACTERISTIC_GARMIN_HEART_RATE = UUID.fromString("6a4e2501-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_STEPS = UUID.fromString("6a4e2502-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_CALORIES = UUID.fromString("6a4e2503-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_STAIRS = UUID.fromString("6a4e2504-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_INTENSITY = UUID.fromString("6a4e2505-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_HEART_RATE_VARIATION = UUID.fromString("6a4e2507-667b-11e3-949a-0800200c9a66"); - // public static final UUID UUID_CHARACTERISTIC_GARMIN_STRESS = UUID.fromString("6a4e2508-667b-11e3-949a-0800200c9a66"); - public static final UUID UUID_CHARACTERISTIC_GARMIN_2_9 = UUID.fromString("6a4e2509-667b-11e3-949a-0800200c9a66"); - // public static final UUID UUID_CHARACTERISTIC_GARMIN_SPO2 = UUID.fromString("6a4e250c-667b-11e3-949a-0800200c9a66"); - // public static final UUID UUID_CHARACTERISTIC_GARMIN_RESPIRATION = UUID.fromString("6a4e250e-667b-11e3-949a-0800200c9a66"); - - // public static final UUID UUID_CLIENT_CHARACTERISTIC_CONFIGURATION = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); - - public static final int STATUS_ACK = 0; - public static final int STATUS_NAK = 1; - public static final int STATUS_UNSUPPORTED = 2; - public static final int STATUS_DECODE_ERROR = 3; - public static final int STATUS_CRC_ERROR = 4; - public static final int STATUS_LENGTH_ERROR = 5; - - public static final int GADGETBRIDGE_UNIT_NUMBER = 22222; - - public static final int GARMIN_DEVICE_XML_FILE_INDEX = 65533; - - // TODO: Better capability management/configuration - // public static final Set OUR_CAPABILITIES = new HashSet<>(Arrays.asList(GarminCapability.SYNC, GarminCapability.GNCS, GarminCapability.ADVANCED_MUSIC_CONTROLS, GarminCapability.FIND_MY_PHONE, GarminCapability.WEATHER_CONDITIONS, GarminCapability.WEATHER_ALERTS, GarminCapability.DEVICE_MESSAGES, GarminCapability.SMS_NOTIFICATIONS, GarminCapability.SYNC, GarminCapability.DEVICE_INITIATES_SYNC, GarminCapability.HOST_INITIATED_SYNC_REQUESTS, GarminCapability.CALENDAR, GarminCapability.CURRENT_TIME_REQUEST_SUPPORT)); - public static final Set OUR_CAPABILITIES = GarminCapability.ALL_CAPABILITIES; - - public static final int MAX_WRITE_SIZE = 20; - - /** - * Garmin zero time in seconds since Epoch: 1989-12-31T00:00:00Z - */ - public static final int GARMIN_TIME_EPOCH = 631065600; - - public static final SimpleDateFormat ANCS_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ROOT); - - public static final int MESSAGE_RESPONSE = 5000; - public static final int MESSAGE_REQUEST = 5001; - public static final int MESSAGE_DOWNLOAD_REQUEST = 5002; - public static final int MESSAGE_UPLOAD_REQUEST = 5003; - public static final int MESSAGE_FILE_TRANSFER_DATA = 5004; - public static final int MESSAGE_CREATE_FILE_REQUEST = 5005; - public static final int MESSAGE_DIRECTORY_FILE_FILTER_REQUEST = 5007; - public static final int MESSAGE_FILE_READY = 5009; - public static final int MESSAGE_FIT_DEFINITION = 5011; - public static final int MESSAGE_FIT_DATA = 5012; - public static final int MESSAGE_WEATHER_REQUEST = 5014; - public static final int MESSAGE_BATTERY_STATUS = 5023; - public static final int MESSAGE_DEVICE_INFORMATION = 5024; - public static final int MESSAGE_DEVICE_SETTINGS = 5026; - public static final int MESSAGE_SYSTEM_EVENT = 5030; - public static final int MESSAGE_SUPPORTED_FILE_TYPES_REQUEST = 5031; - public static final int MESSAGE_NOTIFICATION_SOURCE = 5033; - public static final int MESSAGE_GNCS_CONTROL_POINT_REQUEST = 5034; - public static final int MESSAGE_GNCS_DATA_SOURCE = 5035; - public static final int MESSAGE_NOTIFICATION_SERVICE_SUBSCRIPTION = 5036; - public static final int MESSAGE_SYNC_REQUEST = 5037; - public static final int MESSAGE_FIND_MY_PHONE = 5039; - public static final int MESSAGE_CANCEL_FIND_MY_PHONE = 5040; - public static final int MESSAGE_MUSIC_CONTROL_CAPABILITIES = 5042; - public static final int MESSAGE_PROTOBUF_REQUEST = 5043; - public static final int MESSAGE_PROTOBUF_RESPONSE = 5044; - public static final int MESSAGE_MUSIC_CONTROL_ENTITY_UPDATE = 5049; - public static final int MESSAGE_CONFIGURATION = 5050; - public static final int MESSAGE_CURRENT_TIME_REQUEST = 5052; - public static final int MESSAGE_AUTH_NEGOTIATION = 5101; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveHrCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveHrCoordinator.java deleted file mode 100644 index 121dc6bf7..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/vivomovehr/VivomoveHrCoordinator.java +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright (C) 2023-2024 Daniel Dakhno, Petr Kadlec - - 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.vivomovehr; - -import android.app.Activity; -import android.bluetooth.le.ScanFilter; -import android.content.Context; -import android.net.Uri; -import android.os.ParcelUuid; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import nodomain.freeyourgadget.gadgetbridge.GBException; -import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; -import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; -import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.VivomoveHrSupport; - -import java.util.Collection; -import java.util.Collections; - -public class VivomoveHrCoordinator extends AbstractBLEDeviceCoordinator { - @NonNull - @Override - public boolean supports(GBDeviceCandidate candidate) { - if ("vívomove HR".equals(candidate.getName())) return true; - - final boolean hasServiceUuids = candidate.getServiceUuids().length > 0; - return hasServiceUuids && candidate.supportsService(VivomoveConstants.UUID_SERVICE_GARMIN_GFDI); - } - - @NonNull - @Override - public Collection createBLEScanFilters() { - final ParcelUuid garminService = new ParcelUuid(VivomoveConstants.UUID_SERVICE_GARMIN_GFDI); - final ScanFilter filter = new ScanFilter.Builder().setServiceUuid(garminService).build(); - return Collections.singletonList(filter); - } - - @Nullable - @Override - public Class getPairingActivity() { - return null; - } - - @Override - public boolean supportsActivityDataFetching() { - return true; - } - - @Override - public boolean supportsActivityTracking() { - return true; - } - - @Override - public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { - return new VivomoveHrSampleProvider(device, session); - } - - @Override - public InstallHandler findInstallHandler(Uri uri, Context context) { - return null; - } - - @Override - public boolean supportsScreenshots(final GBDevice device) { - return false; - } - - @Override - public int getAlarmSlotCount(GBDevice device) { - return 0; - } - - @Override - public boolean supportsHeartRateMeasurement(GBDevice device) { - return true; - } - - @Override - public String getManufacturer() { - return "Garmin"; - } - - @Override - public boolean supportsAppsManagement(GBDevice device) { - return false; - } - - @Override - public Class getAppsManagementActivity() { - return null; - } - - @Override - public boolean supportsCalendarEvents() { - return false; - } - - @Override - public boolean supportsRealtimeData() { - return true; - } - - @Override - public boolean supportsWeather() { - return true; - } - - @Override - public int[] getSupportedDeviceSpecificSettings(GBDevice device) { - // no device-specific settings yet - return null; - } - - @NonNull - @Override - public Class getDeviceSupportClass() { - return VivomoveHrSupport.class; - } - - @Override - public int getDeviceNameResource() { - return R.string.devicetype_garmin_vivomove_hr; - } - - @Override - public boolean supportsFindDevice() { - return true; - } - - @Override - protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { - // nothing to delete, yet - } -} 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 f646471bd..4d089e4e2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -61,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.garmin.venu3.GarminVenu3Coor import nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivoactive4.GarminVivoActive4Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivoactive4s.GarminVivoActive4SCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivoactive5.GarminVivoActive5Coordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivomove.GarminVivomoveHrCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.vivomove.GarminVivomoveStyleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator; @@ -192,7 +193,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vesc.VescCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveHrCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.withingssteelhr.WithingsSteelHRDeviceCoordinator; @@ -340,7 +340,7 @@ public enum DeviceType { COLACAO23(ColaCao23Coordinator.class), ITAG(ITagCoordinator.class), NUTMINI(NutCoordinator.class), - VIVOMOVE_HR(VivomoveHrCoordinator.class), + VIVOMOVE_HR(GarminVivomoveHrCoordinator.class), GARMIN_FORERUNNER_245(GarminForerunner245Coordinator.class), GARMIN_SWIM_2(GarminSwim2Coordinator.class), GARMIN_INSTINCT_SOLAR(GarminInstinctSolarCoordinator.class), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java index cfce06ad8..ac2e140c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v1/CommunicatorV1.java @@ -5,13 +5,12 @@ import android.bluetooth.BluetoothGattCharacteristic; import java.util.UUID; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.ICommunicator; public class CommunicatorV1 implements ICommunicator { - public static final UUID UUID_SERVICE_GARMIN_GFDI = VivomoveConstants.UUID_SERVICE_GARMIN_GFDI; + public static final UUID UUID_SERVICE_GARMIN_GFDI = UUID.fromString("6A4E2401-667B-11E3-949A-0800200C9A66"); private final GarminSupport mSupport; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java index 2c770bcd6..aa657a43c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/communicator/v2/CommunicatorV2.java @@ -19,11 +19,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator. import nodomain.freeyourgadget.gadgetbridge.util.GB; public class CommunicatorV2 implements ICommunicator { - public static final UUID UUID_SERVICE_GARMIN_ML_GFDI = UUID.fromString("6A4E2800-667B-11E3-949A-0800200C9A66"); //VivomoveConstants.UUID_SERVICE_GARMIN_ML_GFDI; - public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND = UUID.fromString("6a4e2822-667b-11e3-949a-0800200c9a66"); //VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND; - public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE = UUID.fromString("6a4e2812-667b-11e3-949a-0800200c9a66"); //VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE; + public static final UUID UUID_SERVICE_GARMIN_ML_GFDI = UUID.fromString("6A4E2800-667B-11E3-949A-0800200C9A66"); + public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_SEND = UUID.fromString("6a4e2822-667b-11e3-949a-0800200c9a66"); + public static final UUID UUID_CHARACTERISTIC_GARMIN_ML_GFDI_RECEIVE = UUID.fromString("6a4e2812-667b-11e3-949a-0800200c9a66"); - public int maxWriteSize = 20; //VivomoveConstants.MAX_WRITE_SIZE + public int maxWriteSize = 20; private static final Logger LOG = LoggerFactory.getLogger(CommunicatorV2.class); public final CobsCoDec cobsCoDec; private final GarminSupport mSupport; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ChecksumCalculator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ChecksumCalculator.java deleted file mode 100644 index 1b3da0e02..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ChecksumCalculator.java +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -public final class ChecksumCalculator { - private static final int[] CONSTANTS = {0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400}; - - private ChecksumCalculator() { - } - - public static int computeCrc(byte[] data, int offset, int length) { - return computeCrc(0, data, offset, length); - } - - public static int computeCrc(int initialCrc, byte[] data, int offset, int length) { - int crc = initialCrc; - for (int i = offset; i < offset + length; ++i) { - int b = data[i]; - crc = (((crc >> 4) & 4095) ^ CONSTANTS[crc & 15]) ^ CONSTANTS[b & 15]; - crc = (((crc >> 4) & 4095) ^ CONSTANTS[crc & 15]) ^ CONSTANTS[(b >> 4) & 15]; - } - return crc; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminDeviceSetting.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminDeviceSetting.java deleted file mode 100644 index cfd2f82a6..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminDeviceSetting.java +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -public enum GarminDeviceSetting { - DEVICE_NAME, - CURRENT_TIME, - DAYLIGHT_SAVINGS_TIME_OFFSET, - TIME_ZONE_OFFSET, - NEXT_DAYLIGHT_SAVINGS_START, - NEXT_DAYLIGHT_SAVINGS_END, - AUTO_UPLOAD_ENABLED, - WEATHER_CONDITIONS_ENABLED, - WEATHER_ALERTS_ENABLED; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMessageType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMessageType.java deleted file mode 100644 index 153c6bed2..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMessageType.java +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -public enum GarminMessageType { - SCHEDULES, - SETTINGS, - GOALS, - WORKOUTS, - COURSES, - ACTIVITIES, - PERSONAL_RECORDS, - UNKNOWN_TYPE, - SOFTWARE_UPDATE, - DEVICE_SETTINGS, - LANGUAGE_SETTINGS, - USER_PROFILE, - SPORTS, - SEGMENT_LEADERS, - GOLF_CLUB, - WELLNESS_DEVICE_INFO, - WELLNESS_DEVICE_CCF, - INSTALL_APP, - CHECK_BACK, - TRUE_UP, - SETTINGS_CHANGE, - ACTIVITY_SUMMARY, - METRICS_FILE, - PACE_BAND -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMusicControlCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMusicControlCommand.java deleted file mode 100644 index 5e0b6e003..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminMusicControlCommand.java +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -public enum GarminMusicControlCommand { - TOGGLE_PLAY_PAUSE, - SKIP_TO_NEXT_ITEM, - SKIP_TO_PREVIOUS_ITEM, - VOLUME_UP, - VOLUME_DOWN, - PLAY, - PAUSE, - SKIP_FORWARD, - SKIP_BACKWARDS; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminSystemEventType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminSystemEventType.java deleted file mode 100644 index edc341847..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminSystemEventType.java +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -public enum GarminSystemEventType { - SYNC_COMPLETE, - SYNC_FAIL, - FACTORY_RESET, - PAIR_START, - PAIR_COMPLETE, - PAIR_FAIL, - HOST_DID_ENTER_FOREGROUND, - HOST_DID_ENTER_BACKGROUND, - SYNC_READY, - NEW_DOWNLOAD_AVAILABLE, - DEVICE_SOFTWARE_UPDATE, - DEVICE_DISCONNECT, - TUTORIAL_COMPLETE, - SETUP_WIZARD_START, - SETUP_WIZARD_COMPLETE, - SETUP_WIZARD_SKIPPED, - TIME_UPDATED; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminTimeUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminTimeUtils.java deleted file mode 100644 index 92fde6da0..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GarminTimeUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; - -public final class GarminTimeUtils { - private GarminTimeUtils() { - } - - public static int unixTimeToGarminTimestamp(int unixTime) { - return unixTime - VivomoveConstants.GARMIN_TIME_EPOCH; - } - - public static int javaMillisToGarminTimestamp(long millis) { - return (int) (millis / 1000) - VivomoveConstants.GARMIN_TIME_EPOCH; - } - - public static long garminTimestampToJavaMillis(int timestamp) { - return (timestamp + VivomoveConstants.GARMIN_TIME_EPOCH) * 1000L; - } - - public static int garminTimestampToUnixTime(int timestamp) { - return timestamp + VivomoveConstants.GARMIN_TIME_EPOCH; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GfdiPacketParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GfdiPacketParser.java deleted file mode 100644 index 77833d597..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/GfdiPacketParser.java +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Arrays; - -/** - * Parser of GFDI messages embedded in COBS packets. - *

- * COBS ensures there are no embedded NUL bytes inside the packet data, and wraps the message into NUL framing bytes. - */ -// Notes: not really optimized; does a lot of (re)allocation, might use more static buffers, I guess… And code cleanup as well. -public class GfdiPacketParser { - private static final Logger LOG = LoggerFactory.getLogger(GfdiPacketParser.class); - - private static final long BUFFER_TIMEOUT = 1500L; - private static final byte[] EMPTY_BUFFER = new byte[0]; - private static final byte[] BUFFER_FRAMING = new byte[1]; - - private byte[] buffer = EMPTY_BUFFER; - private byte[] packet; - private byte[] packetBuffer; - private int bufferPos; - private long lastUpdate; - private boolean insidePacket; - - public void reset() { - buffer = EMPTY_BUFFER; - bufferPos = 0; - insidePacket = false; - packet = null; - packetBuffer = EMPTY_BUFFER; - } - - public void receivedBytes(byte[] bytes) { - final long now = System.currentTimeMillis(); - if ((now - lastUpdate) > BUFFER_TIMEOUT) { - reset(); - } - lastUpdate = now; - final int bufferSize = buffer.length; - buffer = Arrays.copyOf(buffer, bufferSize + bytes.length); - System.arraycopy(bytes, 0, buffer, bufferSize, bytes.length); - parseBuffer(); - } - - public byte[] retrievePacket() { - final byte[] resultPacket = packet; - packet = null; - parseBuffer(); - return resultPacket; - } - - private void parseBuffer() { - if (packet != null) { - // packet is waiting, unable to parse more - return; - } - if (bufferPos >= buffer.length) { - // nothing to parse - return; - } - boolean startOfPacket = !insidePacket; - if (startOfPacket) { - byte b; - while (bufferPos < buffer.length && (b = buffer[bufferPos++]) != 0) { - if (LOG.isDebugEnabled()) { - LOG.debug("Unexpected non-zero byte while looking for framing: {}", Integer.toHexString(b)); - } - } - if (bufferPos >= buffer.length) { - // nothing to parse - return; - } - insidePacket = true; - } - boolean endedWithFullChunk = false; - while (bufferPos < buffer.length) { - int chunkSize = -1; - int chunkStart = bufferPos; - int pos = bufferPos; - while (pos < buffer.length && ((chunkSize = (buffer[pos++] & 0xFF)) == 0) && startOfPacket) { - // skip repeating framing bytes (?) - bufferPos = pos; - chunkStart = pos; - } - if (startOfPacket && pos >= buffer.length) { - // incomplete framing, needs to wait for more data and try again - buffer = BUFFER_FRAMING; - bufferPos = 0; - insidePacket = false; - return; - } - assert chunkSize >= 0; - if (chunkSize == 0) { - // end of packet - // drop the last zero - if (endedWithFullChunk) { - // except when it was explicitly added (TODO: ugly, is it correct?) - packet = packetBuffer; - } else { - packet = Arrays.copyOf(packetBuffer, packetBuffer.length - 1); - } - packetBuffer = EMPTY_BUFFER; - insidePacket = false; - - if (bufferPos == buffer.length - 1) { - buffer = EMPTY_BUFFER; - bufferPos = 0; - } else { - // TODO: Realloc buffer down - ++bufferPos; - } - return; - } - if (chunkStart + chunkSize > buffer.length) { - // incomplete chunk, needs to wait for more data - return; - } - - // completed chunk - final int packetPos = packetBuffer.length; - final int realChunkSize = chunkSize < 255 ? chunkSize : chunkSize - 1; - packetBuffer = Arrays.copyOf(packetBuffer, packetPos + realChunkSize); - System.arraycopy(buffer, chunkStart + 1, packetBuffer, packetPos, chunkSize - 1); - bufferPos = chunkStart + chunkSize; - - endedWithFullChunk = chunkSize == 255; - startOfPacket = false; - } - } - - public static byte[] wrapMessageToPacket(byte[] message) { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(message.length + 2 + (message.length + 253) / 254)) { - outputStream.write(0); - int chunkStart = 0; - for (int i = 0; i < message.length; ++i) { - if (message[i] == 0) { - chunkStart = appendChunk(message, outputStream, chunkStart, i); - } - } - if (chunkStart <= message.length) { - appendChunk(message, outputStream, chunkStart, message.length); - } - outputStream.write(0); - return outputStream.toByteArray(); - } catch (IOException e) { - LOG.error("Error writing to memory buffer", e); - throw new RuntimeException(e); - } - } - - private static int appendChunk(byte[] message, ByteArrayOutputStream outputStream, int chunkStart, int messagePos) { - int chunkLength = messagePos - chunkStart; - while (true) { - if (chunkLength >= 255) { - // write 255-byte chunk - outputStream.write(255); - outputStream.write(message, chunkStart, 254); - chunkLength -= 254; - chunkStart += 254; - } else { - // write chunk from chunkStart to here - outputStream.write(chunkLength + 1); - outputStream.write(message, chunkStart, chunkLength); - chunkStart = messagePos + 1; - break; - } - } - return chunkStart; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/RealTimeActivityHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/RealTimeActivityHandler.java deleted file mode 100644 index 0b9a0a890..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/RealTimeActivityHandler.java +++ /dev/null @@ -1,179 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import android.content.Intent; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveHrSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.entities.VivomoveHrActivitySample; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.UUID; - -/* default */ class RealTimeActivityHandler { - private static final Logger LOG = LoggerFactory.getLogger(RealTimeActivityHandler.class); - - private final VivomoveHrSupport owner; - private final VivomoveHrActivitySample lastSample = new VivomoveHrActivitySample(); - - /* default */ RealTimeActivityHandler(VivomoveHrSupport owner) { - this.owner = owner; - } - - public boolean tryHandleChangedCharacteristic(UUID characteristicUUID, byte[] data) { - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_HEART_RATE.equals(characteristicUUID)) { - processRealtimeHeartRate(data); - return true; - } - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_STEPS.equals(characteristicUUID)) { - processRealtimeSteps(data); - return true; - } - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_CALORIES.equals(characteristicUUID)) { - processRealtimeCalories(data); - return true; - } - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_STAIRS.equals(characteristicUUID)) { - processRealtimeStairs(data); - return true; - } - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_INTENSITY.equals(characteristicUUID)) { - processRealtimeIntensityMinutes(data); - return true; - } - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_HEART_RATE_VARIATION.equals(characteristicUUID)) { - handleRealtimeHeartbeat(data); - return true; - } - - return false; - } - - private void processRealtimeHeartRate(byte[] data) { - int unknown1 = BLETypeConversions.toUnsigned(data, 0); - int heartRate = BLETypeConversions.toUnsigned(data, 1); - int unknown2 = BLETypeConversions.toUnsigned(data, 2); - int unknown3 = BLETypeConversions.toUint16(data, 3); - - lastSample.setHeartRate(heartRate); - processSample(); - - LOG.debug("Realtime HR {} ({}, {}, {})", heartRate, unknown1, unknown2, unknown3); - } - - private void processRealtimeSteps(byte[] data) { - int steps = BLETypeConversions.toUint32(data, 0); - int goal = BLETypeConversions.toUint32(data, 4); - - lastSample.setSteps(steps); - processSample(); - - LOG.debug("Realtime steps: {} steps (goal: {})", steps, goal); - } - - private void processRealtimeCalories(byte[] data) { - int calories = BLETypeConversions.toUint32(data, 0); - int unknown = BLETypeConversions.toUint32(data, 4); - - lastSample.setCaloriesBurnt(calories); - processSample(); - - LOG.debug("Realtime calories: {} cal burned (unknown: {})", calories, unknown); - } - - private void processRealtimeStairs(byte[] data) { - int floorsClimbed = BLETypeConversions.toUint16(data, 0); - int unknown = BLETypeConversions.toUint16(data, 2); - int floorGoal = BLETypeConversions.toUint16(data, 4); - - lastSample.setFloorsClimbed(floorsClimbed); - processSample(); - - LOG.debug("Realtime stairs: {} floors climbed (goal: {}, unknown: {})", floorsClimbed, floorGoal, unknown); - } - - private void processRealtimeIntensityMinutes(byte[] data) { - int weeklyLimit = BLETypeConversions.toUint32(data, 10); - - LOG.debug("Realtime intensity recorded; weekly limit: {}", weeklyLimit); - } - - private void handleRealtimeHeartbeat(byte[] data) { - int interval = BLETypeConversions.toUint16(data, 0); - int timer = BLETypeConversions.toUint32(data, 2); - - float heartRate = (60.0f * 1024.0f) / interval; - LOG.debug("Realtime heartbeat frequency {} at {}", heartRate, timer); - } - - private void processSample() { - if (lastSample.getCaloriesBurnt() == null || lastSample.getFloorsClimbed() == null || lastSample.getHeartRate() == 0 || lastSample.getSteps() == 0) { - LOG.debug("Skipping incomplete sample"); - return; - } - - try (final DBHandler dbHandler = GBApplication.acquireDB()) { - final DaoSession session = dbHandler.getDaoSession(); - - final GBDevice gbDevice = owner.getDevice(); - final Device device = DBHelper.getDevice(gbDevice, session); - final User user = DBHelper.getUser(session); - final int ts = (int) (System.currentTimeMillis() / 1000); - final VivomoveHrSampleProvider provider = new VivomoveHrSampleProvider(gbDevice, session); - final VivomoveHrActivitySample sample = createActivitySample(device, user, ts, provider); - - sample.setCaloriesBurnt(lastSample.getCaloriesBurnt()); - sample.setFloorsClimbed(lastSample.getFloorsClimbed()); - sample.setHeartRate(lastSample.getHeartRate()); - sample.setSteps(lastSample.getSteps()); - sample.setRawIntensity(ActivitySample.NOT_MEASURED); - sample.setRawKind(ActivityKind.TYPE_ACTIVITY); // to make it visible in the charts TODO: add a MANUAL kind for that? - - LOG.debug("Publishing sample"); - provider.addGBActivitySample(sample); - } catch (Exception e) { - LOG.error("Error saving real-time activity data", e); - } - - final Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES) - .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, lastSample); - LocalBroadcastManager.getInstance(owner.getContext()).sendBroadcast(intent); - } - - public VivomoveHrActivitySample createActivitySample(Device device, User user, int timestampInSeconds, VivomoveHrSampleProvider provider) { - final VivomoveHrActivitySample sample = new VivomoveHrActivitySample(); - sample.setDevice(device); - sample.setUser(user); - sample.setTimestamp(timestampInSeconds); - sample.setProvider(provider); - - return sample; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrCommunicator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrCommunicator.java deleted file mode 100644 index d20b56a7c..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrCommunicator.java +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import android.bluetooth.BluetoothGattCharacteristic; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; -import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Arrays; - -public class VivomoveHrCommunicator { - private static final Logger LOG = LoggerFactory.getLogger(VivomoveHrCommunicator.class); - - private final AbstractBTLEDeviceSupport deviceSupport; - - private BluetoothGattCharacteristic characteristicMessageSender; - private BluetoothGattCharacteristic characteristicMessageReceiver; - private BluetoothGattCharacteristic characteristicHeartRate; - private BluetoothGattCharacteristic characteristicSteps; - private BluetoothGattCharacteristic characteristicCalories; - private BluetoothGattCharacteristic characteristicStairs; - private BluetoothGattCharacteristic characteristicHrVariation; - private BluetoothGattCharacteristic char2_9; - - public VivomoveHrCommunicator(AbstractBTLEDeviceSupport deviceSupport) { - this.deviceSupport = deviceSupport; - - this.characteristicMessageSender = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_GFDI_SEND); - this.characteristicMessageReceiver = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE); - this.characteristicHeartRate = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_HEART_RATE); - this.characteristicSteps = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_STEPS); - this.characteristicCalories = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_CALORIES); - this.characteristicStairs = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_STAIRS); - this.characteristicHrVariation = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_HEART_RATE_VARIATION); - this.char2_9 = deviceSupport.getCharacteristic(VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_2_9); - } - - public void start(TransactionBuilder builder) { - builder.notify(characteristicMessageReceiver, true); -// builder.notify(characteristicHeartRate, true); -// builder.notify(characteristicSteps, true); -// builder.notify(characteristicCalories, true); -// builder.notify(characteristicStairs, true); - //builder.notify(char2_7, true); - // builder.notify(char2_9, true); - } - - public void sendMessage(byte[] messageBytes) { - try { - final TransactionBuilder builder = deviceSupport.performInitialized("sendMessage()"); - sendMessage(builder, messageBytes); - builder.queue(deviceSupport.getQueue()); - } catch (IOException e) { - LOG.error("Unable to send a message", e); - } - } - - private void sendMessage(TransactionBuilder builder, byte[] messageBytes) { - final byte[] packet = GfdiPacketParser.wrapMessageToPacket(messageBytes); - int remainingBytes = packet.length; - if (remainingBytes > VivomoveConstants.MAX_WRITE_SIZE) { - int position = 0; - while (remainingBytes > 0) { - final byte[] fragment = Arrays.copyOfRange(packet, position, position + Math.min(remainingBytes, VivomoveConstants.MAX_WRITE_SIZE)); - builder.write(characteristicMessageSender, fragment); - position += fragment.length; - remainingBytes -= fragment.length; - } - } else { - builder.write(characteristicMessageSender, packet); - } - } - - public void enableRealtimeSteps(boolean enable) { - try { - deviceSupport.performInitialized((enable ? "Enable" : "Disable") + " realtime steps").notify(characteristicSteps, enable).queue(deviceSupport.getQueue()); - } catch (IOException e) { - LOG.error("Unable to change realtime steps notification to: " + enable, e); - } - } - - public void enableRealtimeHeartRate(boolean enable) { - try { - deviceSupport.performInitialized((enable ? "Enable" : "Disable") + " realtime heartrate").notify(characteristicHeartRate, enable).queue(deviceSupport.getQueue()); - } catch (IOException ex) { - LOG.error("Unable to change realtime steps notification to: " + enable, ex); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrSupport.java deleted file mode 100644 index f0ece4396..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/VivomoveHrSupport.java +++ /dev/null @@ -1,1074 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr; - -import android.bluetooth.BluetoothGatt; -import android.bluetooth.BluetoothGattCharacteristic; -import android.os.Build; -import android.widget.Toast; -import com.google.protobuf.InvalidProtocolBufferException; -import de.greenrobot.dao.query.Query; -import nodomain.freeyourgadget.gadgetbridge.BuildConfig; -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; -import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.GarminFitFile; -import nodomain.freeyourgadget.gadgetbridge.entities.GarminFitFileDao; -import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; -import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiCore; -import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiDeviceStatus; -import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiFindMyWatch; -import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiSmartProto; -import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ams.AmsEntity; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ams.AmsEntityAttribute; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsAttribute; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsAttributeRequest; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsEvent; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsGetNotificationAttributeCommand; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsGetNotificationAttributesResponse; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.GncsDataSourceQueue; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.downloads.DirectoryData; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.downloads.DirectoryEntry; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.downloads.FileDownloadListener; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.downloads.FileDownloadQueue; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitBool; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitDbImporter; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitMessageDefinitions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitParser; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitWeatherConditions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.AuthNegotiationMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.AuthNegotiationResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.BatteryStatusMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.ConfigurationMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.CreateFileResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.CurrentTimeRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.CurrentTimeRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DeviceInformationMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DeviceInformationResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DirectoryFileFilterRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DirectoryFileFilterResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DownloadRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FindMyPhoneRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FitDataMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FitDataResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FitDefinitionMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FitDefinitionResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GenericResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsControlPointMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsControlPointResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsDataSourceResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsNotificationSourceMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MusicControlCapabilitiesMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MusicControlCapabilitiesResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MusicControlEntityUpdateMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.NotificationServiceSubscriptionMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.NotificationServiceSubscriptionResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.ProtobufRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.ProtobufRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.ResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SetDeviceSettingsMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SetDeviceSettingsResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SupportedFileTypesRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SupportedFileTypesResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SyncRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SystemEventMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.SystemEventResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.UploadRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.WeatherRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.WeatherRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.notifications.NotificationData; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.notifications.NotificationStorage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.uploads.FileUploadQueue; -import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; -import nodomain.freeyourgadget.gadgetbridge.util.GB; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - -public class VivomoveHrSupport extends AbstractBTLEDeviceSupport implements FileDownloadListener { - private static final Logger LOG = LoggerFactory.getLogger(VivomoveHrSupport.class); - - // Should all FIT data files be fully stored into the database? (Currently, there is no user-friendly way to - // retrieve them, anyway.) - // TODO: Choose what to do with them. Either store them or remove the field from DB. - private static final boolean STORE_FIT_FILES = false; - - private final GfdiPacketParser gfdiPacketParser = new GfdiPacketParser(); - private Set capabilities; - - private int lastProtobufRequestId; - private int maxPacketSize; - - private final FitParser fitParser = new FitParser(FitMessageDefinitions.ALL_DEFINITIONS); - private final NotificationStorage notificationStorage = new NotificationStorage(); - private VivomoveHrCommunicator communicator; - private RealTimeActivityHandler realTimeActivityHandler; - private GncsDataSourceQueue gncsDataSourceQueue; - private FileDownloadQueue fileDownloadQueue; - private FileUploadQueue fileUploadQueue; - private FitDbImporter fitImporter; - private boolean notificationSubscription; - - public VivomoveHrSupport() { - super(LOG); - - addSupportedService(VivomoveConstants.UUID_SERVICE_GARMIN_GFDI); - addSupportedService(VivomoveConstants.UUID_SERVICE_GARMIN_REALTIME); - } - - private int getNextProtobufRequestId() { - lastProtobufRequestId = (lastProtobufRequestId + 1) % 65536; - return lastProtobufRequestId; - } - - @Override - protected TransactionBuilder initializeDevice(TransactionBuilder builder) { - LOG.info("Initializing"); - - gbDevice.setState(GBDevice.State.INITIALIZING); - gbDevice.sendDeviceUpdateIntent(getContext()); - - communicator = new VivomoveHrCommunicator(this); - realTimeActivityHandler = new RealTimeActivityHandler(this); - - builder.setCallback(this); - communicator.start(builder); - fileDownloadQueue = new FileDownloadQueue(communicator, this); - fileUploadQueue = new FileUploadQueue(communicator); - - LOG.info("Initialization Done"); - - // OK, this is not perfect: we should not be INITIALIZED until “connected AND all the necessary initialization - // steps have been performed. At the very least, this means that basic information like device name, firmware - // version, hardware revision (as applicable) is available in the GBDevice”. But we cannot send any message - // until we are INITIALIZED. So what can we do… - gbDevice.setState(GBDevice.State.INITIALIZED); - gbDevice.sendDeviceUpdateIntent(getContext()); - - sendMessage(new AuthNegotiationMessage(AuthNegotiationMessage.LONG_TERM_KEY_AVAILABILITY_NONE, AuthNegotiationMessage.ENCRYPTION_ALGORITHM_NONE).packet); - - return builder; - } - - @Override - public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { - final UUID characteristicUUID = characteristic.getUuid(); - if (super.onCharacteristicChanged(gatt, characteristic)) { - LOG.debug("Change of characteristic {} handled by parent", characteristicUUID); - return true; - } - - final byte[] data = characteristic.getValue(); - if (data.length == 0) { - LOG.debug("No data received on change of characteristic {}", characteristicUUID); - return true; - } - - if (VivomoveConstants.UUID_CHARACTERISTIC_GARMIN_GFDI_RECEIVE.equals(characteristicUUID)) { - handleReceivedGfdiBytes(data); - } else if (realTimeActivityHandler.tryHandleChangedCharacteristic(characteristicUUID, data)) { - // handled by real-time activity handler - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Unknown characteristic {} changed: {}", characteristicUUID, GB.hexdump(data)); - } - } - - return true; - } - - private void sendMessage(byte[] messageBytes) { - communicator.sendMessage(messageBytes); - } - - private void handleReceivedGfdiBytes(byte[] data) { - gfdiPacketParser.receivedBytes(data); - LOG.debug("Received {} GFDI bytes", data.length); - byte[] packet; - while ((packet = gfdiPacketParser.retrievePacket()) != null) { - LOG.debug("Processing a {}B GFDI packet", packet.length); - processGfdiPacket(packet); - } - } - - private void processGfdiPacket(byte[] packet) { - final int size = BLETypeConversions.toUint16(packet, 0); - if (size != packet.length) { - LOG.error("Received GFDI packet with invalid length: {} vs {}", size, packet.length); - return; - } - final int crc = BLETypeConversions.toUint16(packet, packet.length - 2); - final int correctCrc = ChecksumCalculator.computeCrc(packet, 0, packet.length - 2); - if (crc != correctCrc) { - LOG.error("Received GFDI packet with invalid CRC: {} vs {}", crc, correctCrc); - return; - } - - final int messageType = BLETypeConversions.toUint16(packet, 2); - switch (messageType) { - case VivomoveConstants.MESSAGE_RESPONSE: - processResponseMessage(ResponseMessage.parsePacket(packet), packet); - break; - - case VivomoveConstants.MESSAGE_FILE_TRANSFER_DATA: - fileDownloadQueue.onFileTransferData(FileTransferDataMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_DEVICE_INFORMATION: - processDeviceInformationMessage(DeviceInformationMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_WEATHER_REQUEST: - processWeatherRequest(WeatherRequestMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_MUSIC_CONTROL_CAPABILITIES: - processMusicControlCapabilities(MusicControlCapabilitiesMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_CURRENT_TIME_REQUEST: - processCurrentTimeRequest(CurrentTimeRequestMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_SYNC_REQUEST: - processSyncRequest(SyncRequestMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_FIND_MY_PHONE: - processFindMyPhoneRequest(FindMyPhoneRequestMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_CANCEL_FIND_MY_PHONE: - processCancelFindMyPhoneRequest(); - break; - - case VivomoveConstants.MESSAGE_NOTIFICATION_SERVICE_SUBSCRIPTION: - processNotificationServiceSubscription(NotificationServiceSubscriptionMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_GNCS_CONTROL_POINT_REQUEST: - processGncsControlPointRequest(GncsControlPointMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_CONFIGURATION: - processConfigurationMessage(ConfigurationMessage.parsePacket(packet)); - break; - - case VivomoveConstants.MESSAGE_PROTOBUF_RESPONSE: - processProtobufResponse(ProtobufRequestMessage.parsePacket(packet)); - break; - - default: - if (LOG.isInfoEnabled()) { - LOG.info("Unknown message type {}: {}", messageType, GB.hexdump(packet, 0, packet.length)); - } - break; - } - } - - private void processCancelFindMyPhoneRequest() { - LOG.info("Processing request to cancel find-my-phone"); - sendMessage(new GenericResponseMessage(VivomoveConstants.MESSAGE_FIND_MY_PHONE, 0).packet); - - final GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); - findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP; - evaluateGBDeviceEvent(findPhoneEvent); - } - - private void processFindMyPhoneRequest(FindMyPhoneRequestMessage requestMessage) { - LOG.info("Processing find-my-phone request ({} s)", requestMessage.duration); - - sendMessage(new GenericResponseMessage(VivomoveConstants.MESSAGE_FIND_MY_PHONE, 0).packet); - - final GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone(); - findPhoneEvent.event = GBDeviceEventFindPhone.Event.START; - evaluateGBDeviceEvent(findPhoneEvent); - } - - private void processGncsControlPointRequest(GncsControlPointMessage requestMessage) { - if (requestMessage == null) { - // TODO: Proper error handling with specific error code - sendMessage(new GncsControlPointResponseMessage(VivomoveConstants.STATUS_ACK, GncsControlPointResponseMessage.RESPONSE_ANCS_ERROR_OCCURRED, GncsControlPointResponseMessage.ANCS_ERROR_UNKNOWN_ANCS_COMMAND).packet); - return; - } - switch (requestMessage.command.command) { - case GET_NOTIFICATION_ATTRIBUTES: - final AncsGetNotificationAttributeCommand getNotificationAttributeCommand = (AncsGetNotificationAttributeCommand) requestMessage.command; - LOG.info("Processing ANCS request to get attributes of notification #{}", getNotificationAttributeCommand.notificationUID); - sendMessage(new GncsControlPointResponseMessage(VivomoveConstants.STATUS_ACK, GncsControlPointResponseMessage.RESPONSE_SUCCESSFUL, GncsControlPointResponseMessage.ANCS_ERROR_NO_ERROR).packet); - final NotificationData notificationData = notificationStorage.retrieveNotification(getNotificationAttributeCommand.notificationUID); - if (notificationData == null) { - LOG.warn("Notification #{} not registered", getNotificationAttributeCommand.notificationUID); - } - final Map attributes = new LinkedHashMap<>(); - for (final AncsAttributeRequest attributeRequest : getNotificationAttributeCommand.attributes) { - final AncsAttribute attribute = attributeRequest.attribute; - final String attributeValue = notificationData == null ? null : notificationData.getAttribute(attributeRequest.attribute); - final String valueShortened = attributeRequest.maxLength > 0 && attributeValue != null && attributeValue.length() > attributeRequest.maxLength ? attributeValue.substring(0, attributeRequest.maxLength) : attributeValue; - LOG.debug("Requested ANCS attribute {}: '{}'", attribute, valueShortened); - attributes.put(attribute, valueShortened == null ? "" : valueShortened); - } - gncsDataSourceQueue.addToQueue(new AncsGetNotificationAttributesResponse(getNotificationAttributeCommand.notificationUID, attributes).packet); - break; - - default: - LOG.error("Unknown GNCS control point command {}", requestMessage.command.command); - sendMessage(new GncsControlPointResponseMessage(VivomoveConstants.STATUS_ACK, GncsControlPointResponseMessage.RESPONSE_ANCS_ERROR_OCCURRED, GncsControlPointResponseMessage.ANCS_ERROR_UNKNOWN_ANCS_COMMAND).packet); - break; - } - } - - private void processNotificationServiceSubscription(NotificationServiceSubscriptionMessage requestMessage) { - LOG.info("Processing notification service subscription request message: intent={}, flags={}", requestMessage.intentIndicator, requestMessage.featureFlags); - notificationSubscription = requestMessage.intentIndicator == NotificationServiceSubscriptionMessage.INTENT_SUBSCRIBE; - sendMessage(new NotificationServiceSubscriptionResponseMessage(0, 0, requestMessage.intentIndicator, requestMessage.featureFlags).packet); - } - - private void processSyncRequest(SyncRequestMessage requestMessage) { - if (LOG.isInfoEnabled()) { - final StringBuilder requestedTypes = new StringBuilder(); - for (GarminMessageType type : requestMessage.fileTypes) { - if (requestedTypes.length() > 0) { - requestedTypes.append(", "); - } - requestedTypes.append(type); - } - LOG.info("Processing sync request message: option={}, types: {}", requestMessage.option, requestedTypes); - } - sendMessage(new GenericResponseMessage(VivomoveConstants.MESSAGE_SYNC_REQUEST, 0).packet); - if (requestMessage.option != SyncRequestMessage.OPTION_INVISIBLE) { - doSync(); - } - } - - private void processProtobufResponse(ProtobufRequestMessage requestMessage) { - LOG.info("Received protobuf response #{}, {}B@{}/{}: {}", requestMessage.requestId, requestMessage.protobufDataLength, requestMessage.dataOffset, requestMessage.totalProtobufLength, GB.hexdump(requestMessage.messageBytes, 0, requestMessage.messageBytes.length)); - sendMessage(new GenericResponseMessage(VivomoveConstants.MESSAGE_PROTOBUF_RESPONSE, 0).packet); - final GdiSmartProto.Smart smart; - try { - smart = GdiSmartProto.Smart.parseFrom(requestMessage.messageBytes); - } catch (InvalidProtocolBufferException e) { - LOG.error("Failed to parse protobuf message ({}): {}", e.getLocalizedMessage(), GB.hexdump(requestMessage.messageBytes, 0, requestMessage.messageBytes.length)); - return; - } - boolean processed = false; - if (smart.hasDeviceStatusService()) { - processProtobufDeviceStatusResponse(smart.getDeviceStatusService()); - processed = true; - } - if (smart.hasFindMyWatchService()) { - processProtobufFindMyWatchResponse(smart.getFindMyWatchService()); - processed = true; - } - if (smart.hasCoreService()) { - processProtobufCoreResponse(smart.getCoreService()); - processed = true; - } - if (!processed) { - LOG.warn("Unknown protobuf response: {}", smart); - } - } - - private void processProtobufCoreResponse(GdiCore.CoreService coreService) { - if (coreService.hasSyncResponse()) { - final GdiCore.CoreService.SyncResponse syncResponse = coreService.getSyncResponse(); - LOG.info("Received sync status: {}", syncResponse.getStatus()); - } - LOG.warn("Unknown CoreService response: {}", coreService); - } - - private void processProtobufDeviceStatusResponse(GdiDeviceStatus.DeviceStatusService deviceStatusService) { - if (deviceStatusService.hasRemoteDeviceBatteryStatusResponse()) { - final GdiDeviceStatus.DeviceStatusService.RemoteDeviceBatteryStatusResponse batteryStatusResponse = deviceStatusService.getRemoteDeviceBatteryStatusResponse(); - final int batteryLevel = batteryStatusResponse.getCurrentBatteryLevel(); - LOG.info("Received remote battery status {}: level={}", batteryStatusResponse.getStatus(), batteryLevel); - final GBDeviceEventBatteryInfo batteryEvent = new GBDeviceEventBatteryInfo(); - batteryEvent.level = (short) batteryLevel; - handleGBDeviceEvent(batteryEvent); - return; - } - if (deviceStatusService.hasActivityStatusResponse()) { - final GdiDeviceStatus.DeviceStatusService.ActivityStatusResponse activityStatusResponse = deviceStatusService.getActivityStatusResponse(); - LOG.info("Received activity status: {}", activityStatusResponse.getStatus()); - return; - } - LOG.warn("Unknown DeviceStatusService response: {}", deviceStatusService); - } - - private void processProtobufFindMyWatchResponse(GdiFindMyWatch.FindMyWatchService findMyWatchService) { - if (findMyWatchService.hasCancelRequest()) { - LOG.info("Watch search cancelled, watch found"); - GBApplication.deviceService().onFindDevice(false); - return; - } - if (findMyWatchService.hasCancelResponse() || findMyWatchService.hasFindResponse()) { - LOG.debug("Received findMyWatch response"); - return; - } - LOG.warn("Unknown FindMyWatchService response: {}", findMyWatchService); - } - - private void processMusicControlCapabilities(MusicControlCapabilitiesMessage capabilitiesMessage) { - LOG.info("Processing music control capabilities request caps={}", capabilitiesMessage.supportedCapabilities); - sendMessage(new MusicControlCapabilitiesResponseMessage(0, GarminMusicControlCommand.values()).packet); - } - - private void processWeatherRequest(WeatherRequestMessage requestMessage) { - LOG.info("Processing weather request fmt={}, {} hrs, {}/{}", requestMessage.format, requestMessage.hoursOfForecast, requestMessage.latitude, requestMessage.longitude); - sendMessage(new WeatherRequestResponseMessage(0, 0, 1, 300).packet); - } - - private void processCurrentTimeRequest(CurrentTimeRequestMessage requestMessage) { - long now = System.currentTimeMillis(); - final TimeZone timeZone = TimeZone.getDefault(); - final Calendar calendar = Calendar.getInstance(timeZone); - calendar.setTimeInMillis(now); - int dstOffset = calendar.get(Calendar.DST_OFFSET) / 1000; - int timeZoneOffset = timeZone.getOffset(now) / 1000; - int garminTimestamp = GarminTimeUtils.javaMillisToGarminTimestamp(now); - - LOG.info("Processing current time request #{}: time={}, DST={}, ofs={}", requestMessage.referenceID, garminTimestamp, dstOffset, timeZoneOffset); - sendMessage(new CurrentTimeRequestResponseMessage(0, requestMessage.referenceID, garminTimestamp, timeZoneOffset, dstOffset).packet); - } - - private void processResponseMessage(ResponseMessage responseMessage, byte[] packet) { - switch (responseMessage.requestID) { - case VivomoveConstants.MESSAGE_DIRECTORY_FILE_FILTER_REQUEST: - processDirectoryFileFilterResponse(DirectoryFileFilterResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_DOWNLOAD_REQUEST: - fileDownloadQueue.onDownloadRequestResponse(DownloadRequestResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_UPLOAD_REQUEST: - fileUploadQueue.onUploadRequestResponse(UploadRequestResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_FILE_TRANSFER_DATA: - fileUploadQueue.onFileTransferResponse(FileTransferDataResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_CREATE_FILE_REQUEST: - fileUploadQueue.onCreateFileRequestResponse(CreateFileResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_FIT_DEFINITION: - processFitDefinitionResponse(FitDefinitionResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_FIT_DATA: - processFitDataResponse(FitDataResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_PROTOBUF_REQUEST: - processProtobufRequestResponse(ProtobufRequestResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_DEVICE_SETTINGS: - processDeviceSettingsResponse(SetDeviceSettingsResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_SYSTEM_EVENT: - processSystemEventResponse(SystemEventResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_SUPPORTED_FILE_TYPES_REQUEST: - processSupportedFileTypesResponse(SupportedFileTypesResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_GNCS_DATA_SOURCE: - gncsDataSourceQueue.responseReceived(GncsDataSourceResponseMessage.parsePacket(packet)); - break; - case VivomoveConstants.MESSAGE_AUTH_NEGOTIATION: - processAuthNegotiationRequestResponse(AuthNegotiationResponseMessage.parsePacket(packet)); - break; - default: - LOG.info("Received response to message {}: {}", responseMessage.requestID, responseMessage.getStatusStr()); - break; - } - } - - private void processDirectoryFileFilterResponse(DirectoryFileFilterResponseMessage responseMessage) { - if (responseMessage.status == VivomoveConstants.STATUS_ACK && responseMessage.response == DirectoryFileFilterResponseMessage.RESPONSE_DIRECTORY_FILTER_APPLIED) { - LOG.info("Received response to directory file filter request: {}/{}, requesting download of directory data", responseMessage.status, responseMessage.response); - fileDownloadQueue.addToDownloadQueue(0, 0); - } else { - LOG.error("Directory file filter request failed: {}/{}", responseMessage.status, responseMessage.response); - } - } - - private void processSupportedFileTypesResponse(SupportedFileTypesResponseMessage responseMessage) { - final StringBuilder supportedTypes = new StringBuilder(); - for (SupportedFileTypesResponseMessage.FileTypeInfo type : responseMessage.fileTypes) { - if (supportedTypes.length() > 0) { - supportedTypes.append(", "); - } - supportedTypes.append(String.format(Locale.ROOT, "%d/%d: %s", type.fileDataType, type.fileSubType, type.garminDeviceFileType)); - } - LOG.info("Received the list of supported file types (status={}): {}", responseMessage.status, supportedTypes); - } - - private void processDeviceSettingsResponse(SetDeviceSettingsResponseMessage responseMessage) { - LOG.info("Received response to device settings message: status={}, response={}", responseMessage.status, responseMessage.response); - } - - private void processAuthNegotiationRequestResponse(AuthNegotiationResponseMessage responseMessage) { - LOG.info("Received response to auth negotiation message: status={}, response={}, LTK={}, algorithms={}", responseMessage.status, responseMessage.response, responseMessage.longTermKeyAvailability, responseMessage.supportedEncryptionAlgorithms); - } - - private void processSystemEventResponse(SystemEventResponseMessage responseMessage) { - LOG.info("Received response to system event message: status={}, response={}", responseMessage.status, responseMessage.response); - } - - private void processFitDefinitionResponse(FitDefinitionResponseMessage responseMessage) { - LOG.info("Received response to FIT definition message: status={}, FIT response={}", responseMessage.status, responseMessage.fitResponse); - } - - private void processFitDataResponse(FitDataResponseMessage responseMessage) { - LOG.info("Received response to FIT data message: status={}, FIT response={}", responseMessage.status, responseMessage.fitResponse); - } - - private void processProtobufRequestResponse(ProtobufRequestResponseMessage responseMessage) { - LOG.info("Received response to protobuf message #{}@{}: status={}, error={}", responseMessage.requestId, responseMessage.dataOffset, responseMessage.protobufStatus, responseMessage.error); - } - - private void processDeviceInformationMessage(DeviceInformationMessage deviceInformationMessage) { - LOG.info("Received device information: protocol {}, product {}, unit {}, SW {}, max packet {}, BT name {}, device name {}, device model {}", deviceInformationMessage.protocolVersion, deviceInformationMessage.productNumber, deviceInformationMessage.unitNumber, deviceInformationMessage.getSoftwareVersionStr(), deviceInformationMessage.maxPacketSize, deviceInformationMessage.bluetoothFriendlyName, deviceInformationMessage.deviceName, deviceInformationMessage.deviceModel); - - this.maxPacketSize = deviceInformationMessage.maxPacketSize; - this.gncsDataSourceQueue = new GncsDataSourceQueue(communicator, maxPacketSize); - - final GBDeviceEventVersionInfo deviceEventVersionInfo = new GBDeviceEventVersionInfo(); - deviceEventVersionInfo.fwVersion = deviceInformationMessage.getSoftwareVersionStr(); - handleGBDeviceEvent(deviceEventVersionInfo); - - gbDevice.setState(GBDevice.State.INITIALIZED); - gbDevice.sendDeviceUpdateIntent(getContext()); - - // prepare and send response - final boolean protocolVersionSupported = deviceInformationMessage.protocolVersion / 100 == 1; - if (!protocolVersionSupported) { - LOG.error("Unsupported protocol version {}", deviceInformationMessage.protocolVersion); - } - final int protocolFlags = protocolVersionSupported ? 1 : 0; - final DeviceInformationResponseMessage deviceInformationResponseMessage = new DeviceInformationResponseMessage(VivomoveConstants.STATUS_ACK, 112, -1, VivomoveConstants.GADGETBRIDGE_UNIT_NUMBER, BuildConfig.VERSION_CODE, 16384, getBluetoothAdapter().getName(), Build.MANUFACTURER, Build.DEVICE, protocolFlags); - - sendMessage(deviceInformationResponseMessage.packet); - } - - private void processConfigurationMessage(ConfigurationMessage configurationMessage) { - this.capabilities = GarminCapability.setFromBinary(configurationMessage.configurationPayload); - - if (LOG.isInfoEnabled()) { - LOG.info("Received configuration message; capabilities: {}", GarminCapability.setToString(capabilities)); - } - - // prepare and send response - sendMessage(new GenericResponseMessage(VivomoveConstants.MESSAGE_CONFIGURATION, VivomoveConstants.STATUS_ACK).packet); - - // and report our own configuration/capabilities - final byte[] ourCapabilityFlags = GarminCapability.setToBinary(VivomoveConstants.OUR_CAPABILITIES); - sendMessage(new ConfigurationMessage(ourCapabilityFlags).packet); - - // initialize current time and settings - sendCurrentTime(); - sendSettings(); - - // and everything is ready now - sendSyncReady(); - requestBatteryStatusUpdate(); - sendFitDefinitions(); - sendFitConnectivityMessage(); - requestSupportedFileTypes(); - } - - private void sendProtobufRequest(byte[] protobufMessage) { - final int requestId = getNextProtobufRequestId(); - if (LOG.isDebugEnabled()) { - LOG.debug("Sending {}B protobuf request #{}: {}", protobufMessage.length, requestId, GB.hexdump(protobufMessage, 0, protobufMessage.length)); - } - sendMessage(new ProtobufRequestMessage(requestId, 0, protobufMessage.length, protobufMessage.length, protobufMessage).packet); - } - - private void requestBatteryStatusUpdate() { - sendProtobufRequest( - GdiSmartProto.Smart.newBuilder() - .setDeviceStatusService( - GdiDeviceStatus.DeviceStatusService.newBuilder() - .setRemoteDeviceBatteryStatusRequest( - GdiDeviceStatus.DeviceStatusService.RemoteDeviceBatteryStatusRequest.newBuilder() - ) - ) - .build() - .toByteArray()); - } - - private void requestActivityStatus() { - sendProtobufRequest( - GdiSmartProto.Smart.newBuilder() - .setDeviceStatusService( - GdiDeviceStatus.DeviceStatusService.newBuilder() - .setActivityStatusRequest( - GdiDeviceStatus.DeviceStatusService.ActivityStatusRequest.newBuilder() - ) - ) - .build() - .toByteArray()); - } - - private void sendRequestSync() { - sendProtobufRequest( - GdiSmartProto.Smart.newBuilder() - .setCoreService( - GdiCore.CoreService.newBuilder() - .setSyncRequest( - GdiCore.CoreService.SyncRequest.newBuilder() - ) - ) - .build() - .toByteArray()); - } - - private void requestSupportedFileTypes() { - LOG.info("Requesting list of supported file types"); - sendMessage(new SupportedFileTypesRequestMessage().packet); - } - - private void sendSyncReady() { - sendMessage(new SystemEventMessage(GarminSystemEventType.SYNC_READY, 0).packet); - } - - private void sendCurrentTime() { - final Map settings = new LinkedHashMap<>(3); - - long now = System.currentTimeMillis(); - final TimeZone timeZone = TimeZone.getDefault(); - final Calendar calendar = Calendar.getInstance(timeZone); - calendar.setTimeInMillis(now); - int dstOffset = calendar.get(Calendar.DST_OFFSET) / 1000; - int timeZoneOffset = timeZone.getOffset(now) / 1000; - int garminTimestamp = GarminTimeUtils.javaMillisToGarminTimestamp(now); - - settings.put(GarminDeviceSetting.CURRENT_TIME, garminTimestamp); - settings.put(GarminDeviceSetting.DAYLIGHT_SAVINGS_TIME_OFFSET, dstOffset); - settings.put(GarminDeviceSetting.TIME_ZONE_OFFSET, timeZoneOffset); - // TODO: NEXT_DAYLIGHT_SAVINGS_START, NEXT_DAYLIGHT_SAVINGS_END - LOG.info("Setting time to {}, dstOffset={}, tzOffset={} (DST={})", garminTimestamp, dstOffset, timeZoneOffset, timeZone.inDaylightTime(new Date(now)) ? 1 : 0); - sendMessage(new SetDeviceSettingsMessage(settings).packet); - } - - private void sendSettings() { - final Map settings = new LinkedHashMap<>(3); - - settings.put(GarminDeviceSetting.WEATHER_CONDITIONS_ENABLED, true); - settings.put(GarminDeviceSetting.WEATHER_ALERTS_ENABLED, true); - settings.put(GarminDeviceSetting.AUTO_UPLOAD_ENABLED, true); - LOG.info("Sending settings"); - sendMessage(new SetDeviceSettingsMessage(settings).packet); - } - - private void sendFitDefinitions() { - sendMessage(new FitDefinitionMessage( - FitMessageDefinitions.DEFINITION_CONNECTIVITY, - FitMessageDefinitions.DEFINITION_WEATHER_CONDITIONS, - FitMessageDefinitions.DEFINITION_WEATHER_ALERT, - FitMessageDefinitions.DEFINITION_DEVICE_SETTINGS - ).packet); - } - - private void sendFitConnectivityMessage() { - final FitMessage connectivityMessage = new FitMessage(FitMessageDefinitions.DEFINITION_CONNECTIVITY); - connectivityMessage.setField(0, FitBool.TRUE); - connectivityMessage.setField(1, FitBool.TRUE); - connectivityMessage.setField(2, FitBool.INVALID); - connectivityMessage.setField(4, FitBool.TRUE); - connectivityMessage.setField(5, FitBool.TRUE); - connectivityMessage.setField(6, FitBool.TRUE); - connectivityMessage.setField(7, FitBool.TRUE); - connectivityMessage.setField(8, FitBool.TRUE); - connectivityMessage.setField(9, FitBool.TRUE); - connectivityMessage.setField(10, FitBool.TRUE); - connectivityMessage.setField(13, FitBool.TRUE); - sendMessage(new FitDataMessage(connectivityMessage).packet); - } - - private void sendWeatherConditions(WeatherSpec weather) { - final FitMessage weatherConditionsMessage = new FitMessage(FitMessageDefinitions.DEFINITION_WEATHER_CONDITIONS); - weatherConditionsMessage.setField(253, GarminTimeUtils.unixTimeToGarminTimestamp(weather.timestamp)); - weatherConditionsMessage.setField(0, 0); // 0 = current, 2 = hourly_forecast, 3 = daily_forecast - weatherConditionsMessage.setField(1, weather.currentTemp); - weatherConditionsMessage.setField(2, FitWeatherConditions.openWeatherCodeToFitWeatherStatus(weather.currentConditionCode)); - weatherConditionsMessage.setField(3, weather.windDirection); - weatherConditionsMessage.setField(4, Math.round(weather.windSpeed * 1000.0 / 3.6)); - weatherConditionsMessage.setField(7, weather.currentHumidity); - weatherConditionsMessage.setField(8, weather.location); - final Calendar timestamp = Calendar.getInstance(); - timestamp.setTimeInMillis(weather.timestamp * 1000L); - weatherConditionsMessage.setField(12, timestamp.get(Calendar.DAY_OF_WEEK)); - weatherConditionsMessage.setField(13, weather.todayMaxTemp); - weatherConditionsMessage.setField(14, weather.todayMinTemp); - - sendMessage(new FitDataMessage(weatherConditionsMessage).packet); - } - - /* - private void sendWeatherAlert() { - final FitMessage weatherConditionsMessage = new FitMessage(FitMessageDefinitions.DEFINITION_WEATHER_ALERT); - weatherConditionsMessage.setField(253, GarminTimeUtils.javaMillisToGarminTimestamp(System.currentTimeMillis())); - weatherConditionsMessage.setField(0, "TESTRPT"); - final Calendar issue = Calendar.getInstance(); - issue.set(2019, 8, 27, 0, 0, 0); - final Calendar expiry = Calendar.getInstance(); - issue.set(2019, 8, 29, 0, 0, 0); - weatherConditionsMessage.setField(1, GarminTimeUtils.javaMillisToGarminTimestamp(issue.getTimeInMillis())); - weatherConditionsMessage.setField(2, GarminTimeUtils.javaMillisToGarminTimestamp(expiry.getTimeInMillis())); - weatherConditionsMessage.setField(3, FitWeatherConditions.ALERT_SEVERITY_ADVISORY); - weatherConditionsMessage.setField(4, FitWeatherConditions.ALERT_TYPE_SEVERE_THUNDERSTORM); - - sendMessage(new FitDataMessage(weatherConditionsMessage).packet); - } - */ - - private void sendNotification(AncsEvent event, NotificationData notification) { - if (event == AncsEvent.NOTIFICATION_ADDED) { - notificationStorage.registerNewNotification(notification); - } else { - notificationStorage.deleteNotification(notification.spec.getId()); - } - sendMessage(new GncsNotificationSourceMessage(event, notification.flags, notification.category, notificationStorage.getCountInCategory(notification.category), notification.spec.getId()).packet); - } - - private void listFiles(int filterType) { - LOG.info("Requesting file list (filter={})", filterType); - sendMessage(new DirectoryFileFilterRequestMessage(filterType).packet); - } - - private void downloadFile(int fileIndex) { - LOG.info("Requesting download of file {}", fileIndex); - fileDownloadQueue.addToDownloadQueue(fileIndex, 0); - } - - private void downloadGarminDeviceXml() { - LOG.info("Requesting Garmin device XML download"); - fileDownloadQueue.addToDownloadQueue(VivomoveConstants.GARMIN_DEVICE_XML_FILE_INDEX, 0); - } - - private void sendBatteryStatus(int batteryLevel) { - LOG.info("Sending battery status"); - sendMessage(new BatteryStatusMessage(batteryLevel).packet); - } - - private void doSync() { - LOG.info("Starting sync"); - fitImporter = new FitDbImporter(getDevice()); - // sendMessage(new SystemEventMessage(GarminSystemEventType.PAIR_START, 0).packet); - listFiles(DirectoryFileFilterRequestMessage.FILTER_NO_FILTER); - // TODO: Localization - GB.updateTransferNotification(null, "Downloading list of files", true, 0, getContext()); - } - - @Override - public boolean useAutoConnect() { - return true; - } - - @Override - public void onNotification(NotificationSpec notificationSpec) { - if (notificationSubscription) { - sendNotification(AncsEvent.NOTIFICATION_ADDED, new NotificationData(notificationSpec)); - } else { - LOG.debug("No notification subscription is active, ignoring notification"); - } - } - - @Override - public void onDeleteNotification(int id) { - final NotificationData notificationData = notificationStorage.retrieveNotification(id); - if (notificationData != null) { - sendNotification(AncsEvent.NOTIFICATION_REMOVED, notificationData); - } - } - - @Override - public void onSetTime() { - sendCurrentTime(); - } - - @Override - public void onSetMusicInfo(MusicSpec musicSpec) { - sendMessage(new MusicControlEntityUpdateMessage( - new AmsEntityAttribute(AmsEntity.TRACK, AmsEntityAttribute.TRACK_ATTRIBUTE_ARTIST, 0, musicSpec.artist), - new AmsEntityAttribute(AmsEntity.TRACK, AmsEntityAttribute.TRACK_ATTRIBUTE_ALBUM, 0, musicSpec.album), - new AmsEntityAttribute(AmsEntity.TRACK, AmsEntityAttribute.TRACK_ATTRIBUTE_TITLE, 0, musicSpec.track), - new AmsEntityAttribute(AmsEntity.TRACK, AmsEntityAttribute.TRACK_ATTRIBUTE_DURATION, 0, String.valueOf(musicSpec.duration)) - ).packet); - } - - @Override - public void onEnableRealtimeSteps(boolean enable) { - communicator.enableRealtimeSteps(enable); - } - - @Override - public void onFetchRecordedData(int dataTypes) { - doSync(); - } - - @Override - public void onReset(int flags) { - switch (flags) { - case GBDeviceProtocol.RESET_FLAGS_FACTORY_RESET: - LOG.warn("Requesting factory reset"); - sendMessage(new SystemEventMessage(GarminSystemEventType.FACTORY_RESET, 1).packet); - break; - - default: - GB.toast(getContext(), "This kind of reset not supported for this device", Toast.LENGTH_LONG, GB.ERROR); - break; - } - } - - @Override - public void onEnableRealtimeHeartRateMeasurement(boolean enable) { - communicator.enableRealtimeHeartRate(enable); - } - - @Override - public void onFindDevice(boolean start) { - if (start) { - sendProtobufRequest( - GdiSmartProto.Smart.newBuilder() - .setFindMyWatchService( - GdiFindMyWatch.FindMyWatchService.newBuilder() - .setFindRequest( - GdiFindMyWatch.FindMyWatchService.FindMyWatchRequest.newBuilder() - .setTimeout(60) - ) - ) - .build() - .toByteArray()); - } else { - sendProtobufRequest( - GdiSmartProto.Smart.newBuilder() - .setFindMyWatchService( - GdiFindMyWatch.FindMyWatchService.newBuilder() - .setCancelRequest( - GdiFindMyWatch.FindMyWatchService.FindMyWatchCancelRequest.newBuilder() - ) - ) - .build() - .toByteArray()); - } - } - - private void updateDeviceSettings() { - final FitMessage deviceSettingsMessage = new FitMessage(FitMessageDefinitions.DEFINITION_DEVICE_SETTINGS); - deviceSettingsMessage.setField("bluetooth_connection_alerts_enabled", 0); - deviceSettingsMessage.setField("auto_lock_enabled", 0); - deviceSettingsMessage.setField("activity_tracker_enabled", 1); - deviceSettingsMessage.setField("alarm_time", 0); - deviceSettingsMessage.setField("ble_auto_upload_enabled", 1); - deviceSettingsMessage.setField("autosync_min_steps", 1000); - deviceSettingsMessage.setField("vibration_intensity", 2); - deviceSettingsMessage.setField("screen_timeout", 0); - deviceSettingsMessage.setField("mounting_side", 1); - deviceSettingsMessage.setField("phone_notification_activity_filter", 0); - deviceSettingsMessage.setField("auto_goal_enabled", 1); - deviceSettingsMessage.setField("autosync_min_time", 60); - deviceSettingsMessage.setField("glance_mode_layout", 0); - deviceSettingsMessage.setField("time_offset", 7200); - deviceSettingsMessage.setField("phone_notification_default_filter", 0); - deviceSettingsMessage.setField("alarm_mode", -1); - deviceSettingsMessage.setField("backlight_timeout", 5); - deviceSettingsMessage.setField("sedentary_hr_alert_threshold", 100); - deviceSettingsMessage.setField("backlight_brightness", 0); - deviceSettingsMessage.setField("time_zone", 254); - deviceSettingsMessage.setField("sedentary_hr_alert_state", 0); - deviceSettingsMessage.setField("auto_activity_start_enabled", 0); - deviceSettingsMessage.setField("alarm_days", 0); - deviceSettingsMessage.setField("default_page", 1); - deviceSettingsMessage.setField("message_tones_enabled", 2); - deviceSettingsMessage.setField("key_tones_enabled", 2); - deviceSettingsMessage.setField("date_mode", 0); - deviceSettingsMessage.setField("backlight_gesture", 1); - deviceSettingsMessage.setField("backlight_mode", 3); - deviceSettingsMessage.setField("move_alert_enabled", 1); - deviceSettingsMessage.setField("sleep_do_not_disturb_enabled", 0); - deviceSettingsMessage.setField("display_orientation", 2); - deviceSettingsMessage.setField("time_mode", 1); - deviceSettingsMessage.setField("pages_enabled", 127); - deviceSettingsMessage.setField("smart_notification_display_orientation", 0); - deviceSettingsMessage.setField("display_steps_goal_enabled", 1); - sendMessage(new FitDataMessage(deviceSettingsMessage).packet); - } - - private boolean foreground; - - @Override - public void onSendWeather(ArrayList weatherSpecs) { - sendWeatherConditions(weatherSpecs.get(0)); - } - - private final Map filesToDownload = new ConcurrentHashMap<>(); - private long totalDownloadSize; - private long lastTransferNotificationTimestamp; - - private GarminFitFile findDownloadedFitFile(DaoSession session, Device device, User user, int fileNumber, int fileDataType, int fileSubType) { - final GarminFitFileDao fileDao = session.getGarminFitFileDao(); - final Query query = fileDao.queryBuilder() - .where( - GarminFitFileDao.Properties.DeviceId.eq(device.getId()), - GarminFitFileDao.Properties.UserId.eq(user.getId()), - GarminFitFileDao.Properties.FileNumber.eq(fileNumber), - GarminFitFileDao.Properties.FileDataType.eq(fileDataType), - GarminFitFileDao.Properties.FileSubType.eq(fileSubType) - ) - .build(); - - final List files = query.list(); - return files.size() > 0 ? files.get(0) : null; - } - - @Override - public void onDirectoryDownloaded(DirectoryData directoryData) { - if (filesToDownload.size() != 0) { - throw new IllegalStateException("File download already in progress!"); - } - - long totalSize = 0; - try { - try (final DBHandler dbHandler = GBApplication.acquireDB()) { - final DaoSession session = dbHandler.getDaoSession(); - final GBDevice gbDevice = getDevice(); - final Device device = DBHelper.getDevice(gbDevice, session); - final User user = DBHelper.getUser(session); - - for (final DirectoryEntry entry : directoryData.entries) { - LOG.info("File #{}: type {}/{} #{}, {}B, flags {}/{}, timestamp {}", entry.fileIndex, entry.fileDataType, entry.fileSubType, entry.fileNumber, entry.fileSize, entry.specificFlags, entry.fileFlags, entry.fileDate); - if (entry.fileIndex == 0) { - // ? - LOG.warn("File #0 reported?"); - continue; - } - - final long timestamp = entry.fileDate.getTime(); - final GarminFitFile alreadyDownloadedFile = findDownloadedFitFile(session, device, user, entry.fileNumber, entry.fileDataType, entry.fileSubType); - if (alreadyDownloadedFile == null) { - LOG.debug("File not yet downloaded"); - } else { - if (alreadyDownloadedFile.getFileTimestamp() == timestamp && alreadyDownloadedFile.getFileSize() == entry.fileSize) { - LOG.debug("File already downloaded, skipping"); - continue; - } else { - LOG.info("File #{} modified after previous download, removing previous version and re-downloading", entry.fileIndex); - alreadyDownloadedFile.delete(); - } - } - - filesToDownload.put(entry.fileIndex, entry); - fileDownloadQueue.addToDownloadQueue(entry.fileIndex, entry.fileSize); - totalSize += entry.fileSize; - } - } - } catch (Exception e) { - LOG.error("Error storing data to DB", e); - } - - totalDownloadSize = totalSize; - } - - @Override - public void onFileDownloadComplete(int fileIndex, byte[] data) { - LOG.info("Downloaded file {}: {} bytes", fileIndex, data.length); - final DirectoryEntry downloadedDirectoryEntry = filesToDownload.get(fileIndex); - if (downloadedDirectoryEntry == null) { - LOG.warn("Unexpected file {} downloaded", fileIndex); - } else { - try (final DBHandler dbHandler = GBApplication.acquireDB()) { - final DaoSession session = dbHandler.getDaoSession(); - - final GBDevice gbDevice = getDevice(); - final Device device = DBHelper.getDevice(gbDevice, session); - final User user = DBHelper.getUser(session); - final int ts = (int) (System.currentTimeMillis() / 1000); - - final GarminFitFile garminFitFile = new GarminFitFile(null, ts, device.getId(), user.getId(), downloadedDirectoryEntry.fileNumber, downloadedDirectoryEntry.fileDataType, downloadedDirectoryEntry.fileSubType, downloadedDirectoryEntry.fileDate.getTime(), downloadedDirectoryEntry.specificFlags, downloadedDirectoryEntry.fileSize, STORE_FIT_FILES ? data : null); - session.getGarminFitFileDao().insert(garminFitFile); - } catch (Exception e) { - LOG.error("Error saving downloaded file to database", e); - } - } - - if (fileIndex <= 0x8000) { - fitImporter.processFitFile(fitParser.parseFitFile(data)); - } else { - LOG.debug("Not importing file {} as FIT", fileIndex); - } - } - - @Override - public void onFileDownloadError(int fileIndex) { - LOG.error("Failed to download file {}", fileIndex); - } - - @Override - public void onDownloadProgress(long remainingBytes) { - LOG.debug("{}B/{} remaining to download", remainingBytes, totalDownloadSize); - if (remainingBytes == 0) { - GB.updateTransferNotification(null, null, false, 100, getContext()); - } else if (totalDownloadSize > 0) { - final long now = System.currentTimeMillis(); - if (now - lastTransferNotificationTimestamp < 1000) { - // do not issue updates too often - return; - } - lastTransferNotificationTimestamp = now; - // TODO: Localization - GB.updateTransferNotification(null, "Downloading data", true, Math.round(100.0f * (totalDownloadSize - remainingBytes) / totalDownloadSize), getContext()); - } - } - - @Override - public void onAllDownloadsCompleted() { - LOG.info("All downloads completed"); - GB.updateTransferNotification(null, null, false, 100, getContext()); - sendMessage(new SystemEventMessage(GarminSystemEventType.SYNC_COMPLETE, 0).packet); - if (fitImporter != null) { - fitImporter.processData(); - GB.signalActivityDataFinish(); - } - fitImporter = null; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntity.java deleted file mode 100644 index e07645908..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntity.java +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ams; - -public enum AmsEntity { - PLAYER, - QUEUE, - TRACK -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntityAttribute.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntityAttribute.java deleted file mode 100644 index d0bed4bce..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ams/AmsEntityAttribute.java +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ams; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; - -import java.nio.charset.StandardCharsets; - -public class AmsEntityAttribute { - public static final int PLAYER_ATTRIBUTE_NAME = 0; - public static final int PLAYER_ATTRIBUTE_PLAYBACK_INFO = 1; - public static final int PLAYER_ATTRIBUTE_VOLUME = 2; - - public static final int QUEUE_ATTRIBUTE_INDEX = 0; - public static final int QUEUE_ATTRIBUTE_COUNT = 1; - public static final int QUEUE_ATTRIBUTE_SHUFFLE_MODE = 2; - public static final int QUEUE_ATTRIBUTE_REPEAT_MODE = 3; - - public static final int TRACK_ATTRIBUTE_ARTIST = 0; - public static final int TRACK_ATTRIBUTE_ALBUM = 1; - public static final int TRACK_ATTRIBUTE_TITLE = 2; - public static final int TRACK_ATTRIBUTE_DURATION = 3; - - public final AmsEntity entity; - public final int attributeID; - public final int updateFlags; - public final byte[] value; - - public AmsEntityAttribute(AmsEntity entity, int attributeID, int updateFlags, String value) { - this.entity = entity; - this.attributeID = attributeID; - this.updateFlags = updateFlags; - this.value = value.getBytes(StandardCharsets.UTF_8); - if (this.value.length > 255) throw new IllegalArgumentException("Too long value"); - } - - public void writeToMessage(MessageWriter writer) { - writer.writeByte(entity.ordinal()); - writer.writeByte(attributeID); - writer.writeByte(updateFlags); - writer.writeByte(value.length); - writer.writeBytes(value); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAction.java deleted file mode 100644 index 48f59aa2e..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAction.java +++ /dev/null @@ -1,22 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public enum AncsAction { - POSITIVE, - NEGATIVE -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAndroidAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAndroidAction.java deleted file mode 100644 index 3c941149b..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAndroidAction.java +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import android.util.SparseArray; - -public enum AncsAndroidAction { - REPLY_TEXT_MESSAGE(94), - REPLY_INCOMING_CALL(95), - ACCEPT_INCOMING_CALL(96), - REJECT_INCOMING_CALL(97), - DISMISS_NOTIFICATION(98), - BLOCK_APPLICATION(99); - - private static final SparseArray valueByCode; - - public final int code; - - AncsAndroidAction(int code) { - this.code = code; - } - - static { - final AncsAndroidAction[] values = values(); - valueByCode = new SparseArray<>(values.length); - for (AncsAndroidAction value : values) { - valueByCode.append(value.code, value); - } - } - - public static AncsAndroidAction getByCode(int code) { - return valueByCode.get(code); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAppAttribute.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAppAttribute.java deleted file mode 100644 index 8ba395305..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAppAttribute.java +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public enum AncsAppAttribute { - DISPLAY_NAME -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttribute.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttribute.java deleted file mode 100644 index 6397533e3..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttribute.java +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import android.util.SparseArray; - -public enum AncsAttribute { - APP_IDENTIFIER(0), - TITLE(1, true), - SUBTITLE(2, true), - MESSAGE(3, true), - MESSAGE_SIZE(4), - DATE(5), - POSITIVE_ACTION_LABEL(6), - NEGATIVE_ACTION_LABEL(7), - // Garmin extensions - PHONE_NUMBER(126, true), - ACTIONS(127, false, true); - - private static final SparseArray valueByCode; - - public final int code; - public final boolean hasLengthParam; - public final boolean hasAdditionalParams; - - AncsAttribute(int code) { - this(code, false, false); - } - - AncsAttribute(int code, boolean hasLengthParam) { - this(code, hasLengthParam, false); - } - - AncsAttribute(int code, boolean hasLengthParam, boolean hasAdditionalParams) { - this.code = code; - this.hasLengthParam = hasLengthParam; - this.hasAdditionalParams = hasAdditionalParams; - } - - static { - final AncsAttribute[] values = values(); - valueByCode = new SparseArray<>(values.length); - for (AncsAttribute value : values) { - valueByCode.append(value.code, value); - } - } - - public static AncsAttribute getByCode(int code) { - return valueByCode.get(code); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttributeRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttributeRequest.java deleted file mode 100644 index 6dc9ba584..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsAttributeRequest.java +++ /dev/null @@ -1,27 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public class AncsAttributeRequest { - public final AncsAttribute attribute; - public final int maxLength; - - public AncsAttributeRequest(AncsAttribute attribute, int maxLength) { - this.attribute = attribute; - this.maxLength = maxLength; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCategory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCategory.java deleted file mode 100644 index db5be8ce5..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCategory.java +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public enum AncsCategory { - OTHER, - INCOMING_CALL, - MISSED_CALL, - VOICEMAIL, - SOCIAL, - SCHEDULE, - EMAIL, - NEWS, - HEALTH_AND_FITNESS, - BUSINESS_AND_FINANCE, - LOCATION, - ENTERTAINMENT, - SMS -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCommand.java deleted file mode 100644 index 8cdbbbfb9..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsCommand.java +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import android.util.SparseArray; - -public enum AncsCommand { - GET_NOTIFICATION_ATTRIBUTES(0), - GET_APP_ATTRIBUTES(1), - PERFORM_NOTIFICATION_ACTION(2), - // Garmin extensions - PERFORM_ANDROID_ACTION(128); - - private static final SparseArray valueByCode; - - public final int code; - - AncsCommand(int code) { - this.code = code; - } - - static { - final AncsCommand[] values = values(); - valueByCode = new SparseArray<>(values.length); - for (AncsCommand value : values) { - valueByCode.append(value.code, value); - } - } - - public static AncsCommand getByCode(int code) { - return valueByCode.get(code); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsControlCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsControlCommand.java deleted file mode 100644 index 50e03ee9f..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsControlCommand.java +++ /dev/null @@ -1,137 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageReader; -import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -public abstract class AncsControlCommand { - private static final Logger LOG = LoggerFactory.getLogger(AncsControlCommand.class); - - private static final AncsAppAttribute[] APP_ATTRIBUTE_VALUES = AncsAppAttribute.values(); - private static final AncsAction[] ACTION_VALUES = AncsAction.values(); - - public final AncsCommand command; - - protected AncsControlCommand(AncsCommand command) { - this.command = command; - } - - public static AncsControlCommand parseCommand(byte[] buffer, int offset, int size) { - final int commandID = BLETypeConversions.toUnsigned(buffer, offset); - final AncsCommand command = AncsCommand.getByCode(commandID); - if (command == null) { - LOG.error("Unknown ANCS command {}", commandID); - return null; - } - switch (command) { - case GET_NOTIFICATION_ATTRIBUTES: - return createGetNotificationAttributesCommand(buffer, offset + 1, size - 1); - case GET_APP_ATTRIBUTES: - return createGetAppAttributesCommand(buffer, offset + 1, size - 1); - case PERFORM_NOTIFICATION_ACTION: - return createPerformNotificationAction(buffer, offset + 1, size - 1); - case PERFORM_ANDROID_ACTION: - return createPerformAndroidAction(buffer, offset + 1, size - 1); - default: - LOG.error("Unknown ANCS command {}", command); - return null; - } - } - - private static AncsPerformAndroidAction createPerformAndroidAction(byte[] buffer, int offset, int size) { - final int notificationUID = BLETypeConversions.toUint32(buffer, offset); - final int actionID = BLETypeConversions.toUnsigned(buffer, offset + 4); - final AncsAndroidAction action = AncsAndroidAction.getByCode(actionID); - if (action == null) { - LOG.error("Unknown ANCS Android action {}", actionID); - return null; - } - int zero = ArrayUtils.indexOf((byte) 0, buffer, offset + 6, size - offset - 6); - if (zero < 0) zero = size; - final String text = new String(buffer, offset + 6, zero - offset - 6); - - return new AncsPerformAndroidAction(notificationUID, action, text); - } - - private static AncsPerformNotificationAction createPerformNotificationAction(byte[] buffer, int offset, int size) { - final MessageReader reader = new MessageReader(buffer, offset); - final int notificationUID = reader.readInt(); - final int actionID = reader.readByte(); - if (actionID < 0 || actionID >= ACTION_VALUES.length) { - LOG.error("Unknown ANCS action {}", actionID); - return null; - } - return new AncsPerformNotificationAction(notificationUID, ACTION_VALUES[actionID]); - } - - private static AncsGetAppAttributesCommand createGetAppAttributesCommand(byte[] buffer, int offset, int size) { - int zero = ArrayUtils.indexOf((byte) 0, buffer, offset, size - offset); - if (zero < 0) zero = size; - final String appIdentifier = new String(buffer, offset, zero - offset, StandardCharsets.UTF_8); - final int attributeCount = size - (zero - offset); - final List requestedAttributes = new ArrayList<>(attributeCount); - for (int i = 0; i < attributeCount; ++i) { - final int attributeID = BLETypeConversions.toUnsigned(buffer, zero + 1 + i); - if (attributeID < 0 || attributeID >= APP_ATTRIBUTE_VALUES.length) { - LOG.error("Unknown ANCS app attribute {}", attributeID); - return null; - } - final AncsAppAttribute attribute = APP_ATTRIBUTE_VALUES[attributeID]; - requestedAttributes.add(attribute); - } - return new AncsGetAppAttributesCommand(appIdentifier, requestedAttributes); - } - - private static AncsGetNotificationAttributeCommand createGetNotificationAttributesCommand(byte[] buffer, int offset, int size) { - final MessageReader reader = new MessageReader(buffer, offset); - final int notificationUID = reader.readInt(); - int pos = 4; - final List attributes = new ArrayList<>(size); - while (pos < size) { - final int attributeID = reader.readByte(); - ++pos; - final AncsAttribute attribute = AncsAttribute.getByCode(attributeID); - if (attribute == null) { - LOG.error("Unknown ANCS attribute {}", attributeID); - return null; - } - final int maxLength; - if (attribute.hasLengthParam) { - maxLength = reader.readShort(); - pos += 2; - } else if (attribute.hasAdditionalParams) { - maxLength = reader.readByte(); - // TODO: What is this?? - reader.readByte(); - reader.readByte(); - pos += 3; - } else { - maxLength = 0; - } - attributes.add(new AncsAttributeRequest(attribute, maxLength)); - } - return new AncsGetNotificationAttributeCommand(notificationUID, attributes); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEvent.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEvent.java deleted file mode 100644 index 86c06569b..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public enum AncsEvent { - NOTIFICATION_ADDED, - NOTIFICATION_MODIFIED, - NOTIFICATION_REMOVED -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEventFlag.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEventFlag.java deleted file mode 100644 index 6775655b5..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsEventFlag.java +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public enum AncsEventFlag { - SILENT, - IMPORTANT, - PRE_EXISTING, - POSITIVE_ACTION, - NEGATIVE_ACTION -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetAppAttributesCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetAppAttributesCommand.java deleted file mode 100644 index 94557365f..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetAppAttributesCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import java.util.List; - -public class AncsGetAppAttributesCommand extends AncsControlCommand { - public final String appIdentifier; - public final List requestedAttributes; - - public AncsGetAppAttributesCommand(String appIdentifier, List requestedAttributes) { - super(AncsCommand.GET_APP_ATTRIBUTES); - this.appIdentifier = appIdentifier; - this.requestedAttributes = requestedAttributes; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributeCommand.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributeCommand.java deleted file mode 100644 index 5558550d9..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributeCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import java.util.List; - -public class AncsGetNotificationAttributeCommand extends AncsControlCommand { - public final int notificationUID; - public final List attributes; - - public AncsGetNotificationAttributeCommand(int notificationUID, List attributes) { - super(AncsCommand.GET_NOTIFICATION_ATTRIBUTES); - this.notificationUID = notificationUID; - this.attributes = attributes; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributesResponse.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributesResponse.java deleted file mode 100644 index a4872b693..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsGetNotificationAttributesResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; - -import java.nio.charset.StandardCharsets; -import java.util.Map; - -public class AncsGetNotificationAttributesResponse { - public final byte[] packet; - - public AncsGetNotificationAttributesResponse(int notificationUID, Map attributes) { - final MessageWriter messageWriter = new MessageWriter(); - messageWriter.writeByte(AncsCommand.GET_NOTIFICATION_ATTRIBUTES.code); - messageWriter.writeInt(notificationUID); - for(Map.Entry attribute : attributes.entrySet()) { - messageWriter.writeByte(attribute.getKey().code); - final byte[] bytes = attribute.getValue().getBytes(StandardCharsets.UTF_8); - messageWriter.writeShort(bytes.length); - messageWriter.writeBytes(bytes); - } - this.packet = messageWriter.getBytes(); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformAndroidAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformAndroidAction.java deleted file mode 100644 index 2db7c8659..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformAndroidAction.java +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public class AncsPerformAndroidAction extends AncsControlCommand { - public final int notificationUID; - public final AncsAndroidAction action; - public final String text; - - public AncsPerformAndroidAction(int notificationUID, AncsAndroidAction action, String text) { - super(AncsCommand.PERFORM_ANDROID_ACTION); - this.notificationUID = notificationUID; - this.action = action; - this.text = text; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformNotificationAction.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformNotificationAction.java deleted file mode 100644 index 316535010..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/AncsPerformNotificationAction.java +++ /dev/null @@ -1,28 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -public class AncsPerformNotificationAction extends AncsControlCommand { - public final int notificationUID; - public final AncsAction action; - - public AncsPerformNotificationAction(int notificationUID, AncsAction action) { - super(AncsCommand.PERFORM_NOTIFICATION_ACTION); - this.notificationUID = notificationUID; - this.action = action; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/GncsDataSourceQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/GncsDataSourceQueue.java deleted file mode 100644 index 1b0aaeaf2..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/ancs/GncsDataSourceQueue.java +++ /dev/null @@ -1,113 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.ancs; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.VivomoveHrCommunicator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsDataSourceMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.GncsDataSourceResponseMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.LinkedList; -import java.util.Queue; - -public class GncsDataSourceQueue { - private static final Logger LOG = LoggerFactory.getLogger(GncsDataSourceQueue.class); - - private final VivomoveHrCommunicator communicator; - private final int maxPacketSize; - private final Queue queue = new LinkedList<>(); - - private byte[] currentPacket; - private int currentDataOffset; - private int lastSentSize; - - public GncsDataSourceQueue(VivomoveHrCommunicator communicator, int maxPacketSize) { - this.communicator = communicator; - this.maxPacketSize = maxPacketSize; - } - - public void addToQueue(byte[] packet) { - queue.add(packet); - checkStartUpload(); - } - - public void responseReceived(GncsDataSourceResponseMessage responseMessage) { - if (currentPacket == null) { - LOG.error("Unexpected GNCS data source response, no current packet"); - return; - } - switch (responseMessage.response) { - case GncsDataSourceResponseMessage.RESPONSE_TRANSFER_SUCCESSFUL: - LOG.debug("Confirmed {}B@{} GNCS transfer", lastSentSize, currentDataOffset); - currentDataOffset += lastSentSize; - if (currentDataOffset >= currentPacket.length) { - LOG.debug("ANCS packet transfer done"); - currentPacket = null; - checkStartUpload(); - } else { - sendNextMessage(); - } - break; - - case GncsDataSourceResponseMessage.RESPONSE_RESEND_LAST_DATA_PACKET: - LOG.info("Received RESEND_LAST_DATA_PACKET GNCS response"); - sendNextMessage(); - break; - - case GncsDataSourceResponseMessage.RESPONSE_ABORT_REQUEST: - LOG.info("Received RESPONSE_ABORT_REQUEST GNCS response"); - currentPacket = null; - checkStartUpload(); - break; - - case GncsDataSourceResponseMessage.RESPONSE_ERROR_CRC_MISMATCH: - case GncsDataSourceResponseMessage.RESPONSE_ERROR_DATA_OFFSET_MISMATCH: - default: - LOG.error("Received {} GNCS response", responseMessage.response); - currentPacket = null; - checkStartUpload(); - break; - } - } - - private void checkStartUpload() { - if (currentPacket != null) { - LOG.debug("Another upload is still running"); - return; - } - if (queue.isEmpty()) { - LOG.debug("Nothing in queue"); - return; - } - startNextUpload(); - } - - private void startNextUpload() { - currentPacket = queue.remove(); - currentDataOffset = 0; - LOG.debug("Sending {}B ANCS data", currentPacket.length); - sendNextMessage(); - } - - private void sendNextMessage() { - final int remainingSize = currentPacket.length - currentDataOffset; - final int availableSize = Math.min(remainingSize, maxPacketSize); - communicator.sendMessage(new GncsDataSourceMessage(currentPacket, currentDataOffset, Math.min(remainingSize, maxPacketSize)).packet); - lastSentSize = availableSize; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryData.java deleted file mode 100644 index bdfea69ba..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryData.java +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.downloads; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminTimeUtils; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageReader; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class DirectoryData { - public final List entries; - - public DirectoryData(List entries) { - this.entries = entries; - } - - public static DirectoryData parse(byte[] bytes) { - int size = bytes.length; - if ((size % 16) != 0) throw new IllegalArgumentException("Invalid directory data length"); - int count = (size - 16) / 16; - final MessageReader reader = new MessageReader(bytes, 16); - final List entries = new ArrayList<>(count); - for (int i = 0; i < count; ++i) { - final int fileIndex = reader.readShort(); - final int fileDataType = reader.readByte(); - final int fileSubType = reader.readByte(); - final int fileNumber = reader.readShort(); - final int specificFlags = reader.readByte(); - final int fileFlags = reader.readByte(); - final int fileSize = reader.readInt(); - final Date fileDate = new Date(GarminTimeUtils.garminTimestampToJavaMillis(reader.readInt())); - - entries.add(new DirectoryEntry(fileIndex, fileDataType, fileSubType, fileNumber, specificFlags, fileFlags, fileSize, fileDate)); - } - - return new DirectoryData(entries); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryEntry.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryEntry.java deleted file mode 100644 index 763304892..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/DirectoryEntry.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.downloads; - -import java.util.Date; - -public class DirectoryEntry { - public final int fileIndex; - public final int fileDataType; - public final int fileSubType; - public final int fileNumber; - public final int specificFlags; - public final int fileFlags; - public final int fileSize; - public final Date fileDate; - - public DirectoryEntry(int fileIndex, int fileDataType, int fileSubType, int fileNumber, int specificFlags, int fileFlags, int fileSize, Date fileDate) { - this.fileIndex = fileIndex; - this.fileDataType = fileDataType; - this.fileSubType = fileSubType; - this.fileNumber = fileNumber; - this.specificFlags = specificFlags; - this.fileFlags = fileFlags; - this.fileSize = fileSize; - this.fileDate = fileDate; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadListener.java deleted file mode 100644 index f64898ec5..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.downloads; - -public interface FileDownloadListener { - void onDirectoryDownloaded(DirectoryData directoryData); - void onFileDownloadComplete(int fileIndex, byte[] data); - void onFileDownloadError(int fileIndex); - void onDownloadProgress(long remainingBytes); - void onAllDownloadsCompleted(); -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadQueue.java deleted file mode 100644 index 22c233ec6..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/downloads/FileDownloadQueue.java +++ /dev/null @@ -1,188 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.downloads; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.VivomoveHrCommunicator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DownloadRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DownloadRequestResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataResponseMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Queue; -import java.util.Set; - -public class FileDownloadQueue { - private static final Logger LOG = LoggerFactory.getLogger(FileDownloadQueue.class); - - private final VivomoveHrCommunicator communicator; - private final FileDownloadListener listener; - - private final Queue queue = new LinkedList<>(); - private final Set queuedFileIndices = new HashSet<>(); - - private QueueItem currentlyDownloadingItem; - private int currentCrc; - private long totalRemainingBytes; - - public FileDownloadQueue(VivomoveHrCommunicator communicator, FileDownloadListener listener) { - this.communicator = communicator; - this.listener = listener; - } - - public void addToDownloadQueue(int fileIndex, int dataSize) { - if (queuedFileIndices.contains(fileIndex)) { - LOG.debug("Ignoring download request of {}, already in queue", fileIndex); - return; - } - queue.add(new QueueItem(fileIndex, dataSize)); - queuedFileIndices.add(fileIndex); - totalRemainingBytes += dataSize; - checkRequestNextDownload(); - } - - public void cancelAllDownloads() { - queue.clear(); - currentlyDownloadingItem = null; - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ABORT_DOWNLOAD_REQUEST, 0).packet); - } - - private boolean checkRequestNextDownload() { - if (currentlyDownloadingItem != null) { - LOG.debug("Another download is pending"); - return false; - } - if (queue.isEmpty()) { - LOG.debug("No download in queue"); - return true; - } - requestNextDownload(); - return false; - } - - private void requestNextDownload() { - currentlyDownloadingItem = queue.remove(); - currentCrc = 0; - final int fileIndex = currentlyDownloadingItem.fileIndex; - LOG.info("Requesting download of {} ({} B)", fileIndex, currentlyDownloadingItem.dataSize); - queuedFileIndices.remove(fileIndex); - communicator.sendMessage(new DownloadRequestMessage(fileIndex, 0, DownloadRequestMessage.REQUEST_NEW_TRANSFER, 0, 0).packet); - } - - public void onDownloadRequestResponse(DownloadRequestResponseMessage responseMessage) { - if (currentlyDownloadingItem == null) { - LOG.error("Download request response arrived, but nothing is being downloaded"); - return; - } - - if (responseMessage.status == VivomoveConstants.STATUS_ACK && responseMessage.response == DownloadRequestResponseMessage.RESPONSE_DOWNLOAD_REQUEST_OKAY) { - LOG.info("Received response for download request of {}: {}/{}, {}B", currentlyDownloadingItem.fileIndex, responseMessage.status, responseMessage.response, responseMessage.fileSize); - totalRemainingBytes += responseMessage.fileSize - currentlyDownloadingItem.dataSize; - currentlyDownloadingItem.setDataSize(responseMessage.fileSize); - } else { - LOG.error("Received error response for download request of {}: {}/{}", currentlyDownloadingItem.fileIndex, responseMessage.status, responseMessage.response); - listener.onFileDownloadError(currentlyDownloadingItem.fileIndex); - totalRemainingBytes -= currentlyDownloadingItem.dataSize; - currentlyDownloadingItem = null; - checkRequestNextDownload(); - } - } - - public void onFileTransferData(FileTransferDataMessage dataMessage) { - final QueueItem currentlyDownloadingItem = this.currentlyDownloadingItem; - if (currentlyDownloadingItem == null) { - LOG.error("Download request response arrived, but nothing is being downloaded"); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ABORT_DOWNLOAD_REQUEST, 0).packet); - return; - } - - if (dataMessage.dataOffset < currentlyDownloadingItem.dataOffset) { - LOG.warn("Ignoring repeated transfer at offset {} of #{}", dataMessage.dataOffset, currentlyDownloadingItem.fileIndex); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ERROR_DATA_OFFSET_MISMATCH, currentlyDownloadingItem.dataOffset).packet); - return; - } - if (dataMessage.dataOffset > currentlyDownloadingItem.dataOffset) { - LOG.warn("Missing data at offset {} when received data at offset {} of #{}", currentlyDownloadingItem.dataOffset, dataMessage.dataOffset, currentlyDownloadingItem.fileIndex); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ERROR_DATA_OFFSET_MISMATCH, currentlyDownloadingItem.dataOffset).packet); - return; - } - - final int dataCrc = ChecksumCalculator.computeCrc(currentCrc, dataMessage.data, 0, dataMessage.data.length); - if (dataCrc != dataMessage.crc) { - LOG.warn("Invalid CRC ({} vs {}) for {}B data @{} of {}", dataCrc, dataMessage.crc, dataMessage.data.length, dataMessage.dataOffset, currentlyDownloadingItem.fileIndex); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ERROR_CRC_MISMATCH, currentlyDownloadingItem.dataOffset).packet); - return; - } - currentCrc = dataCrc; - - LOG.info("Received {}B@{}/{} of {}", dataMessage.data.length, dataMessage.dataOffset, currentlyDownloadingItem.dataSize, currentlyDownloadingItem.fileIndex); - currentlyDownloadingItem.appendData(dataMessage.data); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_TRANSFER_SUCCESSFUL, currentlyDownloadingItem.dataOffset).packet); - - totalRemainingBytes -= dataMessage.data.length; - listener.onDownloadProgress(totalRemainingBytes); - - if (currentlyDownloadingItem.dataOffset >= currentlyDownloadingItem.dataSize) { - LOG.info("Transfer of file #{} complete, {}/{}B downloaded", currentlyDownloadingItem.fileIndex, currentlyDownloadingItem.dataOffset, currentlyDownloadingItem.dataSize); - this.currentlyDownloadingItem = null; - final boolean allDone = checkRequestNextDownload(); - reportCompletedDownload(currentlyDownloadingItem); - if (allDone && isIdle()) listener.onAllDownloadsCompleted(); - } - } - - private boolean isIdle() { - return currentlyDownloadingItem == null; - } - - private void reportCompletedDownload(QueueItem downloadedItem) { - if (downloadedItem.fileIndex == 0) { - final DirectoryData directoryData = DirectoryData.parse(downloadedItem.data); - listener.onDirectoryDownloaded(directoryData); - } else { - listener.onFileDownloadComplete(downloadedItem.fileIndex, downloadedItem.data); - } - } - - private static class QueueItem { - public final int fileIndex; - public int dataSize; - public int dataOffset; - public byte[] data; - - public QueueItem(int fileIndex, int dataSize) { - this.fileIndex = fileIndex; - this.dataSize = dataSize; - } - - public void setDataSize(int dataSize) { - if (this.data != null) throw new IllegalStateException("Data size already set"); - this.dataSize = dataSize; - this.data = new byte[dataSize]; - } - - public void appendData(byte[] data) { - System.arraycopy(data, 0, this.data, dataOffset, data.length); - dataOffset += data.length; - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitBool.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitBool.java deleted file mode 100644 index 4b3e3c6ba..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitBool.java +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -public final class FitBool { - public static final int FALSE = 0; - public static final int TRUE = 1; - public static final int INVALID = 255; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitDbImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitDbImporter.java deleted file mode 100644 index 1d815a660..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitDbImporter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; -import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveHrSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; -import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.User; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -public class FitDbImporter { - private static final Logger LOG = LoggerFactory.getLogger(FitDbImporter.class); - - private final GBDevice gbDevice; - private final FitImporter fitImporter; - - public FitDbImporter(GBDevice gbDevice) { - this.gbDevice = gbDevice; - fitImporter = new FitImporter(); - } - - public void processFitFile(List messages) { - try { - fitImporter.importFitData(messages); - } catch (Exception e) { - LOG.error("Error importing FIT data", e); - } - } - - public void processData() { - try (DBHandler dbHandler = GBApplication.acquireDB()) { - final DaoSession session = dbHandler.getDaoSession(); - - final Device device = DBHelper.getDevice(gbDevice, session); - final User user = DBHelper.getUser(session); - final VivomoveHrSampleProvider provider = new VivomoveHrSampleProvider(gbDevice, session); - - fitImporter.processImportedData(sample -> { - sample.setDevice(device); - sample.setUser(user); - sample.setProvider(provider); - - provider.addGBActivitySample(sample); - }); - } catch (Exception e) { - LOG.error("Error importing FIT data", e); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitFieldBaseType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitFieldBaseType.java deleted file mode 100644 index 8fdad0071..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitFieldBaseType.java +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseArray; - -public enum FitFieldBaseType { - ENUM(0, 1, 0xFF), - SINT8(1, 1, 0x7F), - UINT8(2, 1, 0xFF), - SINT16(3, 2, 0x7FFF), - UINT16(4, 2, 0xFFFF), - SINT32(5, 4, 0x7FFFFFFF), - UINT32(6, 4, 0xFFFFFFFF), - STRING(7, 1, ""), - FLOAT32(8, 4, 0xFFFFFFFF), - FLOAT64(9, 8, 0xFFFFFFFFFFFFFFFFL), - UINT8Z(10, 1, 0), - UINT16Z(11, 2, 0), - UINT32Z(12, 4, 0), - BYTE(13, 1, 0xFF), - SINT64(14, 8, 0x7FFFFFFFFFFFFFFFL), - UINT64(15, 8, 0xFFFFFFFFFFFFFFFFL), - UINT64Z(16, 8, 0); - - public final int typeNumber; - public final int size; - public final int typeID; - public final Object invalidValue; - - private static final SparseArray typeForCode = new SparseArray<>(values().length); - private static final SparseArray typeForID = new SparseArray<>(values().length); - - static { - for (FitFieldBaseType value : values()) { - typeForCode.append(value.typeNumber, value); - typeForID.append(value.typeID, value); - } - } - - FitFieldBaseType(int typeNumber, int size, Object invalidValue) { - this.typeNumber = typeNumber; - this.size = size; - this.invalidValue = invalidValue; - this.typeID = size > 1 ? (typeNumber | 0x80) : typeNumber; - } - - public static FitFieldBaseType decodeTypeID(int typeNumber) { - final FitFieldBaseType type = typeForID.get(typeNumber); - if (type == null) { - throw new IllegalArgumentException("Unknown type " + typeNumber); - } - return type; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImportProcessor.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImportProcessor.java deleted file mode 100644 index 2558afcab..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImportProcessor.java +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import nodomain.freeyourgadget.gadgetbridge.entities.VivomoveHrActivitySample; - -interface FitImportProcessor { - void onSample(VivomoveHrActivitySample sample); -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImporter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImporter.java deleted file mode 100644 index b4fd75448..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitImporter.java +++ /dev/null @@ -1,288 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseIntArray; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveHrSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.entities.VivomoveHrActivitySample; -import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminTimeUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.TreeMap; - -public class FitImporter { - private static final int ACTIVITY_TYPE_ALL = -1; - private final SortedMap> eventsPerTimestamp = new TreeMap<>(); - - public void importFitData(List messages) { - boolean ohrEnabled = false; - int softwareVersion = -1; - - int lastTimestamp = 0; - final SparseIntArray lastCycles = new SparseIntArray(); - - for (FitMessage message : messages) { - switch (message.definition.globalMessageID) { - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_EVENT: - //message.getField(); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_SOFTWARE: - final Integer versionField = message.getIntegerField("version"); - if (versionField != null) softwareVersion = versionField; - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_MONITORING_INFO: - lastTimestamp = message.getIntegerField("timestamp"); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_MONITORING: - lastTimestamp = processMonitoringMessage(message, ohrEnabled, lastTimestamp, lastCycles); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_OHR_SETTINGS: - final Boolean isOhrEnabled = message.getBooleanField("enabled"); - if (isOhrEnabled != null) ohrEnabled = isOhrEnabled; - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_SLEEP_LEVEL: - processSleepLevelMessage(message); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_MONITORING_HR_DATA: - processHrDataMessage(message); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_STRESS_LEVEL: - processStressLevelMessage(message); - break; - - case FitMessageDefinitions.FIT_MESSAGE_NUMBER_MAX_MET_DATA: - processMaxMetDataMessage(message); - break; - } - } - } - - public void processImportedData(FitImportProcessor processor) { - for (final Map.Entry> eventsForTimestamp : eventsPerTimestamp.entrySet()) { - final VivomoveHrActivitySample sample = new VivomoveHrActivitySample(); - sample.setTimestamp(eventsForTimestamp.getKey()); - - sample.setRawKind(ActivitySample.NOT_MEASURED); - sample.setCaloriesBurnt(ActivitySample.NOT_MEASURED); - sample.setSteps(ActivitySample.NOT_MEASURED); - sample.setHeartRate(ActivitySample.NOT_MEASURED); - sample.setFloorsClimbed(ActivitySample.NOT_MEASURED); - sample.setRawIntensity(ActivitySample.NOT_MEASURED); - - FitEvent.EventKind bestKind = FitEvent.EventKind.UNKNOWN; - float bestScore = Float.NEGATIVE_INFINITY; - for (final FitEvent event : eventsForTimestamp.getValue()) { - if (event.getHeartRate() > sample.getHeartRate()) { - sample.setHeartRate(event.getHeartRate()); - } - if (event.getFloorsClimbed() > sample.getFloorsClimbed()) { - sample.setFloorsClimbed(event.getFloorsClimbed()); - } - - float score = 0; - if (event.getRawKind() > 0) score += 1; - if (event.getCaloriesBurnt() > 0) score += event.getCaloriesBurnt() * 10.0f; - if (event.getSteps() > 0) score += event.getSteps(); - //if (event.getRawIntensity() > 0) score += 10.0f * event.getRawIntensity(); - if (event.getKind().isBetterThan(bestKind) || (event.getKind() == bestKind && score > bestScore)) { -// if (bestScore > Float.NEGATIVE_INFINITY && event.getKind() != FitEvent.EventKind.NOT_WORN) { -// System.out.println(String.format(Locale.ROOT, "Replacing %s %d (%d cal, %d steps) with %s %d (%d cal, %d steps)", sample.getRawKind(), sample.getRawIntensity(), sample.getCaloriesBurnt(), sample.getSteps(), event.getRawKind(), event.getRawIntensity(), event.getCaloriesBurnt(), event.getSteps())); -// } - bestScore = score; - bestKind = event.getKind(); - sample.setRawKind(event.getRawKind()); - sample.setCaloriesBurnt(event.getCaloriesBurnt()); - sample.setSteps(event.getSteps()); - sample.setRawIntensity(event.getRawIntensity()); - } - } - - if (sample.getHeartRate() == ActivitySample.NOT_MEASURED && ((sample.getRawKind() & VivomoveHrSampleProvider.RAW_TYPE_KIND_SLEEP) != 0)) { - sample.setRawKind(VivomoveHrSampleProvider.RAW_NOT_WORN); - sample.setRawIntensity(0); - } - - processor.onSample(sample); - } - } - - private void processSleepLevelMessage(FitMessage message) { - final Integer timestampFull = message.getIntegerField("timestamp"); - final Integer sleepLevel = message.getIntegerField("sleep_level"); - - final int timestamp = GarminTimeUtils.garminTimestampToUnixTime(timestampFull); - final int rawIntensity = (4 - sleepLevel) * 40; - final int rawKind = VivomoveHrSampleProvider.RAW_TYPE_KIND_SLEEP | sleepLevel; - - addEvent(new FitEvent(timestamp, FitEvent.EventKind.SLEEP, rawKind, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, rawIntensity)); - } - - private int processMonitoringMessage(FitMessage message, boolean ohrEnabled, int lastTimestamp, SparseIntArray lastCycles) { - final Integer activityType = message.getIntegerField("activity_type"); - final Double activeCalories = message.getNumericField("active_calories"); - final Integer intensity = message.getIntegerField("current_activity_type_intensity"); - final Integer cycles = message.getIntegerField("cycles"); - final Double heartRateMeasured = message.getNumericField("heart_rate"); - final Integer timestampFull = message.getIntegerField("timestamp"); - final Integer timestamp16 = message.getIntegerField("timestamp_16"); - final Double activeTime = message.getNumericField("active_time"); - - final int activityTypeOrAll = activityType == null ? ACTIVITY_TYPE_ALL : activityType; - final int activityTypeOrDefault = activityType == null ? 0 : activityType; - - final int lastDefaultCycleCount = lastCycles.get(ACTIVITY_TYPE_ALL); - final int lastCycleCount = Math.max(lastCycles.get(activityTypeOrAll), lastDefaultCycleCount); - final Integer currentCycles = cycles == null ? null : cycles < lastCycleCount ? cycles : cycles - lastCycleCount; - if (currentCycles != null) { - lastCycles.put(activityTypeOrDefault, cycles); - final int newAllCycles = Math.max(lastDefaultCycleCount, cycles); - if (newAllCycles != lastDefaultCycleCount) { - assert newAllCycles > lastDefaultCycleCount; - lastCycles.put(ACTIVITY_TYPE_ALL, newAllCycles); - } - } - - if (timestampFull != null) { - lastTimestamp = timestampFull; - } else if (timestamp16 != null) { - lastTimestamp += (timestamp16 - (lastTimestamp & 0xFFFF)) & 0xFFFF; - } else { - // TODO: timestamp_min_8 - throw new IllegalArgumentException("Unsupported timestamp"); - } - - final int timestamp = GarminTimeUtils.garminTimestampToUnixTime(lastTimestamp); - final int rawKind, caloriesBurnt, floorsClimbed, heartRate, steps, rawIntensity; - final FitEvent.EventKind eventKind; - - caloriesBurnt = activeCalories == null ? ActivitySample.NOT_MEASURED : (int) Math.round(activeCalories); - floorsClimbed = ActivitySample.NOT_MEASURED; - heartRate = ohrEnabled && heartRateMeasured != null && heartRateMeasured > 0 ? (int) Math.round(heartRateMeasured) : ActivitySample.NOT_MEASURED; - steps = currentCycles == null ? ActivitySample.NOT_MEASURED : currentCycles; - rawIntensity = intensity == null ? 0 : intensity; - rawKind = VivomoveHrSampleProvider.RAW_TYPE_KIND_ACTIVITY | activityTypeOrDefault; - eventKind = steps != ActivitySample.NOT_MEASURED || rawIntensity > 0 || activityTypeOrDefault > 0 ? FitEvent.EventKind.ACTIVITY : FitEvent.EventKind.WORN; - - if (rawKind != ActivitySample.NOT_MEASURED - || caloriesBurnt != ActivitySample.NOT_MEASURED - || floorsClimbed != ActivitySample.NOT_MEASURED - || heartRate != ActivitySample.NOT_MEASURED - || steps != ActivitySample.NOT_MEASURED - || rawIntensity != ActivitySample.NOT_MEASURED) { - - addEvent(new FitEvent(timestamp, eventKind, rawKind, caloriesBurnt, floorsClimbed, heartRate, steps, rawIntensity)); - } else { - addEvent(new FitEvent(timestamp, FitEvent.EventKind.NOT_WORN, VivomoveHrSampleProvider.RAW_NOT_WORN, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED, ActivitySample.NOT_MEASURED)); - } - return lastTimestamp; - } - - private void processHrDataMessage(FitMessage message) { - } - - private void processStressLevelMessage(FitMessage message) { - } - - private void processMaxMetDataMessage(FitMessage message) { - } - - private void addEvent(FitEvent event) { - List eventsForTimestamp = eventsPerTimestamp.get(event.getTimestamp()); - if (eventsForTimestamp == null) { - eventsForTimestamp = new ArrayList<>(); - eventsPerTimestamp.put(event.getTimestamp(), eventsForTimestamp); - } - eventsForTimestamp.add(event); - } - - private static class FitEvent { - private final int timestamp; - private final EventKind kind; - private final int rawKind; - private final int caloriesBurnt; - private final int floorsClimbed; - private final int heartRate; - private final int steps; - private final int rawIntensity; - - private FitEvent(int timestamp, EventKind kind, int rawKind, int caloriesBurnt, int floorsClimbed, int heartRate, int steps, int rawIntensity) { - this.timestamp = timestamp; - this.kind = kind; - this.rawKind = rawKind; - this.caloriesBurnt = caloriesBurnt; - this.floorsClimbed = floorsClimbed; - this.heartRate = heartRate; - this.steps = steps; - this.rawIntensity = rawIntensity; - } - - public int getTimestamp() { - return timestamp; - } - - public EventKind getKind() { - return kind; - } - - public int getRawKind() { - return rawKind; - } - - public int getCaloriesBurnt() { - return caloriesBurnt; - } - - public int getFloorsClimbed() { - return floorsClimbed; - } - - public int getHeartRate() { - return heartRate; - } - - public int getSteps() { - return steps; - } - - public int getRawIntensity() { - return rawIntensity; - } - - public enum EventKind { - UNKNOWN, - NOT_WORN, - WORN, - SLEEP, - ACTIVITY; - - public boolean isBetterThan(EventKind other) { - return ordinal() > other.ordinal(); - } - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalFieldDefinition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalFieldDefinition.java deleted file mode 100644 index e4d34d876..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalFieldDefinition.java +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -class FitLocalFieldDefinition { - public final FitMessageFieldDefinition globalDefinition; - public final int size; - public final FitFieldBaseType baseType; - - FitLocalFieldDefinition(FitMessageFieldDefinition globalDefinition, int size, FitFieldBaseType baseType) { - this.globalDefinition = globalDefinition; - this.size = size; - this.baseType = baseType; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalMessageDefinition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalMessageDefinition.java deleted file mode 100644 index c51c47bee..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitLocalMessageDefinition.java +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import java.util.List; - -class FitLocalMessageDefinition { - public final FitMessageDefinition globalDefinition; - public final List fieldDefinitions; - - FitLocalMessageDefinition(FitMessageDefinition globalDefinition, List fieldDefinitions) { - this.globalDefinition = globalDefinition; - this.fieldDefinitions = fieldDefinitions; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessage.java deleted file mode 100644 index b30a0b6b6..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessage.java +++ /dev/null @@ -1,181 +0,0 @@ -/* Copyright (C) 2023-2024 Daniele Gobbetti, Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseArray; -import androidx.annotation.NonNull; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; - -import java.lang.reflect.Array; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -public class FitMessage { - public final FitMessageDefinition definition; - private final SparseArray fieldValues = new SparseArray<>(); - private final Map fieldValuesPerName = new HashMap<>(); - - public FitMessage(FitMessageDefinition definition) { - this.definition = definition; - } - - public void setField(int fieldNumber, Object value) { - // TODO: Support arrays - fieldValues.append(fieldNumber, value); - final FitMessageFieldDefinition fieldDefinition = definition.getField(fieldNumber); - fieldValuesPerName.put(fieldDefinition.fieldName, value); - } - - public void setField(String fieldName, Object value) { - final FitMessageFieldDefinition fieldDefinition = definition.findField(fieldName); - if (fieldDefinition == null) throw new IllegalArgumentException("Unknown field name " + fieldName); - setField(fieldDefinition.fieldNumber, value); - } - - public Object getField(int fieldNumber) { - return fieldValues.get(fieldNumber); - } - - public Object getField(String fieldName) { - return fieldValuesPerName.get(fieldName); - } - - public String getStringField(String fieldName) { - return (String) getField(fieldName); - } - - public Integer getIntegerField(String fieldName) { - return (Integer) getField(fieldName); - } - - public Double getNumericField(String fieldName) { - return (Double) getField(fieldName); - } - - public Boolean getBooleanField(String fieldName) { - final Integer value = (Integer) getField(fieldName); - if (value == null) return null; - int v = value; - return v == FitBool.INVALID ? null : (v != 0); - } - - public boolean isBooleanFieldTrue(String fieldName) { - final Boolean value = getBooleanField(fieldName); - return value != null && value; - } - - public void writeToMessage(MessageWriter writer) { - writer.writeByte(definition.localMessageID); - for (FitMessageFieldDefinition fieldDefinition : definition.fieldDefinitions) { - final Object value = fieldValues.get(fieldDefinition.fieldNumber, fieldDefinition.defaultValue); - writeFitValueToMessage(writer, value, fieldDefinition.fieldType, fieldDefinition.fieldSize); - } - } - - private static void writeFitValueToMessage(MessageWriter writer, Object value, FitFieldBaseType type, int size) { - switch (type) { - case ENUM: - case SINT8: - case UINT8: - case SINT16: - case UINT16: - case SINT32: - case UINT32: - case UINT8Z: - case UINT16Z: - case UINT32Z: - case BYTE: - writeFitNumberToMessage(writer, ((Number) value).intValue(), size); - break; - case SINT64: - case UINT64: - case UINT64Z: - writeFitNumberToMessage(writer, ((Number) value).longValue(), size); - break; - case STRING: - writeFitStringToMessage(writer, (String) value, size); - break; - // TODO: Float data types - default: - throw new IllegalArgumentException("Unable to write value of type " + type); - } - } - - private static void writeFitStringToMessage(MessageWriter writer, String value, int size) { - final byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - int valueSize = Math.min(bytes.length, size - 1); - writer.writeBytes(bytes, 0, valueSize); - for (int i = valueSize; i < size; ++i) { - writer.writeByte(0); - } - } - - private static void writeFitNumberToMessage(MessageWriter writer, long value, int size) { - switch (size) { - case 1: - writer.writeByte((int) value); - break; - case 2: - writer.writeShort((int) value); - break; - case 4: - writer.writeInt((int) value); - break; - case 8: - writer.writeLong(value); - break; - default: - throw new IllegalArgumentException("Unable to write number of size " + size); - } - } - - @Override - @NonNull - public String toString() { - final StringBuilder result = new StringBuilder(); - result.append(this.definition.messageName); - result.append(System.lineSeparator()); - for (Map.Entry field : fieldValuesPerName.entrySet()) { - result.append('\t'); - result.append(field.getKey()); - result.append(": "); - result.append(valueToString(field.getValue())); - result.append(System.lineSeparator()); - } - return result.toString(); - } - - @NonNull - private static String valueToString(Object value) { - if (value == null) return "null"; - final Class clazz = value.getClass(); - if (clazz.isArray()) { - final StringBuilder result = new StringBuilder(); - result.append('['); - final int length = Array.getLength(value); - for (int i = 0; i < length; ++i) { - if (i > 0) result.append(", "); - result.append(valueToString(Array.get(value, i))); - } - result.append(']'); - return result.toString(); - } else { - return String.valueOf(value); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinition.java deleted file mode 100644 index a3b52377d..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinition.java +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseArray; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; - -import java.util.Arrays; -import java.util.List; - -public class FitMessageDefinition { - public final String messageName; - public final int globalMessageID; - public final int localMessageID; - public final List fieldDefinitions; - public final SparseArray fieldsPerNumber; - - public FitMessageDefinition(String messageName, int globalMessageID, int localMessageID, FitMessageFieldDefinition... fieldDefinitions) { - this.messageName = messageName; - this.globalMessageID = globalMessageID; - this.localMessageID = localMessageID; - this.fieldDefinitions = Arrays.asList(fieldDefinitions); - fieldsPerNumber = new SparseArray<>(fieldDefinitions.length); - for (FitMessageFieldDefinition fieldDefinition : fieldDefinitions) { - addField(fieldDefinition); - } - } - - public FitMessageFieldDefinition getField(int fieldNumber) { - return fieldsPerNumber.get(fieldNumber); - } - - public void writeToMessage(MessageWriter writer) { - writer.writeByte(localMessageID | 0x40); - writer.writeByte(0); - writer.writeByte(0); - writer.writeShort(globalMessageID); - writer.writeByte(fieldDefinitions.size()); - for (FitMessageFieldDefinition fieldDefinition : fieldDefinitions) { - fieldDefinition.writeToMessage(writer); - } - } - - public void addField(FitMessageFieldDefinition fieldDefinition) { - if (fieldsPerNumber.get(fieldDefinition.fieldNumber) != null) { - throw new IllegalArgumentException("Duplicate field number " + fieldDefinition.fieldNumber + " in message " + globalMessageID); - } - fieldsPerNumber.append(fieldDefinition.fieldNumber, fieldDefinition); - } - - public FitMessageFieldDefinition findField(String fieldName) { - for (final FitMessageFieldDefinition fieldDefinition : fieldDefinitions) { - if (fieldName.equals(fieldDefinition.fieldName)) return fieldDefinition; - } - return null; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinitions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinitions.java deleted file mode 100644 index 8b33b7765..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageDefinitions.java +++ /dev/null @@ -1,662 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import java.util.Arrays; -import java.util.List; - -public class FitMessageDefinitions { - public static final int MESSAGE_ID_CONNECTIVITY = 0; - public static final int MESSAGE_ID_WEATHER_ALERT = 5; - public static final int MESSAGE_ID_WEATHER_CONDITIONS = 6; - public static final int MESSAGE_ID_DEVICE_SETTINGS = 7; - - public static final String FIT_MESSAGE_NAME_FILE_ID = "file_id"; - public static final String FIT_MESSAGE_NAME_CAPABILITIES = "capabilities"; - public static final String FIT_MESSAGE_NAME_DEVICE_SETTINGS = "device_settings"; - public static final String FIT_MESSAGE_NAME_USER_PROFILE = "user_profile"; - public static final String FIT_MESSAGE_NAME_EVENT = "event"; - public static final String FIT_MESSAGE_NAME_DEVICE_INFO = "device_info"; - public static final String FIT_MESSAGE_NAME_DEBUG = "debug"; - public static final String FIT_MESSAGE_NAME_SOFTWARE = "software"; - public static final String FIT_MESSAGE_NAME_FILE_CAPABILITIES = "file_capabilities"; - public static final String FIT_MESSAGE_NAME_FILE_CREATOR = "file_creator"; - public static final String FIT_MESSAGE_NAME_MONITORING = "monitoring"; - public static final String FIT_MESSAGE_NAME_MONITORING_INFO = "monitoring_info"; - public static final String FIT_MESSAGE_NAME_CONNECTIVITY = "connectivity"; - public static final String FIT_MESSAGE_NAME_WEATHER_CONDITIONS = "weather_conditions"; - public static final String FIT_MESSAGE_NAME_WEATHER_ALERT = "weather_alert"; - public static final String FIT_MESSAGE_NAME_FILE_DESCRIPTION = "file_description"; - public static final String FIT_MESSAGE_NAME_OHR_SETTINGS = "ohr_settings"; - public static final String FIT_MESSAGE_NAME_EXD_SCREEN_CONFIGURATION = "exd_screen_configuration"; - public static final String FIT_MESSAGE_NAME_EXD_DATA_FIELD_CONFIGURATION = "exd_data_field_configuration"; - public static final String FIT_MESSAGE_NAME_EXD_DATA_CONCEPT_CONFIGURATION = "exd_data_concept_configuration"; - public static final String FIT_MESSAGE_NAME_MONITORING_HR_DATA = "monitoring_hr_data"; - public static final String FIT_MESSAGE_NAME_ALARM_SETTINGS = "alarm_settings"; - public static final String FIT_MESSAGE_NAME_STRESS_LEVEL = "stress_level"; - public static final String FIT_MESSAGE_NAME_MANUAL_STRESS_LEVEL = "manual_stress_level"; - public static final String FIT_MESSAGE_NAME_MAX_MET_DATA = "max_met_data"; - public static final String FIT_MESSAGE_NAME_WHR_DIAG = "whr_diag"; - public static final String FIT_MESSAGE_NAME_METRICS_INFO = "metrics_info"; - public static final String FIT_MESSAGE_NAME_PAGES_MAP = "pages_map"; - public static final String FIT_MESSAGE_NAME_NEURAL_NETWORK_INFO = "neural_network_info"; - public static final String FIT_MESSAGE_NAME_NEURAL_NETWORK_DATA = "neural_network_data"; - public static final String FIT_MESSAGE_NAME_SLEEP_LEVEL = "sleep_level"; - public static final String FIT_MESSAGE_NAME_END_OF_FILE = "end_of_file"; - - public static final int FIT_MESSAGE_NUMBER_FILE_ID = 0; - public static final int FIT_MESSAGE_NUMBER_CAPABILITIES = 1; - public static final int FIT_MESSAGE_NUMBER_DEVICE_SETTINGS = 2; - public static final int FIT_MESSAGE_NUMBER_USER_PROFILE = 3; - public static final int FIT_MESSAGE_NUMBER_EVENT = 21; - public static final int FIT_MESSAGE_NUMBER_DEVICE_INFO = 23; - public static final int FIT_MESSAGE_NUMBER_DEBUG = 24; - public static final int FIT_MESSAGE_NUMBER_SOFTWARE = 35; - public static final int FIT_MESSAGE_NUMBER_FILE_CAPABILITIES = 37; - public static final int FIT_MESSAGE_NUMBER_FILE_CREATOR = 49; - public static final int FIT_MESSAGE_NUMBER_MONITORING = 55; - public static final int FIT_MESSAGE_NUMBER_MONITORING_INFO = 103; - public static final int FIT_MESSAGE_NUMBER_CONNECTIVITY = 127; - public static final int FIT_MESSAGE_NUMBER_WEATHER_CONDITIONS = 128; - public static final int FIT_MESSAGE_NUMBER_WEATHER_ALERT = 129; - public static final int FIT_MESSAGE_NUMBER_FILE_DESCRIPTION = 138; - public static final int FIT_MESSAGE_NUMBER_OHR_SETTINGS = 188; - public static final int FIT_MESSAGE_NUMBER_EXD_SCREEN_CONFIGURATION = 200; - public static final int FIT_MESSAGE_NUMBER_EXD_DATA_FIELD_CONFIGURATION = 201; - public static final int FIT_MESSAGE_NUMBER_EXD_DATA_CONCEPT_CONFIGURATION = 202; - public static final int FIT_MESSAGE_NUMBER_MONITORING_HR_DATA = 211; - public static final int FIT_MESSAGE_NUMBER_ALARM_SETTINGS = 222; - public static final int FIT_MESSAGE_NUMBER_STRESS_LEVEL = 227; - public static final int FIT_MESSAGE_NUMBER_MANUAL_STRESS_LEVEL = 228; - public static final int FIT_MESSAGE_NUMBER_MAX_MET_DATA = 229; - public static final int FIT_MESSAGE_NUMBER_WHR_DIAG = 233; - public static final int FIT_MESSAGE_NUMBER_METRICS_INFO = 241; - public static final int FIT_MESSAGE_NUMBER_PAGES_MAP = 254; - public static final int FIT_MESSAGE_NUMBER_NEURAL_NETWORK_INFO = 273; - public static final int FIT_MESSAGE_NUMBER_NEURAL_NETWORK_DATA = 274; - public static final int FIT_MESSAGE_NUMBER_SLEEP_LEVEL = 275; - public static final int FIT_MESSAGE_NUMBER_END_OF_FILE = 276; - - public static final FitMessageDefinition DEFINITION_FILE_ID = new FitMessageDefinition(FIT_MESSAGE_NAME_FILE_ID, FIT_MESSAGE_NUMBER_FILE_ID, -1, - new FitMessageFieldDefinition("type", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("manufacturer", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("product", 2, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("serial_number", 3, 4, FitFieldBaseType.UINT32Z, null), - new FitMessageFieldDefinition("time_created", 4, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("number", 5, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("manufacturer_partner", 6, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("product_name", 8, 20, FitFieldBaseType.STRING, null) - ); - - public static final FitMessageDefinition DEFINITION_CAPABILITIES = new FitMessageDefinition(FIT_MESSAGE_NAME_CAPABILITIES, FIT_MESSAGE_NUMBER_CAPABILITIES, -1, - new FitMessageFieldDefinition("languages", 0, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("sports", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("workouts_supported", 21, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("activity_profile_supported", 22, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("connectivity_supported", 23, 4, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("wifi_supported", 24, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("segments_supported", 25, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("audio_prompts_supported", 26, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_DEVICE_SETTINGS = new FitMessageDefinition(FIT_MESSAGE_NAME_DEVICE_SETTINGS, FIT_MESSAGE_NUMBER_DEVICE_SETTINGS, MESSAGE_ID_DEVICE_SETTINGS, - new FitMessageFieldDefinition("active_time_zone", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("utc_offset", 1, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("time_offset", 2, 4, FitFieldBaseType.UINT32, 1, 0, "s", null), - new FitMessageFieldDefinition("time_daylight_savings", 3, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("time_mode", 4, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("time_zone_offset", 5, 1, FitFieldBaseType.SINT8, 1, 0, "hr", null), - new FitMessageFieldDefinition("alarm_time", 8, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("alarm_mode", 9, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("key_tones_enabled", 10, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("message_tones_enabled", 11, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_mode", 12, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_timeout", 13, 1, FitFieldBaseType.UINT8, 1, 0, "s", null), - new FitMessageFieldDefinition("backlight_brightness", 14, 1, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("display_contrast", 15, 1, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("computer_beacon", 16, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("computer_pairing", 17, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("fitness_equipment_pairing", 18, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("bezel_sensitivity", 19, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("gps_enabled", 21, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("weight_scale_enabled", 22, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_orientation", 23, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("map_show", 24, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_show_locations", 25, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("time_zone", 26, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_shutdown", 27, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("alarm_tone", 28, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("data_storage", 29, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("map_auto_zoom", 30, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_guidance", 31, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("current_map_profile", 32, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("current_routing_profile", 33, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("display_mode", 34, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("first_day_of_week", 35, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("activity_tracker_enabled", 36, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("sleep_enabled", 37, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("wifi_auto_upload_enabled", 38, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("clock_time", 39, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("pages_enabled", 40, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("recovery_advisor_enabled", 41, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("auto_max_hr_enabled", 42, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("clock_profile_color_enabled", 43, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("clock_background_inverted", 44, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("auto_goal_enabled", 45, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("move_alert_enabled", 46, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("date_mode", 47, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("data_recording_interval", 48, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("data_recording_value", 49, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("vivohub_settings", 50, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("display_steps_goal_enabled", 51, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("course_navigation_enabled", 52, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("course_off_course_warnings_enabled", 53, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("segment_navigation_enabled", 54, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("display_orientation", 55, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("mounting_side", 56, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("default_page", 57, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("autosync_min_steps", 58, 2, FitFieldBaseType.UINT16, 1, 0, "steps", null), - new FitMessageFieldDefinition("autosync_min_time", 59, 2, FitFieldBaseType.UINT16, 1, 0, "minutes", null), - new FitMessageFieldDefinition("smart_sleep_window", 60, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("gesture_detection_mode", 61, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("glonass_enabled", 62, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("display_pace", 63, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("display_activity_tracker_enabled", 64, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("phone_notification_enabled", 65, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("phone_notification_tone", 66, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("phone_notification_default_filter", 67, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("phone_notification_activity_filter", 68, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("phone_notification_activity_tone", 69, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("user_notices_enabled", 70, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("lap_key_enabled", 71, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("features", 72, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("features_mask", 73, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("course_points_enabled", 74, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("course_segments_enabled", 75, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_show_track", 76, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_track_color", 77, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("next_dst_change", 78, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("dst_change_value", 79, 1, FitFieldBaseType.SINT8, 1, 0, "hours", null), - new FitMessageFieldDefinition("lactate_threshold_autodetect_enabled", 80, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("backlight_keys", 81, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_alerts", 82, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_gesture", 83, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("bluetooth_connection_alerts_enabled", 84, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("ftp_auto_calc_enabled", 85, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("ble_auto_upload_enabled", 86, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("sleep_do_not_disturb_enabled", 87, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("backlight_smart_notifications", 88, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_sync_frequency", 89, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_activity_detect", 90, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("phone_notification_filters", 91, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("alarm_days", 92, 1, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("auto_update_app_enabled", 93, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("number_of_screens", 94, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("smart_notification_display_orientation", 95, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_lock_enabled", 96, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("grouptrack_activity_type", 97, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("wifi_enabled", 98, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("smart_notification_enabled", 99, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("beeper_enabled", 100, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("goal_notification", 101, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("product_category", 102, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("touch_sensitivity", 103, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("power_controls_items", 104, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("selected_watchface_index", 105, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("livetrack_message_notification_enabled", 106, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("alert_tones_app_only", 107, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("auto_detect_max_hr", 108, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("perf_cond_ntfctn_enabled", 109, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("new_vo2_ntfctn_enabled", 110, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("training_effect_ntfctn_enabled", 111, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("recovery_time_ntfctn_enabled", 112, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("auto_activity_start_enabled", 113, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("move_bar_enabled", 114, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("vibration_intensity", 115, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("lock_on_road", 116, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("map_detail", 117, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("screen_timeout", 119, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("display_theme", 120, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("popularity_routing_enabled", 121, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("glance_mode_layout", 122, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("user_text", 123, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_brightness_current_activity", 124, 1, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("backlight_timeout_current_activity", 125, 1, FitFieldBaseType.UINT8, 1, 0, "s", null), - new FitMessageFieldDefinition("backlight_keys_current_activity", 126, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_alerts_current_activity", 127, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight_gesture_current_activity", 128, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("marine_chart_mode", 129, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("spot_soundings", 130, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("light_sectors", 131, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("marine_symbol_set", 132, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_update_software_enabled", 133, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("tap_interface", 134, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("auto_lock_mode", 135, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("simplified_backlight_timeout", 136, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("draw_segments", 137, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("hourly_alert", 138, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("turn_guidance_popup", 139, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("stress_alert_enabled", 140, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("spo2_mode", 141, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("low_spo2_threshold", 142, 1, FitFieldBaseType.UINT8, 1, 0, "percent", null), - new FitMessageFieldDefinition("sedentary_hr_alert_threshold", 143, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("activity_physio_true_up_enabled", 144, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("smart_notification_timeout", 145, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("sideswipe_enabled", 146, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("sideswipe_direction_inverted", 147, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("draw_contour_lines", 148, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("sedentary_hr_alert_state", 149, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("autosync_max_steps", 150, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("low_spo2_alert_enabled", 151, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("cda_auto_calc_enabled", 152, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("hydration_system_units", 153, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("hydration_containers", 154, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("hydration_alert_enabled", 155, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("hydration_alert_frequency", 156, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("hydration_containers_units", 157, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("hydration_auto_goal_enabled", 158, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("user_phone_verified", 159, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("primary_tracker_enabled", 160, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("phone_notification_default_privacy", 161, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("phone_notification_activity_privacy", 162, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("abnormal_low_hr_alert_state", 163, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("abnormal_low_hr_alert_threshold", 164, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null) - ); - - public static final FitMessageDefinition DEFINITION_USER_PROFILE = new FitMessageDefinition(FIT_MESSAGE_NAME_USER_PROFILE, FIT_MESSAGE_NUMBER_USER_PROFILE, -1, - new FitMessageFieldDefinition("friendly_name", 0, 16, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("gender", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("age", 2, 1, FitFieldBaseType.UINT8, 1, 0, "years", null), - new FitMessageFieldDefinition("height", 3, 1, FitFieldBaseType.UINT8, 1, 0, "cm", null), - new FitMessageFieldDefinition("weight", 4, 2, FitFieldBaseType.UINT16, 10, 0, "kg", null), - new FitMessageFieldDefinition("language", 5, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("elev_setting", 6, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("weight_setting", 7, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("resting_heart_rate", 8, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("default_max_running_heart_rate", 9, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("default_max_biking_heart_rate", 10, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("default_max_heart_rate", 11, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("hr_setting", 12, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("speed_setting", 13, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("dist_setting", 14, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("power_setting", 16, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("activity_class", 17, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("position_setting", 18, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("rmr", 19, 2, FitFieldBaseType.UINT16, 1, 0, "kcal/d", null), - new FitMessageFieldDefinition("active_time", 20, 1, FitFieldBaseType.UINT8, 1, 0, "min", null), - new FitMessageFieldDefinition("temperature_setting", 21, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("local_id", 22, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("global_id", 23, 6, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("birth_year", 24, 1, FitFieldBaseType.UINT8, 1, 1900, "", null), - new FitMessageFieldDefinition("avg_cycle_length", 25, 2, FitFieldBaseType.UINT16, 10000, 0, "m", null), - new FitMessageFieldDefinition("pressure_setting", 26, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("handedness", 27, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("wake_time", 28, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("sleep_time", 29, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("height_setting", 30, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("user_running_step_length", 31, 2, FitFieldBaseType.UINT16, 1, 0, "mm", null), - new FitMessageFieldDefinition("user_walking_step_length", 32, 2, FitFieldBaseType.UINT16, 1, 0, "mm", null), - new FitMessageFieldDefinition("firstbeat_monthly_load", 33, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("firstbeat_recovery_time", 34, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("firstbeat_recovery_time_start", 35, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("firstbeat_max_stress_score", 36, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("firstbeat_running_lt_kmh", 37, 2, FitFieldBaseType.UINT16, 10, 0, "km/h", null), - new FitMessageFieldDefinition("firstbeat_cycling_lt_watts", 38, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("firstbeat_running_maxMET", 39, 4, FitFieldBaseType.FLOAT32, null), - new FitMessageFieldDefinition("firstbeat_cycling_maxMET", 40, 4, FitFieldBaseType.FLOAT32, null), - new FitMessageFieldDefinition("firstbeat_running_lt_timestamp", 41, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("firstbeat_cycling_lt_timestamp", 42, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("resting_hr_auto_update_enabled", 43, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("birth_day", 44, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("birth_month", 45, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("avatar", 46, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("depth_setting", 47, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("dive_count", 49, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("phone_number", 50, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("keep_user_name_private", 51, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("active_minutes_calc_method", 52, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("active_minutes_moderate_zone", 53, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("active_minutes_vigorous_zone", 54, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("swim_skill_level", 55, 1, FitFieldBaseType.UINT8, null) - ); - - public static final FitMessageDefinition DEFINITION_EVENT = new FitMessageDefinition(FIT_MESSAGE_NAME_EVENT, FIT_MESSAGE_NUMBER_EVENT, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("event", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("event_type", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("data16", 2, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("data", 3, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("event_group", 4, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("device_index", 13, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("activity_type", 14, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("start_timestamp", 15, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("activity_subtype", 16, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_DEVICE_INFO = new FitMessageDefinition(FIT_MESSAGE_NAME_DEVICE_INFO, FIT_MESSAGE_NUMBER_DEVICE_INFO, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("device_index", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("device_type", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("manufacturer", 2, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("serial_number", 3, 4, FitFieldBaseType.UINT32Z, null), - new FitMessageFieldDefinition("product", 4, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("software_version", 5, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("hardware_version", 6, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("cum_operating_time", 7, 4, FitFieldBaseType.UINT32, 1, 0, "s", null), - new FitMessageFieldDefinition("cum_training_time", 8, 4, FitFieldBaseType.UINT32, 1, 0, "s", null), - new FitMessageFieldDefinition("reception", 9, 4, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("battery_voltage", 10, 2, FitFieldBaseType.UINT16, 256, 0, "V", null), - new FitMessageFieldDefinition("battery_status", 11, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("rx_pass_count", 15, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("rx_fail_count", 16, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("software_version_string", 17, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("sensor_position", 18, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("descriptor", 19, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("ant_transmission_type", 20, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("ant_device_number", 21, 2, FitFieldBaseType.UINT16Z, null), - new FitMessageFieldDefinition("ant_network", 22, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("source_type", 25, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("product_name", 27, 20, FitFieldBaseType.STRING, null) - ); - - public static final FitMessageDefinition DEFINITION_DEBUG = new FitMessageDefinition(FIT_MESSAGE_NAME_DEBUG, FIT_MESSAGE_NUMBER_DEBUG, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("id", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("string", 1, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("data", 2, 20, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("time256", 3, 20, FitFieldBaseType.UINT8, 256, 0, "s", null), - new FitMessageFieldDefinition("fractional_timestamp", 4, 2, FitFieldBaseType.UINT16, 32768.0, 0, "s", null) - ); - - public static final FitMessageDefinition DEFINITION_SOFTWARE = new FitMessageDefinition(FIT_MESSAGE_NAME_SOFTWARE, FIT_MESSAGE_NUMBER_SOFTWARE, -1, - new FitMessageFieldDefinition("message_index", 254, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("version", 3, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("part_number", 5, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("version_string", 6, 20, FitFieldBaseType.STRING, null) - ); - - public static final FitMessageDefinition DEFINITION_FILE_CAPABILITIES = new FitMessageDefinition(FIT_MESSAGE_NAME_FILE_CAPABILITIES, FIT_MESSAGE_NUMBER_FILE_CAPABILITIES, -1, - new FitMessageFieldDefinition("type", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("flags", 1, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("directory", 2, 16, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("max_count", 3, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("max_size", 4, 4, FitFieldBaseType.UINT32, null) - ); - - public static final FitMessageDefinition DEFINITION_FILE_CREATOR = new FitMessageDefinition(FIT_MESSAGE_NAME_FILE_CREATOR, FIT_MESSAGE_NUMBER_FILE_CREATOR, -1, - new FitMessageFieldDefinition("software_version", 0, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("hardware_version", 1, 2, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("creator_name", 2, 2, FitFieldBaseType.STRING, null) - ); - - public static final FitMessageDefinition DEFINITION_MONITORING = new FitMessageDefinition(FIT_MESSAGE_NAME_MONITORING, FIT_MESSAGE_NUMBER_MONITORING, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("device_index", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("calories", 1, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("distance", 2, 4, FitFieldBaseType.UINT32, 100, 0, "m", null), - // TODO: Scale depends on activity type - new FitMessageFieldDefinition("cycles", 3, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("active_time", 4, 1, FitFieldBaseType.UINT32, 1000, 0, "s", null), - new FitMessageFieldDefinition("activity_type", 5, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("activity_subtype", 6, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("activity_level", 7, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("distance_16", 8, 2, FitFieldBaseType.UINT16, 0.01, 0, "m", null), - new FitMessageFieldDefinition("cycles_16", 9, 2, FitFieldBaseType.UINT16, 0.5, 0, "cycles", null), - new FitMessageFieldDefinition("active_time_16", 10, 2, FitFieldBaseType.UINT16, 1, 0, "s", null), - new FitMessageFieldDefinition("local_timestamp", 11, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("temperature", 12, 2, FitFieldBaseType.SINT16, 100, 0, "°C", null), - new FitMessageFieldDefinition("temperature_min", 14, 2, FitFieldBaseType.SINT16, 100, 0, "°C", null), - new FitMessageFieldDefinition("temperature_max", 15, 2, FitFieldBaseType.SINT16, 100, 0, "°C", null), - // TODO: Array - new FitMessageFieldDefinition("activity_time", 16, 2, FitFieldBaseType.UINT16, 1, 0, "min", null), - new FitMessageFieldDefinition("active_calories", 19, 2, FitFieldBaseType.UINT16, 1, 0, "kcal", null), - new FitMessageFieldDefinition("current_activity_type_intensity", 24, 1, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("timestamp_min_8", 25, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("timestamp_16", 26, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("heart_rate", 27, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("intensity", 28, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("duration_min", 29, 2, FitFieldBaseType.UINT16, 1, 0, "min", null), - new FitMessageFieldDefinition("duration", 30, 4, FitFieldBaseType.UINT32, 1, 0, "s", null), - new FitMessageFieldDefinition("ascent", 31, 4, FitFieldBaseType.UINT32, 1000, 0, "m", null), - new FitMessageFieldDefinition("descent", 32, 4, FitFieldBaseType.UINT32, 1000, 0, "m", null), - new FitMessageFieldDefinition("moderate_activity_minutes", 33, 2, FitFieldBaseType.UINT16, 1, 0, "min", null), - new FitMessageFieldDefinition("vigorous_activity_minutes", 34, 2, FitFieldBaseType.UINT16, 1, 0, "min", null), - new FitMessageFieldDefinition("ascent_total", 35, 4, FitFieldBaseType.UINT32, 1000, 0, "m", null), - new FitMessageFieldDefinition("descent_total", 36, 4, FitFieldBaseType.UINT32, 1000, 0, "m", null), - new FitMessageFieldDefinition("moderate_activity_minutes_total", 37, 2, FitFieldBaseType.UINT16, 1, 0, "min", null), - new FitMessageFieldDefinition("vigorous_activity_minutes_total", 38, 2, FitFieldBaseType.UINT16, 1, 0, "min", null) - ); - - public static final FitMessageDefinition DEFINITION_MONITORING_INFO = new FitMessageDefinition(FIT_MESSAGE_NAME_MONITORING_INFO, FIT_MESSAGE_NUMBER_MONITORING_INFO, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("local_timestamp", 0, 4, FitFieldBaseType.UINT32, null), - // TODO: Arrays - new FitMessageFieldDefinition("activity_type", 1, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("cycles_to_distance", 3, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("cycles_to_calories", 4, 2, FitFieldBaseType.UINT16, null), - - new FitMessageFieldDefinition("resting_metabolic_rate", 5, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("cycles_goal", 7, 4, FitFieldBaseType.UINT32, 2, 0, "cycles", null), - new FitMessageFieldDefinition("monitoring_time_source", 8, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_CONNECTIVITY = new FitMessageDefinition(FIT_MESSAGE_NAME_CONNECTIVITY, FIT_MESSAGE_NUMBER_CONNECTIVITY, MESSAGE_ID_CONNECTIVITY, - new FitMessageFieldDefinition("bluetooth_enabled", 0, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("bluetooth_le_enabled", 1, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("ant_enabled", 2, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("name", 3, 16, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("live_tracking_enabled", 4, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("weather_conditions_enabled", 5, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("weather_alerts_enabled", 6, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("auto_activity_upload_enabled", 7, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("course_download_enabled", 8, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("workout_download_enabled", 9, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("gps_ephemeris_download_enabled", 10, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("live_track_auto_start_enabled", 13, 1, FitFieldBaseType.ENUM, FitBool.FALSE) - ); - - public static final FitMessageDefinition DEFINITION_WEATHER_CONDITIONS = new FitMessageDefinition(FIT_MESSAGE_NAME_WEATHER_CONDITIONS, FIT_MESSAGE_NUMBER_WEATHER_CONDITIONS, MESSAGE_ID_WEATHER_CONDITIONS, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("weather_report", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("temperature", 1, 1, FitFieldBaseType.SINT8, 1, 0, "°C", null), - new FitMessageFieldDefinition("condition", 2, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("wind_direction", 3, 2, FitFieldBaseType.UINT16, 1, 0, "°", null), - new FitMessageFieldDefinition("wind_speed", 4, 2, FitFieldBaseType.UINT16, 1000, 0, "m/s", null), - new FitMessageFieldDefinition("precipitation_probability", 5, 1, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("temperature_feels_like", 6, 1, FitFieldBaseType.SINT8, 1, 0, "°C", null), - new FitMessageFieldDefinition("relative_humidity", 7, 1, FitFieldBaseType.UINT8, 1, 0, "%", null), - new FitMessageFieldDefinition("location", 8, 16, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("observed_at_time", 9, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("observed_location_lat", 10, 4, FitFieldBaseType.SINT32, 1, 0, "semicircles", null), - new FitMessageFieldDefinition("observed_location_long", 11, 4, FitFieldBaseType.SINT32, 1, 0, "semicircles", null), - new FitMessageFieldDefinition("day_of_week", 12, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("high_temperature", 13, 1, FitFieldBaseType.SINT8, 1, 0, "°C", null), - new FitMessageFieldDefinition("low_temperature", 14, 1, FitFieldBaseType.SINT8, 1, 0, "°C", null) - ); - - public static final FitMessageDefinition DEFINITION_WEATHER_ALERT = new FitMessageDefinition(FIT_MESSAGE_NAME_WEATHER_ALERT, FIT_MESSAGE_NUMBER_WEATHER_ALERT, MESSAGE_ID_WEATHER_ALERT, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("report_id", 0, 10, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("issue_time", 1, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("expire_time", 2, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("severity", 3, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("type", 4, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_FILE_DESCRIPTION = new FitMessageDefinition(FIT_MESSAGE_NAME_FILE_DESCRIPTION, FIT_MESSAGE_NUMBER_FILE_DESCRIPTION, -1, - new FitMessageFieldDefinition("message_index", 254, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("manufacturer", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("directory", 1, 16, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("name", 2, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("flags", 3, 1, FitFieldBaseType.UINT8Z, null), - new FitMessageFieldDefinition("purpose", 4, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("garmin_file_purpose", 5, 1, FitFieldBaseType.UINT8, null) - ); - - public static final FitMessageDefinition DEFINITION_OHR_SETTINGS = new FitMessageDefinition(FIT_MESSAGE_NAME_OHR_SETTINGS, FIT_MESSAGE_NUMBER_OHR_SETTINGS, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("enabled", 0, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("sample_rate", 1, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("transmit_hr_enabled", 2, 1, FitFieldBaseType.ENUM, FitBool.FALSE) - ); - - public static final FitMessageDefinition DEFINITION_EXD_SCREEN_CONFIGURATION = new FitMessageDefinition(FIT_MESSAGE_NAME_EXD_SCREEN_CONFIGURATION, FIT_MESSAGE_NUMBER_EXD_SCREEN_CONFIGURATION, -1, - new FitMessageFieldDefinition("screen_index", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("field_count", 1, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("layout", 2, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("screen_enabled", 3, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_EXD_DATA_FIELD_CONFIGURATION = new FitMessageDefinition(FIT_MESSAGE_NAME_EXD_DATA_FIELD_CONFIGURATION, FIT_MESSAGE_NUMBER_EXD_DATA_FIELD_CONFIGURATION, -1, - new FitMessageFieldDefinition("screen_index", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("concept_field", 1, 1, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("field_id", 2, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("concept_count", 3, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("display_type", 4, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("title", 5, 32, FitFieldBaseType.STRING, null) - ); - - public static final FitMessageDefinition DEFINITION_EXD_DATA_CONCEPT_CONFIGURATION = new FitMessageDefinition(FIT_MESSAGE_NAME_EXD_DATA_CONCEPT_CONFIGURATION, FIT_MESSAGE_NUMBER_EXD_DATA_CONCEPT_CONFIGURATION, -1, - new FitMessageFieldDefinition("screen_index", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("concept_field", 1, 1, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("field_id", 2, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("concept_index", 3, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("data_page", 4, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("concept_key", 5, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("scaling", 6, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("offset", 7, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("data_units", 8, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("qualifier", 9, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("descriptor", 10, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("is_signed", 11, 1, FitFieldBaseType.ENUM, FitBool.FALSE) - ); - - public static final FitMessageDefinition DEFINITION_MONITORING_HR_DATA = new FitMessageDefinition(FIT_MESSAGE_NAME_MONITORING_HR_DATA, FIT_MESSAGE_NUMBER_MONITORING_HR_DATA, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("resting_heart_rate", 0, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null), - new FitMessageFieldDefinition("current_day_resting_heart_rate", 1, 1, FitFieldBaseType.UINT8, 1, 0, "bpm", null) - ); - - public static final FitMessageDefinition DEFINITION_ALARM_SETTINGS = new FitMessageDefinition(FIT_MESSAGE_NAME_ALARM_SETTINGS, FIT_MESSAGE_NUMBER_ALARM_SETTINGS, -1, - new FitMessageFieldDefinition("message_index", 254, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("time", 0, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("days", 1, 1, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("enabled", 2, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("sound", 3, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("backlight", 4, 1, FitFieldBaseType.ENUM, FitBool.FALSE), - new FitMessageFieldDefinition("id", 5, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("alarm_mesg", 6, 20, FitFieldBaseType.STRING, null), - new FitMessageFieldDefinition("snooze_count", 7, 1, FitFieldBaseType.UINT8, null) - ); - - public static final FitMessageDefinition DEFINITION_STRESS_LEVEL = new FitMessageDefinition(FIT_MESSAGE_NAME_STRESS_LEVEL, FIT_MESSAGE_NUMBER_STRESS_LEVEL, -1, - new FitMessageFieldDefinition("stress_level_value", 0, 2, FitFieldBaseType.SINT16, null), - new FitMessageFieldDefinition("stress_level_time", 1, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("average_stress_intensity", 2, 1, FitFieldBaseType.SINT8, null) - ); - - public static final FitMessageDefinition DEFINITION_MANUAL_STRESS_LEVEL = new FitMessageDefinition(FIT_MESSAGE_NAME_MANUAL_STRESS_LEVEL, FIT_MESSAGE_NUMBER_MANUAL_STRESS_LEVEL, -1, - new FitMessageFieldDefinition("stress_level_value", 0, 2, FitFieldBaseType.SINT16, null), - new FitMessageFieldDefinition("stress_level_time", 1, 4, FitFieldBaseType.UINT32, null) - ); - - public static final FitMessageDefinition DEFINITION_MAX_MET_DATA = new FitMessageDefinition(FIT_MESSAGE_NAME_MAX_MET_DATA, FIT_MESSAGE_NUMBER_MAX_MET_DATA, -1, - new FitMessageFieldDefinition("update_time", 0, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("max_met", 1, 4, FitFieldBaseType.SINT32, null), - new FitMessageFieldDefinition("vo2_max", 2, 2, FitFieldBaseType.UINT16, 10, 0, "mL/kg/min", null), - new FitMessageFieldDefinition("fitness_age", 3, 1, FitFieldBaseType.SINT8, null), - new FitMessageFieldDefinition("fitness_age_desc", 4, 1, FitFieldBaseType.SINT8, null), - new FitMessageFieldDefinition("sport", 5, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("sub_sport", 6, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("analyzer_method", 7, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("max_met_category", 8, 1, FitFieldBaseType.ENUM, null), - new FitMessageFieldDefinition("calibrated_data", 9, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_WHR_DIAG = new FitMessageDefinition(FIT_MESSAGE_NAME_WHR_DIAG, FIT_MESSAGE_NUMBER_WHR_DIAG, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("fractional_timestamp", 1, 2, FitFieldBaseType.UINT16, 32768.0, 0, "s", null), - new FitMessageFieldDefinition("page_data", 2, 1, FitFieldBaseType.BYTE, null) - ); - - public static final FitMessageDefinition DEFINITION_METRICS_INFO = new FitMessageDefinition(FIT_MESSAGE_NAME_METRICS_INFO, FIT_MESSAGE_NUMBER_METRICS_INFO, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("local_timestamp", 0, 4, FitFieldBaseType.UINT32, null) - ); - - public static final FitMessageDefinition DEFINITION_PAGES_MAP = new FitMessageDefinition(FIT_MESSAGE_NAME_PAGES_MAP, FIT_MESSAGE_NUMBER_PAGES_MAP, -1, - new FitMessageFieldDefinition("message_index", 254, 2, FitFieldBaseType.UINT16, null), - new FitMessageFieldDefinition("map", 0, 10, FitFieldBaseType.BYTE, null), - new FitMessageFieldDefinition("default_to_last", 1, 1, FitFieldBaseType.ENUM, FitBool.FALSE) - ); - - public static final FitMessageDefinition DEFINITION_NEURAL_NETWORK_INFO = new FitMessageDefinition(FIT_MESSAGE_NAME_NEURAL_NETWORK_INFO, FIT_MESSAGE_NUMBER_NEURAL_NETWORK_INFO, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("network_version", 0, 1, FitFieldBaseType.UINT8, null), - new FitMessageFieldDefinition("implicit_message_duration", 1, 2, FitFieldBaseType.UINT16, 1, 0, "s", null), - new FitMessageFieldDefinition("local_timestamp", 2, 4, FitFieldBaseType.UINT32, null) - ); - - public static final FitMessageDefinition DEFINITION_NEURAL_NETWORK_DATA = new FitMessageDefinition(FIT_MESSAGE_NAME_NEURAL_NETWORK_DATA, FIT_MESSAGE_NUMBER_NEURAL_NETWORK_DATA, -1, - new FitMessageFieldDefinition("network_data", 0, 20, FitFieldBaseType.BYTE, null) - ); - - public static final FitMessageDefinition DEFINITION_SLEEP_LEVEL = new FitMessageDefinition(FIT_MESSAGE_NAME_SLEEP_LEVEL, FIT_MESSAGE_NUMBER_SLEEP_LEVEL, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null), - new FitMessageFieldDefinition("sleep_level", 0, 1, FitFieldBaseType.ENUM, null) - ); - - public static final FitMessageDefinition DEFINITION_END_OF_FILE = new FitMessageDefinition(FIT_MESSAGE_NAME_END_OF_FILE, FIT_MESSAGE_NUMBER_END_OF_FILE, -1, - new FitMessageFieldDefinition("timestamp", 253, 4, FitFieldBaseType.UINT32, null) - ); - - - public static final List ALL_DEFINITIONS = Arrays.asList( - DEFINITION_FILE_ID, - DEFINITION_CAPABILITIES, - DEFINITION_DEVICE_SETTINGS, - DEFINITION_USER_PROFILE, - DEFINITION_EVENT, - DEFINITION_DEVICE_INFO, - DEFINITION_DEBUG, - DEFINITION_SOFTWARE, - DEFINITION_FILE_CAPABILITIES, - DEFINITION_FILE_CREATOR, - DEFINITION_MONITORING, - DEFINITION_MONITORING_INFO, - DEFINITION_CONNECTIVITY, - DEFINITION_WEATHER_CONDITIONS, - DEFINITION_WEATHER_ALERT, - DEFINITION_FILE_DESCRIPTION, - DEFINITION_EXD_SCREEN_CONFIGURATION, - DEFINITION_EXD_DATA_FIELD_CONFIGURATION, - DEFINITION_EXD_DATA_CONCEPT_CONFIGURATION, - DEFINITION_OHR_SETTINGS, - DEFINITION_MONITORING_HR_DATA, - DEFINITION_ALARM_SETTINGS, - DEFINITION_STRESS_LEVEL, - DEFINITION_MANUAL_STRESS_LEVEL, - DEFINITION_MAX_MET_DATA, - DEFINITION_WHR_DIAG, - DEFINITION_METRICS_INFO, - DEFINITION_PAGES_MAP, - DEFINITION_NEURAL_NETWORK_INFO, - DEFINITION_NEURAL_NETWORK_DATA, - DEFINITION_SLEEP_LEVEL, - DEFINITION_END_OF_FILE - ); -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageFieldDefinition.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageFieldDefinition.java deleted file mode 100644 index 9b9525c10..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitMessageFieldDefinition.java +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; - -public class FitMessageFieldDefinition { - public final String fieldName; - public final int fieldNumber; - public final int fieldSize; - public final FitFieldBaseType fieldType; - public final double scale; - public final double offset; - public final String units; - public final Object defaultValue; - - public FitMessageFieldDefinition(String fieldName, int fieldNumber, int fieldSize, FitFieldBaseType fieldType, Object defaultValue) { - this(fieldName, fieldNumber, fieldSize, fieldType, 0, 0, null, defaultValue); - } - - public FitMessageFieldDefinition(String fieldName, int fieldNumber, int fieldSize, FitFieldBaseType fieldType, double scale, double offset, String units, Object defaultValue) { - this.fieldName = fieldName; - this.fieldNumber = fieldNumber; - this.fieldSize = fieldSize; - this.fieldType = fieldType; - this.scale = scale; - this.offset = offset; - this.units = units; - this.defaultValue = defaultValue == null ? fieldType.invalidValue : defaultValue; - } - - public void writeToMessage(MessageWriter writer) { - writer.writeByte(fieldNumber); - writer.writeByte(fieldSize); - writer.writeByte(fieldType.typeID); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitParser.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitParser.java deleted file mode 100644 index 8bd92db76..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitParser.java +++ /dev/null @@ -1,301 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseArray; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageReader; -import nodomain.freeyourgadget.gadgetbridge.util.ArrayUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class FitParser { - private static final Logger LOG = LoggerFactory.getLogger(FitParser.class); - - // “.FIT” – magic value indicating a .FIT file - private static final int FIT_MAGIC = 0x5449462E; - - private static final int FLAG_NORMAL_HEADER = 0x80; - private static final int FLAG_DEFINITION_MESSAGE = 0x40; - private static final int FLAG_DEVELOPER_FIELDS = 0x20; - private static final int MASK_LOCAL_MESSAGE_TYPE = 0x0F; - private static final int MASK_TIME_OFFSET = 0x1F; - private static final int MASK_COMPRESSED_LOCAL_MESSAGE_TYPE = 0x60; - - private final SparseArray globalMessageDefinitions; - private final SparseArray localMessageDefinitions = new SparseArray<>(16); - - public FitParser(Collection knownDefinitions) { - globalMessageDefinitions = new SparseArray<>(knownDefinitions.size()); - for (FitMessageDefinition definition : knownDefinitions) { - globalMessageDefinitions.append(definition.globalMessageID, definition); - } - } - - public SparseArray getLocalMessageDefinitions() { - return localMessageDefinitions; - } - - public List parseFitFile(byte[] data) { - if (data.length < 12) throw new IllegalArgumentException("Too short data"); - - final MessageReader reader = new MessageReader(data); - final List result = new ArrayList<>(); - while (!reader.isEof()) { - final int fileHeaderStart = reader.getPosition(); - final int fileHeaderSize = reader.readByte(); - final int protocolVersion = reader.readByte(); - final int profileVersion = reader.readShort(); - final int dataSize = reader.readInt(); - final int dataTypeMagic = reader.readInt(); - final int headerCrc = fileHeaderSize >= 14 ? reader.readShort() : 0; - if (dataTypeMagic != FIT_MAGIC) { - throw new IllegalArgumentException("Not a FIT file, data type signature not found"); - } - if (fileHeaderSize < 12) throw new IllegalArgumentException("Header size too low"); - reader.skip(fileHeaderSize - 14); - - // TODO: Check header CRC - - localMessageDefinitions.clear(); - - int lastTimestamp = 0; - final int end = fileHeaderStart + fileHeaderSize + dataSize; - while (reader.getPosition() < end) { - final int recordHeader = reader.readByte(); - final boolean isDefinitionMessage; - final int localMessageType; - final int currentTimestamp; - if ((recordHeader & FLAG_NORMAL_HEADER) == 0) { - // normal header - isDefinitionMessage = (recordHeader & FLAG_DEFINITION_MESSAGE) != 0; - localMessageType = recordHeader & MASK_LOCAL_MESSAGE_TYPE; - currentTimestamp = -1; - } else { - // compressed timestamp header - final int timestampOffset = recordHeader & MASK_TIME_OFFSET; - localMessageType = (recordHeader & MASK_COMPRESSED_LOCAL_MESSAGE_TYPE) >> 4; - currentTimestamp = lastTimestamp + timestampOffset; - isDefinitionMessage = false; - throw new IllegalArgumentException("Compressed timestamps not supported yet"); - } - - if (isDefinitionMessage) { - final boolean hasDeveloperFields = (recordHeader & FLAG_DEVELOPER_FIELDS) != 0; - final FitLocalMessageDefinition definition = parseDefinitionMessage(reader, hasDeveloperFields); - LOG.trace("Defining local message {} to global message {}", localMessageType, definition.globalDefinition.globalMessageID); - localMessageDefinitions.put(localMessageType, definition); - } else { - final FitLocalMessageDefinition definition = localMessageDefinitions.get(localMessageType); - if (definition == null) { - LOG.error("Use of undefined local message {}", localMessageType); - throw new IllegalArgumentException("Use of undefined local message " + localMessageType); - } - final FitMessage dataMessage = new FitMessage(definition.globalDefinition); - parseDataMessage(reader, definition, dataMessage); - result.add(dataMessage); - } - } - - final int fileCrc = reader.readShort(); - // TODO: Check file CRC - } - return result; - } - - private void parseDataMessage(MessageReader reader, FitLocalMessageDefinition localMessageDefinition, FitMessage dataMessage) { - for (FitLocalFieldDefinition localFieldDefinition : localMessageDefinition.fieldDefinitions) { - final Object value = readValue(reader, localFieldDefinition); - if (!localFieldDefinition.baseType.invalidValue.equals(value)) { - dataMessage.setField(localFieldDefinition.globalDefinition.fieldNumber, value); - } - } - } - - private Object readValue(MessageReader reader, FitLocalFieldDefinition fieldDefinition) { - //switch (fieldDefinition.baseType) { - switch (fieldDefinition.globalDefinition.fieldType) { - case ENUM: - case SINT8: - case UINT8: - case SINT16: - case UINT16: - case SINT32: - case UINT32: - case UINT8Z: - case UINT16Z: - case UINT32Z: - case SINT64: - case UINT64: - case UINT64Z: - return readFitNumber(reader, fieldDefinition.size, fieldDefinition.globalDefinition.scale, fieldDefinition.globalDefinition.offset); - case BYTE: - return fieldDefinition.size == 1 ? reader.readByte() : reader.readBytes(fieldDefinition.size); - case STRING: - return readFitString(reader, fieldDefinition.size); - case FLOAT32: - return readFloat32(reader, fieldDefinition.size); - case FLOAT64: - return readFloat64(reader, fieldDefinition.size); - // TODO: Float data types - default: - throw new IllegalArgumentException("Unable to read value of type " + fieldDefinition.baseType); - } - } - - private float readFloat32(MessageReader reader, int size) { - if (size != 4) { - throw new IllegalArgumentException("Invalid size for Float32: " + size); - } - final byte[] bytes = reader.readBytes(size); - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getFloat(); - } - - private double readFloat64(MessageReader reader, int size) { - if (size != 8) { - throw new IllegalArgumentException("Invalid size for Float64: " + size); - } - final byte[] bytes = reader.readBytes(size); - return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getDouble(); - } - - private String readFitString(MessageReader reader, int size) { - final byte[] bytes = reader.readBytes(size); - final int zero = ArrayUtils.indexOf((byte) 0, bytes); - if (zero < 0) { - LOG.warn("Unterminated string"); - return new String(bytes, StandardCharsets.UTF_8); - } - return new String(bytes, 0, zero, StandardCharsets.UTF_8); - } - - private Object readRawFitNumber(MessageReader reader, int size) { - switch (size) { - case 1: - return reader.readByte(); - case 2: - return reader.readShort(); - case 3: { - // this is strange? - byte[] bytes = new byte[4]; - reader.readBytesTo(3, bytes, 0); - return BLETypeConversions.toUint32(bytes, 0); - } - case 4: - return reader.readInt(); - case 7: { - // this is strange? - byte[] bytes = new byte[8]; - reader.readBytesTo(7, bytes, 0); - return BLETypeConversions.toUint64(bytes, 0); - } - case 8: - return reader.readLong(); - case 12: - // this is strange? - long lower = reader.readLong(); - int upper = reader.readInt(); - return upper * ((double) Long.MAX_VALUE) + lower; - case 16: - // this is strange? - return reader.readLong() + reader.readLong() * (double) (Long.MAX_VALUE); - case 32: - // this is strange? - // TODO: FIXME: 32-byte integer?!? - reader.skip(16); - return Math.pow(2, 128) * (reader.readLong() + reader.readLong() * (double) (Long.MAX_VALUE)); - default: - throw new IllegalArgumentException("Unable to read number of size " + size); - } - } - - private Object readFitNumber(MessageReader reader, int size, double scale, double offset) { - if (scale == 0) { - return readRawFitNumber(reader, size); - } else { - switch (size) { - case 1: - return reader.readByte() / scale + offset; - case 2: - return reader.readShort() / scale + offset; - case 4: - return reader.readInt() / scale + offset; - case 8: - return reader.readLong() / scale + offset; - default: - throw new IllegalArgumentException("Unable to read number of size " + size); - } - } - } - - private FitLocalMessageDefinition parseDefinitionMessage(MessageReader reader, boolean hasDeveloperFields) { - reader.skip(1); - final int architecture = reader.readByte(); - final boolean isBigEndian = architecture == 1; - if (isBigEndian) throw new IllegalArgumentException("Big-endian data not supported yet"); - final int globalMessageType = reader.readShort(); - final FitMessageDefinition messageDefinition = getGlobalDefinition(globalMessageType); - - final int fieldCount = reader.readByte(); - final List fields = new ArrayList<>(fieldCount); - for (int i = 0; i < fieldCount; ++i) { - final int globalField = reader.readByte(); - final int size = reader.readByte(); - final int baseTypeNum = reader.readByte(); - final FitFieldBaseType baseType = FitFieldBaseType.decodeTypeID(baseTypeNum); - - final FitMessageFieldDefinition globalFieldDefinition = getFieldDefinition(messageDefinition, globalField, size, baseType); - - fields.add(new FitLocalFieldDefinition(globalFieldDefinition, size, baseType)); - } - if (hasDeveloperFields) { - final int developerFieldCount = reader.readByte(); - if (developerFieldCount != 0) throw new IllegalArgumentException("Developer fields not supported yet"); - } - - return new FitLocalMessageDefinition(messageDefinition, fields); - } - - private FitMessageFieldDefinition getFieldDefinition(FitMessageDefinition messageDefinition, int field, int size, FitFieldBaseType baseType) { - final FitMessageFieldDefinition definition = messageDefinition.getField(field); - if (definition != null) return definition; - - LOG.warn("Unknown field {} in message {}", field, messageDefinition.globalMessageID); - // System.out.println(String.format(Locale.ROOT, "Unknown field %d in message %d", field, messageDefinition.globalMessageID)); - final FitMessageFieldDefinition newDefinition = new FitMessageFieldDefinition("unknown_" + field, field, size, baseType, baseType.invalidValue); - messageDefinition.addField(newDefinition); - return newDefinition; - } - - private FitMessageDefinition getGlobalDefinition(int globalMessageType) { - final FitMessageDefinition messageDefinition = globalMessageDefinitions.get(globalMessageType); - if (messageDefinition != null) return messageDefinition; - - LOG.warn("Unknown global message {}", globalMessageType); - // System.out.println(String.format(Locale.ROOT, "Unknown message %d", globalMessageType)); - final FitMessageDefinition newDefinition = new FitMessageDefinition("unknown_" + globalMessageType, globalMessageType, 0); - globalMessageDefinitions.append(globalMessageType, newDefinition); - return newDefinition; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitSerializer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitSerializer.java deleted file mode 100644 index edb09742a..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitSerializer.java +++ /dev/null @@ -1,274 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -import android.util.SparseArray; -import android.util.SparseBooleanArray; -import android.util.SparseIntArray; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.MessageWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -public class FitSerializer { - private static final Logger LOG = LoggerFactory.getLogger(FitSerializer.class); - - private final SparseBooleanArray knownMessageIDs = new SparseBooleanArray(16); - private final SparseIntArray localMessageIDs = new SparseIntArray(16); - private final SparseArray localMessageDefinitions; - - // “.FIT” – magic value indicating a .FIT file - private static final int FIT_MAGIC = 0x5449462E; - - private static final int FLAG_NORMAL_HEADER = 0x80; - private static final int FLAG_DEFINITION_MESSAGE = 0x40; - private static final int FLAG_DEVELOPER_FIELDS = 0x20; - private static final int MASK_LOCAL_MESSAGE_TYPE = 0x0F; - private static final int MASK_TIME_OFFSET = 0x1F; - private static final int MASK_COMPRESSED_LOCAL_MESSAGE_TYPE = 0x60; - - public FitSerializer() { - this(new SparseArray(16)); - } - - public FitSerializer(SparseArray initialDefinitions) { - this.localMessageDefinitions = initialDefinitions; - for (int i = 0; i < initialDefinitions.size(); ++i) { - final int localId = initialDefinitions.keyAt(i); - final FitLocalMessageDefinition definition = initialDefinitions.valueAt(i); - knownMessageIDs.put(definition.globalDefinition.globalMessageID, true); - localMessageIDs.put(definition.globalDefinition.globalMessageID, localId); - } - } - - public byte[] serializeFitFile(List messages) { - final MessageWriter writer = new MessageWriter(); - writer.writeByte(14); - writer.writeByte(0x10); - writer.writeShort(2057); - // dataSize will be rewritten later - writer.writeInt(0); - writer.writeInt(FIT_MAGIC); - // CRC will be rewritten later - writer.writeShort(0); - - // first, gather additional needed definitions (if any) - for (final FitMessage message : messages) { - final FitMessageDefinition messageDefinition = message.definition; - final int globalMessageID = messageDefinition.globalMessageID; - if (!knownMessageIDs.get(globalMessageID)) { - LOG.debug("FitSerializer needs to add definition for {}", globalMessageID); - final int localMessageID = localMessageIDs.size() == 0 ? 0 : localMessageIDs.keyAt(localMessageIDs.size() - 1) + 1; - localMessageIDs.put(globalMessageID, localMessageID); - knownMessageIDs.put(globalMessageID, true); - final List fieldDefinitions = messageDefinition.fieldDefinitions; - final List localFieldDefinitions = new ArrayList<>(fieldDefinitions.size()); - for (FitMessageFieldDefinition definition : fieldDefinitions) { - localFieldDefinitions.add(new FitLocalFieldDefinition(definition, definition.fieldSize, definition.fieldType)); - } - localMessageDefinitions.put(localMessageID, new FitLocalMessageDefinition(messageDefinition, localFieldDefinitions)); - } - } - // now, write definition messages for all used message types - final SparseBooleanArray definedMessages = new SparseBooleanArray(); - for (final FitMessage message : messages) { - int localMessageID = localMessageIDs.get(message.definition.globalMessageID); - if (!definedMessages.get(localMessageID)) { - definedMessages.put(localMessageID, true); - - writeDefinitionMessage(writer, localMessageID, localMessageDefinitions.get(localMessageID)); - } - } - - // and now, write the data messages - for (final FitMessage message : messages) { - int localMessageID = localMessageIDs.get(message.definition.globalMessageID); - final FitLocalMessageDefinition localMessageDefinition = localMessageDefinitions.get(localMessageID); - writeDataMessage(writer, message, localMessageID, localMessageDefinition); - } - - writer.writeShort(ChecksumCalculator.computeCrc(writer.peekBytes(), 14, writer.getSize() - 14)); - - final byte[] bytes = writer.getBytes(); - // rewrite size - BLETypeConversions.writeUint32(bytes, 4, bytes.length - 14 - 2); - // rewrite header CRC - BLETypeConversions.writeUint16(bytes, 12, ChecksumCalculator.computeCrc(bytes, 0, 12)); - return bytes; - } - - private void writeDefinitionMessage(MessageWriter writer, int localMessageID, FitLocalMessageDefinition localMessageDefinition) { - writer.writeByte(FLAG_DEFINITION_MESSAGE | localMessageID); - writer.writeByte(0); - writer.writeByte(0); - writer.writeShort(localMessageDefinition.globalDefinition.globalMessageID); - writer.writeByte(localMessageDefinition.fieldDefinitions.size()); - for (FitLocalFieldDefinition localFieldDefinition : localMessageDefinition.fieldDefinitions) { - writer.writeByte(localFieldDefinition.globalDefinition.fieldNumber); - writer.writeByte(localFieldDefinition.size); - writer.writeByte(localFieldDefinition.baseType.typeID); - } - } - - private void writeDataMessage(MessageWriter writer, FitMessage message, int localMessageID, FitLocalMessageDefinition localMessageDefinition) { - writer.writeByte(localMessageID); - - for (FitLocalFieldDefinition localFieldDefinition : localMessageDefinition.fieldDefinitions) { - Object value = message.getField(localFieldDefinition.globalDefinition.fieldNumber); - if (value == null) { - value = localFieldDefinition.baseType.invalidValue; - } - writeValue(writer, localFieldDefinition, value); - } - } - - private void writeValue(MessageWriter writer, FitLocalFieldDefinition fieldDefinition, Object value) { - switch (fieldDefinition.globalDefinition.fieldType) { - case ENUM: - case SINT8: - case UINT8: - case SINT16: - case UINT16: - case SINT32: - case UINT32: - case UINT8Z: - case UINT16Z: - case UINT32Z: - case SINT64: - case UINT64: - case UINT64Z: - writeFitNumber(writer, value, fieldDefinition.size, fieldDefinition.globalDefinition.scale, fieldDefinition.globalDefinition.offset); - break; - case BYTE: - if (fieldDefinition.size == 1) { - writer.writeByte((int) value); - } else { - writer.writeBytes((byte[]) value); - } - break; - case STRING: - writeFitString(writer, (String) value, fieldDefinition.size); - break; - case FLOAT32: - writeFloat32(writer, (float) value); - break; - case FLOAT64: - writeFloat64(writer, (double) value); - break; - default: - throw new IllegalArgumentException("Unable to write value of type " + fieldDefinition.baseType); - } - } - - private void writeFitString(MessageWriter writer, String value, int size) { - if (value.length() >= size) throw new IllegalArgumentException("Too long string"); - final byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - writer.writeBytes(bytes); - final byte[] zeroes = new byte[size - value.length()]; - writer.writeBytes(zeroes); - } - - private void writeFloat32(MessageWriter writer, float value) { - writer.writeBytes(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putFloat(value).array(), 0, 4); - } - - private void writeFloat64(MessageWriter writer, double value) { - writer.writeBytes(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putDouble(value).array(), 0, 4); - } - - private void writeFitNumber(MessageWriter writer, Object value, int size, double scale, double offset) { - if (scale == 0) { - writeRawFitNumber(writer, value, size); - } else { - final long rawValue = Math.round((double) value * scale - offset); - switch (size) { - case 1: - writer.writeByte((int) rawValue); - break; - case 2: - writer.writeShort((int) rawValue); - break; - case 4: - writer.writeInt((int) rawValue); - break; - case 8: - writer.writeLong(rawValue); - break; - default: - throw new IllegalArgumentException("Unable to write number of size " + size); - } - } - } - - private void writeRawFitNumber(MessageWriter writer, Object value, int size) { - switch (size) { - case 1: - writer.writeByte((int) value); - break; - case 2: - writer.writeShort((int) value); - break; - case 3: { - // this is strange? - byte[] bytes = new byte[4]; - BLETypeConversions.writeUint32(bytes, 0, (int) value); - writer.writeBytes(bytes, 0, 3); - break; - } - case 4: - writer.writeInt((int) value); - break; - case 7: { - // this is strange? - byte[] bytes = new byte[8]; - BLETypeConversions.writeUint64(bytes, 0, (long) value); - writer.writeBytes(bytes, 0, 7); - break; - } - case 8: - writer.writeLong((long) value); - break; - case 12: { - // this is strange? (and probably losing precision anyway) - final double val = (double) value; - final long upper = Math.round(val / Long.MAX_VALUE); - final long lower = Math.round(val - upper); - writer.writeLong(lower); - writer.writeInt((int) upper); - break; - } - case 16: { - // this is strange? (and probably losing precision anyway) - final double val = (double) value; - final long upper = Math.round(val / Long.MAX_VALUE); - final long lower = Math.round(val - upper); - writer.writeLong(lower); - writer.writeLong(upper); - break; - } - default: - throw new IllegalArgumentException("Unable to read number of size " + size); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitWeatherConditions.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitWeatherConditions.java deleted file mode 100644 index 0e5e1007f..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/fit/FitWeatherConditions.java +++ /dev/null @@ -1,207 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.fit; - -public final class FitWeatherConditions { - public static final int CLEAR = 0; - public static final int PARTLY_CLOUDY = 1; - public static final int MOSTLY_CLOUDY = 2; - public static final int RAIN = 3; - public static final int SNOW = 4; - public static final int WINDY = 5; - public static final int THUNDERSTORMS = 6; - public static final int WINTRY_MIX = 7; - public static final int FOG = 8; - public static final int HAZY = 11; - public static final int HAIL = 12; - public static final int SCATTERED_SHOWERS = 13; - public static final int SCATTERED_THUNDERSTORMS = 14; - public static final int UNKNOWN_PRECIPITATION = 15; - public static final int LIGHT_RAIN = 16; - public static final int HEAVY_RAIN = 17; - public static final int LIGHT_SNOW = 18; - public static final int HEAVY_SNOW = 19; - public static final int LIGHT_RAIN_SNOW = 20; - public static final int HEAVY_RAIN_SNOW = 21; - public static final int CLOUDY = 22; - - public static final int ALERT_SEVERITY_UNKNOWN = 0; - public static final int ALERT_SEVERITY_WARNING = 1; - public static final int ALERT_SEVERITY_WATCH = 2; - public static final int ALERT_SEVERITY_ADVISORY = 3; - public static final int ALERT_SEVERITY_STATEMENT = 4; - - public static final int ALERT_TYPE_UNSPECIFIED = 0; - public static final int ALERT_TYPE_TORNADO = 1; - public static final int ALERT_TYPE_TSUNAMI = 2; - public static final int ALERT_TYPE_HURRICANE = 3; - public static final int ALERT_TYPE_EXTREME_WIND = 4; - public static final int ALERT_TYPE_TYPHOON = 5; - public static final int ALERT_TYPE_INLAND_HURRICANE = 6; - public static final int ALERT_TYPE_HURRICANE_FORCE_WIND = 7; - public static final int ALERT_TYPE_WATERSPOUT = 8; - public static final int ALERT_TYPE_SEVERE_THUNDERSTORM = 9; - public static final int ALERT_TYPE_WRECKHOUSE_WINDS = 10; - public static final int ALERT_TYPE_LES_SUETES_WIND = 11; - public static final int ALERT_TYPE_AVALANCHE = 12; - public static final int ALERT_TYPE_FLASH_FLOOD = 13; - public static final int ALERT_TYPE_TROPICAL_STORM = 14; - public static final int ALERT_TYPE_INLAND_TROPICAL_STORM = 15; - public static final int ALERT_TYPE_BLIZZARD = 16; - public static final int ALERT_TYPE_ICE_STORM = 17; - public static final int ALERT_TYPE_FREEZING_RAIN = 18; - public static final int ALERT_TYPE_DEBRIS_FLOW = 19; - public static final int ALERT_TYPE_FLASH_FREEZE = 20; - public static final int ALERT_TYPE_DUST_STORM = 21; - public static final int ALERT_TYPE_HIGH_WIND = 22; - public static final int ALERT_TYPE_WINTER_STORM = 23; - public static final int ALERT_TYPE_HEAVY_FREEZING_SPRAY = 24; - public static final int ALERT_TYPE_EXTREME_COLD = 25; - public static final int ALERT_TYPE_WIND_CHILL = 26; - public static final int ALERT_TYPE_COLD_WAVE = 27; - public static final int ALERT_TYPE_HEAVY_SNOW_ALERT = 28; - public static final int ALERT_TYPE_LAKE_EFFECT_BLOWING_SNOW = 29; - public static final int ALERT_TYPE_SNOW_SQUALL = 30; - public static final int ALERT_TYPE_LAKE_EFFECT_SNOW = 31; - public static final int ALERT_TYPE_WINTER_WEATHER = 32; - public static final int ALERT_TYPE_SLEET = 33; - public static final int ALERT_TYPE_SNOWFALL = 34; - public static final int ALERT_TYPE_SNOW_AND_BLOWING_SNOW = 35; - public static final int ALERT_TYPE_BLOWING_SNOW = 36; - public static final int ALERT_TYPE_SNOW_ALERT = 37; - public static final int ALERT_TYPE_ARCTIC_OUTFLOW = 38; - public static final int ALERT_TYPE_FREEZING_DRIZZLE = 39; - public static final int ALERT_TYPE_STORM = 40; - public static final int ALERT_TYPE_STORM_SURGE = 41; - public static final int ALERT_TYPE_RAINFALL = 42; - public static final int ALERT_TYPE_AREAL_FLOOD = 43; - public static final int ALERT_TYPE_COASTAL_FLOOD = 44; - public static final int ALERT_TYPE_LAKESHORE_FLOOD = 45; - public static final int ALERT_TYPE_EXCESSIVE_HEAT = 46; - public static final int ALERT_TYPE_HEAT = 47; - public static final int ALERT_TYPE_WEATHER = 48; - public static final int ALERT_TYPE_HIGH_HEAT_AND_HUMIDITY = 49; - public static final int ALERT_TYPE_HUMIDEX_AND_HEALTH = 50; - public static final int ALERT_TYPE_HUMIDEX = 51; - public static final int ALERT_TYPE_GALE = 52; - public static final int ALERT_TYPE_FREEZING_SPRAY = 53; - public static final int ALERT_TYPE_SPECIAL_MARINE = 54; - public static final int ALERT_TYPE_SQUALL = 55; - public static final int ALERT_TYPE_STRONG_WIND = 56; - public static final int ALERT_TYPE_LAKE_WIND = 57; - public static final int ALERT_TYPE_MARINE_WEATHER = 58; - public static final int ALERT_TYPE_WIND = 59; - public static final int ALERT_TYPE_SMALL_CRAFT_HAZARDOUS_SEAS = 60; - public static final int ALERT_TYPE_HAZARDOUS_SEAS = 61; - public static final int ALERT_TYPE_SMALL_CRAFT = 62; - public static final int ALERT_TYPE_SMALL_CRAFT_WINDS = 63; - public static final int ALERT_TYPE_SMALL_CRAFT_ROUGH_BAR = 64; - public static final int ALERT_TYPE_HIGH_WATER_LEVEL = 65; - public static final int ALERT_TYPE_ASHFALL = 66; - public static final int ALERT_TYPE_FREEZING_FOG = 67; - public static final int ALERT_TYPE_DENSE_FOG = 68; - public static final int ALERT_TYPE_DENSE_SMOKE = 69; - public static final int ALERT_TYPE_BLOWING_DUST = 70; - public static final int ALERT_TYPE_HARD_FREEZE = 71; - public static final int ALERT_TYPE_FREEZE = 72; - public static final int ALERT_TYPE_FROST = 73; - public static final int ALERT_TYPE_FIRE_WEATHER = 74; - public static final int ALERT_TYPE_FLOOD = 75; - public static final int ALERT_TYPE_RIP_TIDE = 76; - public static final int ALERT_TYPE_HIGH_SURF = 77; - public static final int ALERT_TYPE_SMOG = 78; - public static final int ALERT_TYPE_AIR_QUALITY = 79; - public static final int ALERT_TYPE_BRISK_WIND = 80; - public static final int ALERT_TYPE_AIR_STAGNATION = 81; - public static final int ALERT_TYPE_LOW_WATER = 82; - public static final int ALERT_TYPE_HYDROLOGICAL = 83; - public static final int ALERT_TYPE_SPECIAL_WEATHER = 84; - - public static int openWeatherCodeToFitWeatherStatus(int openWeatherCode) { - switch (openWeatherCode) { - case 800: - return CLEAR; - case 801: - case 802: - return PARTLY_CLOUDY; - case 803: - return MOSTLY_CLOUDY; - case 804: - return CLOUDY; - case 701: - case 721: - return HAZY; - case 741: - return FOG; - case 771: - case 781: - return WINDY; - case 615: - return LIGHT_RAIN_SNOW; - case 616: - return HEAVY_RAIN_SNOW; - case 611: - case 612: - case 613: - return WINTRY_MIX; - case 500: - case 520: - case 521: - case 300: - case 310: - case 313: - return LIGHT_RAIN; - case 501: - case 531: - case 301: - case 311: - return RAIN; - case 502: - case 503: - case 504: - case 522: - case 302: - case 312: - case 314: - return HEAVY_RAIN; - case 321: - return SCATTERED_SHOWERS; - case 511: - return UNKNOWN_PRECIPITATION; - case 200: - case 201: - case 202: - case 210: - case 211: - case 212: - case 230: - case 231: - case 232: - return THUNDERSTORMS; - case 221: - return SCATTERED_THUNDERSTORMS; - case 600: - return LIGHT_SNOW; - case 601: - return SNOW; - case 602: - return HEAVY_SNOW; - default: - throw new IllegalArgumentException("Unknown weather code " + openWeatherCode); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationMessage.java deleted file mode 100644 index 902a1bc93..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationMessage.java +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class AuthNegotiationMessage { - public static final int LONG_TERM_KEY_AVAILABILITY_NONE = 0; - public static final int LONG_TERM_KEY_AVAILABILITY_SOME_AVAILABLE = 1; - - public static final int ENCRYPTION_ALGORITHM_NONE = 0; - public static final int ENCRYPTION_ALGORITHM_XXTEA = 1 << 0; - public static final int ENCRYPTION_ALGORITHM_AES128 = 1 << 1; - - public final byte[] packet; - - public AuthNegotiationMessage(int longTermKeyAvailability, int supportedEncryptionAlgorithms) { - final MessageWriter writer = new MessageWriter(11); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_AUTH_NEGOTIATION); - writer.writeByte(longTermKeyAvailability); - writer.writeInt(supportedEncryptionAlgorithms); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationResponseMessage.java deleted file mode 100644 index b7df19fcf..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/AuthNegotiationResponseMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class AuthNegotiationResponseMessage { - public final int status; - public final int response; - public final int longTermKeyAvailability; - public final int supportedEncryptionAlgorithms; - - public AuthNegotiationResponseMessage(int status, int response, int longTermKeyAvailability, int supportedEncryptionAlgorithms) { - this.status = status; - this.response = response; - this.longTermKeyAvailability = longTermKeyAvailability; - this.supportedEncryptionAlgorithms = supportedEncryptionAlgorithms; - } - - public static AuthNegotiationResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - final int longTermKeyAvailability = reader.readByte(); - final int supportedEncryptionAlgorithms = reader.readInt(); - - return new AuthNegotiationResponseMessage(status, response, longTermKeyAvailability, supportedEncryptionAlgorithms); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/BatteryStatusMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/BatteryStatusMessage.java deleted file mode 100644 index 24b8d66cd..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/BatteryStatusMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class BatteryStatusMessage { - public final byte[] packet; - - public BatteryStatusMessage(int batteryPercentage) { - final MessageWriter writer = new MessageWriter(9); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_BATTERY_STATUS); - writer.writeByte(255); - writer.writeByte(batteryPercentage); - writer.writeByte(255); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ConfigurationMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ConfigurationMessage.java deleted file mode 100644 index 1d1fcbeed..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ConfigurationMessage.java +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -import java.util.Arrays; - -public class ConfigurationMessage { - public final byte[] packet; - public final byte[] configurationPayload; - - public ConfigurationMessage(byte[] configurationPayload) { - if (configurationPayload.length > 255) throw new IllegalArgumentException("Too long payload"); - this.configurationPayload = configurationPayload; - - final MessageWriter writer = new MessageWriter(7 + configurationPayload.length); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_CONFIGURATION); - writer.writeByte(configurationPayload.length); - writer.writeBytes(configurationPayload); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } - - public static ConfigurationMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int payloadSize = reader.readByte(); - return new ConfigurationMessage(Arrays.copyOfRange(packet, 5, payloadSize)); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileRequestMessage.java deleted file mode 100644 index b8642c429..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileRequestMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class CreateFileRequestMessage { - public final byte[] packet; - - public CreateFileRequestMessage(int fileSize, int dataType, int subType, int fileIdentifier, int subTypeMask, int numberMask, String path) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_CREATE_FILE_REQUEST); - writer.writeInt(fileSize); - writer.writeByte(dataType); - writer.writeByte(subType); - writer.writeShort(fileIdentifier); - writer.writeByte(0); // reserved - writer.writeByte(subTypeMask); - writer.writeShort(numberMask); - writer.writeString(path); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileResponseMessage.java deleted file mode 100644 index c418eb465..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CreateFileResponseMessage.java +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class CreateFileResponseMessage { - public static final byte RESPONSE_FILE_CREATED_SUCCESSFULLY = 0; - public static final byte RESPONSE_FILE_ALREADY_EXISTS = 1; - public static final byte RESPONSE_NOT_ENOUGH_SPACE = 2; - public static final byte RESPONSE_NOT_SUPPORTED = 3; - public static final byte RESPONSE_NO_SLOTS_AVAILABLE_FOR_FILE_TYPE = 4; - public static final byte RESPONSE_NOT_ENOUGH_SPACE_FOR_FILE_TYPE = 5; - - public final int status; - public final int response; - public final int fileIndex; - public final int dataType; - public final int subType; - public final int fileNumber; - - public CreateFileResponseMessage(int status, int response, int fileIndex, int dataType, int subType, int fileNumber) { - this.status = status; - this.response = response; - this.fileIndex = fileIndex; - this.dataType = dataType; - this.subType = subType; - this.fileNumber = fileNumber; - } - - public static CreateFileResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 6); - final int status = reader.readByte(); - final int response = reader.readByte(); - final int fileIndex = reader.readShort(); - final int dataType = reader.readByte(); - final int subType = reader.readByte(); - final int fileNumber = reader.readShort(); - - return new CreateFileResponseMessage(status, response, fileIndex, dataType, subType, fileNumber); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestMessage.java deleted file mode 100644 index 31f7806f6..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class CurrentTimeRequestMessage { - public final int referenceID; - - public CurrentTimeRequestMessage(int referenceID) { - this.referenceID = referenceID; - } - - public static CurrentTimeRequestMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int referenceID = reader.readInt(); - - return new CurrentTimeRequestMessage(referenceID); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestResponseMessage.java deleted file mode 100644 index 766971668..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/CurrentTimeRequestResponseMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class CurrentTimeRequestResponseMessage { - public final byte[] packet; - - public CurrentTimeRequestResponseMessage(int status, int referenceID, int garminTimestamp, int timeZoneOffset, int dstOffset) { - final MessageWriter writer = new MessageWriter(29); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_CURRENT_TIME_REQUEST); - writer.writeByte(status); - writer.writeInt(referenceID); - writer.writeInt(garminTimestamp); - writer.writeInt(timeZoneOffset); - // TODO: next DST start/end - writer.writeInt(0); - writer.writeInt(0); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationMessage.java deleted file mode 100644 index de936bae1..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import java.util.Locale; - -public class DeviceInformationMessage { - public final int protocolVersion; - public final int productNumber; - public final String unitNumber; - public final int softwareVersion; - public final int maxPacketSize; - public final String bluetoothFriendlyName; - public final String deviceName; - public final String deviceModel; - // dual-pairing flags & MAC addresses... - - public DeviceInformationMessage(int protocolVersion, int productNumber, String unitNumber, int softwareVersion, int maxPacketSize, String bluetoothFriendlyName, String deviceName, String deviceModel) { - this.protocolVersion = protocolVersion; - this.productNumber = productNumber; - this.unitNumber = unitNumber; - this.softwareVersion = softwareVersion; - this.maxPacketSize = maxPacketSize; - this.bluetoothFriendlyName = bluetoothFriendlyName; - this.deviceName = deviceName; - this.deviceModel = deviceModel; - } - - public static DeviceInformationMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int protocolVersion = reader.readShort(); - final int productNumber = reader.readShort(); - final String unitNumber = Long.toString(reader.readInt() & 0xFFFFFFFFL); - final int softwareVersion = reader.readShort(); - final int maxPacketSize = reader.readShort(); - final String bluetoothFriendlyName = reader.readString(); - final String deviceName = reader.readString(); - final String deviceModel = reader.readString(); - - return new DeviceInformationMessage(protocolVersion, productNumber, unitNumber, softwareVersion, maxPacketSize, bluetoothFriendlyName, deviceName, deviceModel); - } - - public String getSoftwareVersionStr() { - int softwareVersionMajor = softwareVersion / 100; - int softwareVersionMinor = softwareVersion % 100; - return String.format(Locale.ROOT, "%d.%02d", softwareVersionMajor, softwareVersionMinor); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationResponseMessage.java deleted file mode 100644 index 247625e87..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DeviceInformationResponseMessage.java +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class DeviceInformationResponseMessage { - public final byte[] packet; - - public DeviceInformationResponseMessage(int status, int protocolVersion, int productNumber, int unitNumber, int softwareVersion, int maxPacketSize, String bluetoothFriendlyName, String deviceName, String deviceModel, int protocolFlags) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_DEVICE_INFORMATION); - writer.writeByte(status); - writer.writeShort(protocolVersion); - writer.writeShort(productNumber); - writer.writeInt(unitNumber); - writer.writeShort(softwareVersion); - writer.writeShort(maxPacketSize); - writer.writeString(bluetoothFriendlyName); - writer.writeString(deviceName); - writer.writeString(deviceModel); - writer.writeByte(protocolFlags); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterRequestMessage.java deleted file mode 100644 index ee11d3c2d..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterRequestMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class DirectoryFileFilterRequestMessage { - public static final int FILTER_NO_FILTER = 0; - public static final int FILTER_DEVICE_DEFAULT_FILTER = 1; - public static final int FILTER_CUSTOM_FILTER = 2; - public static final int FILTER_PENDING_UPLOADS_ONLY = 3; - - public final byte[] packet; - - public DirectoryFileFilterRequestMessage(int filterType) { - final MessageWriter writer = new MessageWriter(7); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_DIRECTORY_FILE_FILTER_REQUEST); - writer.writeByte(filterType); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterResponseMessage.java deleted file mode 100644 index 4122a893e..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DirectoryFileFilterResponseMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class DirectoryFileFilterResponseMessage { - public final int status; - public final int response; - - public static final int RESPONSE_DIRECTORY_FILTER_APPLIED = 0; - public static final int RESPONSE_FAILED_TO_APPLY_DIRECTORY_FILTER = 1; - - public DirectoryFileFilterResponseMessage(int status, int response) { - this.status = status; - this.response = response; - } - - public static DirectoryFileFilterResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - - return new DirectoryFileFilterResponseMessage(status, response); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestMessage.java deleted file mode 100644 index 252ddacc1..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class DownloadRequestMessage { - public static final int REQUEST_CONTINUE_TRANSFER = 0; - public static final int REQUEST_NEW_TRANSFER = 1; - - public final byte[] packet; - - public DownloadRequestMessage(int fileIndex, int dataOffset, int request, int crcSeed, int dataSize) { - final MessageWriter writer = new MessageWriter(19); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_DOWNLOAD_REQUEST); - writer.writeShort(fileIndex); - writer.writeInt(dataOffset); - writer.writeByte(request); - writer.writeShort(crcSeed); - writer.writeInt(dataSize); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestResponseMessage.java deleted file mode 100644 index 2188095ad..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/DownloadRequestResponseMessage.java +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class DownloadRequestResponseMessage { - public final int status; - public final int response; - public final int fileSize; - - public static final byte RESPONSE_DOWNLOAD_REQUEST_OKAY = 0; - public static final byte RESPONSE_DATA_DOES_NOT_EXIST = 1; - public static final byte RESPONSE_DATA_EXISTS_BUT_IS_NOT_DOWNLOADABLE = 2; - public static final byte RESPONSE_NOT_READY_TO_DOWNLOAD = 3; - public static final byte RESPONSE_REQUEST_INVALID = 4; - public static final byte RESPONSE_CRC_INCORRECT = 5; - public static final byte RESPONSE_DATA_REQUESTED_EXCEEDS_FILE_SIZE = 6; - - public DownloadRequestResponseMessage(int status, int response, int fileSize) { - this.status = status; - this.response = response; - this.fileSize = fileSize; - } - - public static DownloadRequestResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - final int fileSize = reader.readInt(); - - return new DownloadRequestResponseMessage(status, response, fileSize); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileReadyMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileReadyMessage.java deleted file mode 100644 index f7864d60d..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileReadyMessage.java +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class FileReadyMessage { - public static final int TRIGGER_MANUAL = 0; - public static final int TRIGGER_AUTOMATIC = 1; - - public final int fileIndex; - public final int dataType; - public final int fileSubtype; - public final int fileNumber; - public final int specificFileFlags; - public final int generalFileFlags; - public final int fileSize; - public final int fileDate; - public final int triggerMethod; - - public FileReadyMessage(int fileIndex, int dataType, int fileSubtype, int fileNumber, int specificFileFlags, int generalFileFlags, int fileSize, int fileDate, int triggerMethod) { - this.fileIndex = fileIndex; - this.dataType = dataType; - this.fileSubtype = fileSubtype; - this.fileNumber = fileNumber; - this.specificFileFlags = specificFileFlags; - this.generalFileFlags = generalFileFlags; - this.fileSize = fileSize; - this.fileDate = fileDate; - this.triggerMethod = triggerMethod; - } - - public static FileReadyMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - - final int fileIndex = reader.readShort(); - final int dataType = reader.readByte(); - final int fileSubtype = reader.readByte(); - final int fileNumber = reader.readShort(); - final int specificFileFlags = reader.readByte(); - final int generalFileFlags = reader.readByte(); - final int fileSize = reader.readInt(); - final int fileDate = reader.readInt(); - final int triggerMethod = reader.readByte(); - - return new FileReadyMessage(fileIndex, dataType, fileSubtype, fileNumber, specificFileFlags, generalFileFlags, fileSize, fileDate, triggerMethod); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataMessage.java deleted file mode 100644 index 5e426b84d..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class FileTransferDataMessage { - public final int flags; - public final int crc; - public final int dataOffset; - public final byte[] data; - - public final byte[] packet; - - public FileTransferDataMessage(int flags, int crc, int dataOffset, byte[] data) { - this.flags = flags; - this.crc = crc; - this.dataOffset = dataOffset; - this.data = data; - - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_FILE_TRANSFER_DATA); - writer.writeByte(flags); - writer.writeShort(crc); - writer.writeInt(dataOffset); - writer.writeBytes(data); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } - - public static FileTransferDataMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - - final int flags = reader.readByte(); - final int crc = reader.readShort(); - final int dataOffset = reader.readInt(); - final int dataSize = packet.length - 13; - final byte[] data = reader.readBytes(dataSize); - - return new FileTransferDataMessage(flags, crc, dataOffset, data); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataResponseMessage.java deleted file mode 100644 index a40eb13b0..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FileTransferDataResponseMessage.java +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class FileTransferDataResponseMessage { - public static final byte RESPONSE_TRANSFER_SUCCESSFUL = 0; - public static final byte RESPONSE_RESEND_LAST_DATA_PACKET = 1; - public static final byte RESPONSE_ABORT_DOWNLOAD_REQUEST = 2; - public static final byte RESPONSE_ERROR_CRC_MISMATCH = 3; - public static final byte RESPONSE_ERROR_DATA_OFFSET_MISMATCH = 4; - public static final byte RESPONSE_SILENT_SYNC_PAUSED = 5; - - public final int status; - public final int response; - public final int nextDataOffset; - - public final byte[] packet; - - public FileTransferDataResponseMessage(int status, int response, int nextDataOffset) { - this.status = status; - this.response = response; - this.nextDataOffset = nextDataOffset; - - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_FILE_TRANSFER_DATA); - writer.writeByte(status); - writer.writeByte(response); - writer.writeInt(nextDataOffset); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } - - public static FileTransferDataResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 6); - final int status = reader.readByte(); - final int response = reader.readByte(); - final int nextDataOffset = reader.readInt(); - - return new FileTransferDataResponseMessage(status, response, nextDataOffset); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FindMyPhoneRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FindMyPhoneRequestMessage.java deleted file mode 100644 index 930462ec9..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FindMyPhoneRequestMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class FindMyPhoneRequestMessage { - public final int duration; - - public FindMyPhoneRequestMessage(int duration) { - this.duration = duration; - } - - public static FindMyPhoneRequestMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - - final int duration = reader.readByte(); - - return new FindMyPhoneRequestMessage(duration); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataMessage.java deleted file mode 100644 index 8ad57d785..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitMessage; - -public class FitDataMessage { - public final byte[] packet; - - public FitDataMessage(FitMessage... messages) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_FIT_DATA); - for (FitMessage message : messages) { - message.writeToMessage(writer); - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataResponseMessage.java deleted file mode 100644 index 55bf3215a..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDataResponseMessage.java +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class FitDataResponseMessage { - public final int requestID; - public final int status; - public final int fitResponse; - - public FitDataResponseMessage(int requestID, int status, int fitResponse) { - this.requestID = requestID; - this.status = status; - this.fitResponse = fitResponse; - } - - public static FitDataResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int fitResponse = reader.readByte(); - - return new FitDataResponseMessage(requestID, status, fitResponse); - } - - public static final int RESPONSE_APPLIED = 0; - public static final int RESPONSE_NO_DEFINITION = 1; - public static final int RESPONSE_MISMATCH = 2; - public static final int RESPONSE_NOT_READY = 3; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionMessage.java deleted file mode 100644 index b9d6d5e9b..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.fit.FitMessageDefinition; - -public class FitDefinitionMessage { - public final byte[] packet; - - public FitDefinitionMessage(FitMessageDefinition... definitions) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_FIT_DEFINITION); - for (FitMessageDefinition definition : definitions) { - definition.writeToMessage(writer); - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionResponseMessage.java deleted file mode 100644 index 2d236c8f3..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/FitDefinitionResponseMessage.java +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class FitDefinitionResponseMessage { - public final int requestID; - public final int status; - public final int fitResponse; - - public FitDefinitionResponseMessage(int requestID, int status, int fitResponse) { - this.requestID = requestID; - this.status = status; - this.fitResponse = fitResponse; - } - - public static FitDefinitionResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int fitResponse = reader.readByte(); - - return new FitDefinitionResponseMessage(requestID, status, fitResponse); - } - - public static final int RESPONSE_APPLIED = 0; - public static final int RESPONSE_NOT_UNIQUE = 1; - public static final int RESPONSE_OUT_OF_RANGE = 2; - public static final int RESPONSE_NOT_READY = 3; -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GenericResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GenericResponseMessage.java deleted file mode 100644 index 865c86fe0..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GenericResponseMessage.java +++ /dev/null @@ -1,38 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class GenericResponseMessage { - public final byte[] packet; - - public GenericResponseMessage(int originalRequestID, int status) { - final MessageWriter writer = new MessageWriter(9); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(originalRequestID); - writer.writeByte(status); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointMessage.java deleted file mode 100644 index 6d3bd8050..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointMessage.java +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsControlCommand; - -public class GncsControlPointMessage { - public final AncsControlCommand command; - - public GncsControlPointMessage(AncsControlCommand command) { - this.command = command; - } - - public static GncsControlPointMessage parsePacket(byte[] packet) { - final AncsControlCommand command = AncsControlCommand.parseCommand(packet, 4, packet.length - 6); - if (command == null) return null; - return new GncsControlPointMessage(command); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointResponseMessage.java deleted file mode 100644 index 62c6f68f2..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsControlPointResponseMessage.java +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class GncsControlPointResponseMessage { - public static final int RESPONSE_SUCCESSFUL = 0; - public static final int RESPONSE_ANCS_ERROR_OCCURRED = 1; - public static final int RESPONSE_INVALID_PARAMETERS = 2; - - public static final int ANCS_ERROR_NO_ERROR = 0; - public static final int ANCS_ERROR_UNKNOWN_ANCS_COMMAND = 0xA0; - public static final int ANCS_ERROR_INVALID_ANCS_COMMAND = 0xA1; - public static final int ANCS_ERROR_INVALID_ANCS_PARAMETER = 0xA2; - public static final int ANCS_ERROR_ACTION_FAILED = 0xA3; - - public final byte[] packet; - - public GncsControlPointResponseMessage(int status, int response, int ancsError) { - final MessageWriter writer = new MessageWriter(11); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_GNCS_CONTROL_POINT_REQUEST); - writer.writeByte(status); - writer.writeByte(response); - writer.writeByte(ancsError); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceMessage.java deleted file mode 100644 index 9ad1aa2ee..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class GncsDataSourceMessage { - public final byte[] packet; - - public GncsDataSourceMessage(byte[] ancsMessage, int dataOffset, int size) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_GNCS_DATA_SOURCE); - writer.writeShort(ancsMessage.length); - // TODO: CRC Seed! - writer.writeShort(ChecksumCalculator.computeCrc(ancsMessage, dataOffset, size)); - writer.writeShort(dataOffset); - writer.writeBytes(ancsMessage, dataOffset, size); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceResponseMessage.java deleted file mode 100644 index f4874d5f7..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsDataSourceResponseMessage.java +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class GncsDataSourceResponseMessage { - public static final int RESPONSE_TRANSFER_SUCCESSFUL = 0; - public static final int RESPONSE_RESEND_LAST_DATA_PACKET = 1; - public static final int RESPONSE_ABORT_REQUEST = 2; - public static final int RESPONSE_ERROR_CRC_MISMATCH = 3; - public static final int RESPONSE_ERROR_DATA_OFFSET_MISMATCH = 4; - - public final int status; - public final int response; - - public GncsDataSourceResponseMessage(int status, int response) { - this.status = status; - this.response = response; - } - - public static GncsDataSourceResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestMessageID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - - return new GncsDataSourceResponseMessage(status, response); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsNotificationSourceMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsNotificationSourceMessage.java deleted file mode 100644 index 6952a5245..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/GncsNotificationSourceMessage.java +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsCategory; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsEvent; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsEventFlag; -import org.apache.commons.lang3.EnumUtils; - -import java.util.Set; - -public class GncsNotificationSourceMessage { - public final byte[] packet; - - public GncsNotificationSourceMessage(AncsEvent event, Set eventFlags, AncsCategory category, int categoryCount, int notificationUID) { - final MessageWriter writer = new MessageWriter(15); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_NOTIFICATION_SOURCE); - - writer.writeByte(event.ordinal()); - writer.writeByte(eventFlags == null ? 26 : ((int) EnumUtils.generateBitVector(AncsEventFlag.class, eventFlags))); - writer.writeByte(category.ordinal()); - writer.writeByte(Math.min(categoryCount, 127)); - writer.writeInt(notificationUID); - // TODO: Extra flags? - writer.writeByte(3); - - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageReader.java deleted file mode 100644 index 669d6e5a1..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageReader.java +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; - -import java.nio.charset.StandardCharsets; - -public class MessageReader { - private final byte[] data; - private int position; - - public MessageReader(byte[] data) { - this.data = data; - } - - public MessageReader(byte[] data, int skipOffset) { - this.data = data; - this.position = skipOffset; - } - - public boolean isEof() { - return position >= data.length; - } - - public int getPosition() { - return position; - } - - public void skip(int offset) { - if (position + offset > data.length) throw new IllegalStateException(); - position += offset; - } - - public int readByte() { - if (position + 1 > data.length) throw new IllegalStateException(); - final int result = BLETypeConversions.toUnsigned(data, position); - ++position; - return result; - } - - public int readShort() { - if (position + 2 > data.length) throw new IllegalStateException(); - final int result = BLETypeConversions.toUint16(data, position); - position += 2; - return result; - } - - public int readInt() { - if (position + 4 > data.length) throw new IllegalStateException(); - final int result = BLETypeConversions.toUint32(data, position); - position += 4; - return result; - } - - public long readLong() { - if (position + 8 > data.length) throw new IllegalStateException(); - final long result = BLETypeConversions.toUint64(data, position); - position += 8; - return result; - } - - public String readString() { - final int size = readByte(); - if (position + size > data.length) throw new IllegalStateException(); - final String result = new String(data, position, size, StandardCharsets.UTF_8); - position += size; - return result; - } - - public byte[] readBytes(int size) { - if (position + size > data.length) throw new IllegalStateException(); - final byte[] result = new byte[size]; - System.arraycopy(data, position, result, 0, size); - position += size; - return result; - } - - public byte[] readBytesTo(int size, byte[] buffer, int offset) { - if (offset + size > buffer.length) throw new IllegalArgumentException(); - if (position + size > data.length) throw new IllegalStateException(); - System.arraycopy(data, position, buffer, offset, size); - position += size; - return buffer; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageWriter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageWriter.java deleted file mode 100644 index 274f55616..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MessageWriter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; - -public class MessageWriter { - private static final int DEFAULT_BUFFER_SIZE = 16384; - - private final byte[] buffer; - private int position; - - public MessageWriter() { - this(DEFAULT_BUFFER_SIZE); - } - - public MessageWriter(int bufferSize) { - this.buffer = new byte[bufferSize]; - } - - public void writeByte(int value) { - if (position + 1 > buffer.length) throw new IllegalStateException(); - BLETypeConversions.writeUint8(buffer, position, value); - ++position; - } - - public void writeShort(int value) { - if (position + 2 > buffer.length) throw new IllegalStateException(); - BLETypeConversions.writeUint16(buffer, position, value); - position += 2; - } - - public void writeInt(int value) { - if (position + 4 > buffer.length) throw new IllegalStateException(); - BLETypeConversions.writeUint32(buffer, position, value); - position += 4; - } - - public void writeLong(long value) { - if (position + 8 > buffer.length) throw new IllegalStateException(); - BLETypeConversions.writeUint64(buffer, position, value); - position += 8; - } - - public void writeString(String value) { - final byte[] bytes = value.getBytes(StandardCharsets.UTF_8); - final int size = bytes.length; - if (size > 255) throw new IllegalArgumentException("Too long string"); - if (position + 1 + size > buffer.length) throw new IllegalStateException(); - writeByte(size); - System.arraycopy(bytes, 0, buffer, position, size); - position += size; - } - - public byte[] getBytes() { - return position == buffer.length ? buffer : Arrays.copyOf(buffer, position); - } - - public byte[] peekBytes() { - return buffer; - } - - public int getSize() { - return position; - } - - public void writeBytes(byte[] bytes) { - writeBytes(bytes, 0, bytes.length); - } - - public void writeBytes(byte[] bytes, int offset, int size) { - if (position + size > buffer.length) throw new IllegalStateException(); - System.arraycopy(bytes, offset, buffer, position, size); - position += size; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesMessage.java deleted file mode 100644 index 3a7958eac..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesMessage.java +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class MusicControlCapabilitiesMessage { - public final int supportedCapabilities; - - public MusicControlCapabilitiesMessage(int supportedCapabilities) { - this.supportedCapabilities = supportedCapabilities; - } - - public static MusicControlCapabilitiesMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int supportedCapabilities = reader.readByte(); - - return new MusicControlCapabilitiesMessage(supportedCapabilities); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesResponseMessage.java deleted file mode 100644 index 616c9b17a..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlCapabilitiesResponseMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminMusicControlCommand; - -public class MusicControlCapabilitiesResponseMessage { - public final byte[] packet; - - public MusicControlCapabilitiesResponseMessage(int status, GarminMusicControlCommand[] commands) { - if (commands.length > 255) throw new IllegalArgumentException("Too many supported commands"); - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_MUSIC_CONTROL_CAPABILITIES); - writer.writeByte(status); - writer.writeByte(commands.length); - for (GarminMusicControlCommand command : commands) { - writer.writeByte(command.ordinal()); - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlEntityUpdateMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlEntityUpdateMessage.java deleted file mode 100644 index 96c46069e..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/MusicControlEntityUpdateMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ams.AmsEntityAttribute; - -public class MusicControlEntityUpdateMessage { - public final byte[] packet; - - public MusicControlEntityUpdateMessage(AmsEntityAttribute... attributes) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_MUSIC_CONTROL_ENTITY_UPDATE); - for (AmsEntityAttribute attribute : attributes) { - attribute.writeToMessage(writer); - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NOTES.txt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NOTES.txt deleted file mode 100644 index 45a370628..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NOTES.txt +++ /dev/null @@ -1,144 +0,0 @@ -Generic message structure: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -+ + -| Payload... | -+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -Generic response message (MESSAGE_RESPONSE) structure: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5000 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Request Message ID | Status | | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + -| | -+ + -| Payload... | -+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -MESSAGE_DEVICE_INFORMATION: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5024 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Protocol Version | Product Number | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Unit Number | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Software Version | Max Packet Size | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|Bluetooth Name.| | -+-+-+-+-+-+-+-+-+ + -| Bluetooth Name UTF-8... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|Device Name Le.| | -+-+-+-+-+-+-+-+-+ + -| Device Name UTF-8... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|Device Model L.| | -+-+-+-+-+-+-+-+-+ + -| Device Model UTF-8... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -MESSAGE_BATTERY_STATUS: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5023 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|unknown| OK|un.| Voltage | Capacity | Remaining Time| -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -MESSAGE_DEVICE_SETTINGS: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5026 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Settings Count| Setting Type | Setting Size | | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + -| Setting Bytes... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Setting Type 2| Setting Size 2| | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + -| Setting Bytes 2... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -MESSAGE_PROTOBUF_REQUEST: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5043 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Request ID | Data Offset | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | Total Protobuf Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | Protobuf Data Length | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + -| Protobuf Payload... | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - -MESSAGE_PROTOBUF_REQUEST response: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5000 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Request Message ID=5043 | Status | Request ID | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | Data Offset | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | Status | Error | CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -+-+-+-+-+-+-+-+-+ - - -MESSAGE_CONFIGURATION: - - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| Size | Message ID=5050 | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -|Bitflags Length| Capability Bitflags... | -+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | CRC | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - - diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionMessage.java deleted file mode 100644 index 5a3a4dadd..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionMessage.java +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class NotificationServiceSubscriptionMessage { - public static final int INTENT_UNSUBSCRIBE = 0; - public static final int INTENT_SUBSCRIBE = 1; - private static final int FEATURE_FLAG_PHONE_NUMBER = 1; - - public final int intentIndicator; - public final int featureFlags; - - public NotificationServiceSubscriptionMessage(int intentIndicator, int featureFlags) { - this.intentIndicator = intentIndicator; - this.featureFlags = featureFlags; - } - - public boolean isSubscribe() { - return intentIndicator == INTENT_SUBSCRIBE; - } - - public boolean hasPhoneNumberSupport() { - return (featureFlags & FEATURE_FLAG_PHONE_NUMBER) != 0; - } - - public static NotificationServiceSubscriptionMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - - final int intentIndicator = reader.readByte(); - final int featureFlags = packet.length > 7 ? reader.readByte() : 0; - - return new NotificationServiceSubscriptionMessage(intentIndicator, featureFlags); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionResponseMessage.java deleted file mode 100644 index 588ac4189..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/NotificationServiceSubscriptionResponseMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class NotificationServiceSubscriptionResponseMessage { - public final byte[] packet; - - public NotificationServiceSubscriptionResponseMessage(int status, int response, int intent, int featureFlags) { - final MessageWriter writer = new MessageWriter(12); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_NOTIFICATION_SERVICE_SUBSCRIPTION); - writer.writeByte(status); - writer.writeByte(response); - writer.writeByte(intent); - writer.writeByte(featureFlags); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestMessage.java deleted file mode 100644 index deab2e665..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestMessage.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class ProtobufRequestMessage { - public final byte[] packet; - public final int requestId; - public final int dataOffset; - public final int totalProtobufLength; - public final int protobufDataLength; - public final byte[] messageBytes; - - public ProtobufRequestMessage(int requestId, int dataOffset, int totalProtobufLength, int protobufDataLength, byte[] messageBytes) { - this.requestId = requestId; - this.dataOffset = dataOffset; - this.totalProtobufLength = totalProtobufLength; - this.protobufDataLength = protobufDataLength; - this.messageBytes = messageBytes; - - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_PROTOBUF_REQUEST); - writer.writeShort(requestId); - writer.writeInt(dataOffset); - writer.writeInt(totalProtobufLength); - writer.writeInt(protobufDataLength); - writer.writeBytes(messageBytes); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } - - public static ProtobufRequestMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int dataOffset = reader.readInt(); - final int totalProtobufLength= reader.readInt(); - final int protobufDataLength = reader.readInt(); - final byte[] messageBytes = reader.readBytes(protobufDataLength); - return new ProtobufRequestMessage(requestID, dataOffset, totalProtobufLength, protobufDataLength, messageBytes); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestResponseMessage.java deleted file mode 100644 index 5263eced1..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ProtobufRequestResponseMessage.java +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class ProtobufRequestResponseMessage { - public static final int NO_ERROR = 0; - public static final int UNKNOWN_REQUEST_ID = 100; - public static final int DUPLICATE_PACKET = 101; - public static final int MISSING_PACKET = 102; - public static final int EXCEEDED_TOTAL_PROTOBUF_LENGTH = 103; - public static final int PROTOBUF_PARSE_ERROR = 200; - public static final int UNKNOWN_PROTOBUF_MESSAGE = 201; - - public final int status; - public final int requestId; - public final int dataOffset; - public final int protobufStatus; - public final int error; - - public ProtobufRequestResponseMessage(int status, int requestId, int dataOffset, int protobufStatus, int error) { - this.status = status; - this.requestId = requestId; - this.dataOffset = dataOffset; - this.protobufStatus = protobufStatus; - this.error = error; - } - - public static ProtobufRequestResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestMessageID = reader.readShort(); - final int status = reader.readByte(); - final int requestID = reader.readShort(); - final int dataOffset = reader.readInt(); - final int protobufStatus = reader.readByte(); - final int error = reader.readByte(); - - return new ProtobufRequestResponseMessage(status, requestID, dataOffset, protobufStatus, error); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/RequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/RequestMessage.java deleted file mode 100644 index 929fc224e..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/RequestMessage.java +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class RequestMessage { - public final byte[] packet; - - public RequestMessage(int requestMessageID) { - final MessageWriter writer = new MessageWriter(8); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_REQUEST); - writer.writeShort(requestMessageID); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ResponseMessage.java deleted file mode 100644 index 007cb39dd..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/ResponseMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; - -import java.util.Locale; - -public class ResponseMessage { - public final int requestID; - public final int status; - - public ResponseMessage(int requestID, int status) { - this.requestID = requestID; - this.status = status; - } - - public static ResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - - return new ResponseMessage(requestID, status); - } - - public String getStatusStr() { - switch (status) { - case VivomoveConstants.STATUS_ACK: - return "ACK"; - case VivomoveConstants.STATUS_NAK: - return "NAK"; - case VivomoveConstants.STATUS_UNSUPPORTED: - return "UNSUPPORTED"; - case VivomoveConstants.STATUS_DECODE_ERROR: - return "DECODE ERROR"; - case VivomoveConstants.STATUS_CRC_ERROR: - return "CRC ERROR"; - case VivomoveConstants.STATUS_LENGTH_ERROR: - return "LENGTH ERROR"; - default: - return String.format(Locale.ROOT, "Unknown status %x", status); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsMessage.java deleted file mode 100644 index 671848854..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsMessage.java +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminDeviceSetting; - -import java.util.Map; - -public class SetDeviceSettingsMessage { - public final byte[] packet; - - public SetDeviceSettingsMessage(Map settings) { - final int settingsCount = settings.size(); - if (settingsCount == 0) throw new IllegalArgumentException("Empty settings"); - if (settingsCount > 255) throw new IllegalArgumentException("Too many settings"); - - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_DEVICE_SETTINGS); - writer.writeByte(settings.size()); - for (Map.Entry settingPair : settings.entrySet()) { - final GarminDeviceSetting setting = settingPair.getKey(); - writer.writeByte(setting.ordinal()); - final Object value = settingPair.getValue(); - if (value instanceof String) { - writer.writeString((String) value); - } else if (value instanceof Integer) { - writer.writeByte(4); - writer.writeInt((Integer) value); - } else if (value instanceof Boolean) { - writer.writeByte(1); - writer.writeByte(Boolean.TRUE.equals(value) ? 1 : 0); - } else { - throw new IllegalArgumentException("Unsupported setting value type " + value); - } - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsResponseMessage.java deleted file mode 100644 index 3f608ff39..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SetDeviceSettingsResponseMessage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class SetDeviceSettingsResponseMessage { - public final int status; - public final int response; - - public SetDeviceSettingsResponseMessage(int status, int response) { - this.status = status; - this.response = response; - } - - public static SetDeviceSettingsResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - - return new SetDeviceSettingsResponseMessage(status, response); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesRequestMessage.java deleted file mode 100644 index c66cf4d08..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesRequestMessage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class SupportedFileTypesRequestMessage { - public final byte[] packet; - - public SupportedFileTypesRequestMessage() { - final MessageWriter writer = new MessageWriter(6); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_SUPPORTED_FILE_TYPES_REQUEST); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesResponseMessage.java deleted file mode 100644 index a84a902e2..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SupportedFileTypesResponseMessage.java +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import java.util.ArrayList; -import java.util.List; - -public class SupportedFileTypesResponseMessage { - public static final int FILE_DATA_TYPE_FIT = 128; - public static final int FILE_DATA_TYPE_GRAPHIC = 2; - public static final int FILE_DATA_TYPE_INVALID = -1; - public static final int FILE_DATA_TYPE_NON_FIT = 255; - - public final int status; - public final List fileTypes; - - public SupportedFileTypesResponseMessage(int status, List fileTypes) { - this.status = status; - this.fileTypes = fileTypes; - } - - public static SupportedFileTypesResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - - final int typeCount = reader.readByte(); - final List types = new ArrayList<>(typeCount); - for (int i = 0; i < typeCount; ++i) { - final int fileDataType = reader.readByte(); - final int fileSubType = reader.readByte(); - final String garminDeviceFileType = reader.readString(); - types.add(new FileTypeInfo(fileDataType, fileSubType, garminDeviceFileType)); - } - - return new SupportedFileTypesResponseMessage(status, types); - } - - public static class FileTypeInfo { - public final int fileDataType; - public final int fileSubType; - public final String garminDeviceFileType; - - public FileTypeInfo(int fileDataType, int fileSubType, String garminDeviceFileType) { - this.fileDataType = fileDataType; - this.fileSubType = fileSubType; - this.garminDeviceFileType = garminDeviceFileType; - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SyncRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SyncRequestMessage.java deleted file mode 100644 index 52225ee6b..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SyncRequestMessage.java +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminMessageType; -import nodomain.freeyourgadget.gadgetbridge.util.GB; - -import java.util.HashSet; -import java.util.Set; - -public class SyncRequestMessage { - public static final int OPTION_MANUAL = 0; - public static final int OPTION_INVISIBLE = 1; - public static final int OPTION_VISIBLE_AS_NEEDED = 2; - - public final int option; - public final Set fileTypes; - - public SyncRequestMessage(int option, Set fileTypes) { - this.option = option; - this.fileTypes = fileTypes; - } - - public static SyncRequestMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int option = reader.readByte(); - final int bitMaskSize = reader.readByte(); - final byte[] longBits = reader.readBytesTo(bitMaskSize, new byte[8], 0); - long bitMask = BLETypeConversions.toUint64(longBits, 0); - - final Set fileTypes = new HashSet<>(GarminMessageType.values().length); - for (GarminMessageType messageType : GarminMessageType.values()) { - int num = messageType.ordinal(); - long mask = 1L << num; - if ((bitMask & mask) != 0) { - fileTypes.add(messageType); - bitMask &= ~mask; - } - } - if (bitMask != 0) { - throw new IllegalArgumentException("Unknown bit mask " + GB.hexdump(longBits, 0, longBits.length)); - } - - return new SyncRequestMessage(option, fileTypes); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventMessage.java deleted file mode 100644 index fda18319f..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventMessage.java +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.GarminSystemEventType; - -public class SystemEventMessage { - public final byte[] packet; - - public SystemEventMessage(GarminSystemEventType eventType, Object value) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_SYSTEM_EVENT); - writer.writeByte(eventType.ordinal()); - if (value instanceof String) { - writer.writeString((String) value); - } else if (value instanceof Integer) { - writer.writeByte((Integer) value); - } else { - throw new IllegalArgumentException("Unsupported event value type " + value); - } - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventResponseMessage.java deleted file mode 100644 index 0d05c5097..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/SystemEventResponseMessage.java +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class SystemEventResponseMessage { - public final int status; - public final int response; - - public SystemEventResponseMessage(int status, int response) { - this.status = status; - this.response = response; - } - - public static SystemEventResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int requestID = reader.readShort(); - final int status = reader.readByte(); - final int response = reader.readByte(); - - return new SystemEventResponseMessage(status, response); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestMessage.java deleted file mode 100644 index f1b0e3a82..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestMessage.java +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class UploadRequestMessage { - public final byte[] packet; - - public UploadRequestMessage(int fileIndex, int dataOffset, int maxSize, int crcSeed) { - final MessageWriter writer = new MessageWriter(18); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_UPLOAD_REQUEST); - writer.writeShort(fileIndex); - writer.writeInt(maxSize); - writer.writeInt(dataOffset); - writer.writeShort(crcSeed); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestResponseMessage.java deleted file mode 100644 index 80fa53f4c..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/UploadRequestResponseMessage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class UploadRequestResponseMessage { - public static final byte RESPONSE_UPLOAD_REQUEST_OKAY = 0; - public static final byte RESPONSE_DATA_FILE_INDEX_DOES_NOT_EXIST = 1; - public static final byte RESPONSE_DATA_FILE_INDEX_EXISTS_BUT_IS_NOT_WRITEABLE = 2; - public static final byte RESPONSE_NOT_ENOUGH_SPACE_TO_COMPLETE_WRITE = 3; - public static final byte RESPONSE_REQUEST_INVALID = 4; - public static final byte RESPONSE_NOT_READY_TO_UPLOAD = 5; - public static final byte RESPONSE_CRC_INCORRECT = 6; - - public final int status; - public final int response; - public final int dataOffset; - public final int maxFileSize; - public final int crcSeed; - - public UploadRequestResponseMessage(int status, int response, int dataOffset, int maxFileSize, int crcSeed) { - this.status = status; - this.response = response; - this.dataOffset = dataOffset; - this.maxFileSize = maxFileSize; - this.crcSeed = crcSeed; - } - - public static UploadRequestResponseMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 6); - final int status = reader.readByte(); - final int response = reader.readByte(); - final int dataOffset = reader.readInt(); - final int maxFileSize = reader.readInt(); - final int crcSeed = reader.readInt(); - - return new UploadRequestResponseMessage(status, response, dataOffset, maxFileSize, crcSeed); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestMessage.java deleted file mode 100644 index 8789a3ae0..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -public class WeatherRequestMessage { - public final int format; - public final int latitude; - public final int longitude; - public final int hoursOfForecast; - - public WeatherRequestMessage(int format, int latitude, int longitude, int hoursOfForecast) { - this.format = format; - this.latitude = latitude; - this.longitude = longitude; - this.hoursOfForecast = hoursOfForecast; - } - - public static WeatherRequestMessage parsePacket(byte[] packet) { - final MessageReader reader = new MessageReader(packet, 4); - final int format = reader.readByte(); - final int latitude = reader.readInt(); - final int longitude = reader.readInt(); - final int hoursOfForecast = reader.readByte(); - - return new WeatherRequestMessage(format, latitude, longitude, hoursOfForecast); - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestResponseMessage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestResponseMessage.java deleted file mode 100644 index d0a480a89..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/messages/WeatherRequestResponseMessage.java +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.messages; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; - -public class WeatherRequestResponseMessage { - public final byte[] packet; - - public WeatherRequestResponseMessage(int status, int requestStatus, int totalDataMessages, int timeout) { - final MessageWriter writer = new MessageWriter(); - writer.writeShort(0); // packet size will be filled below - writer.writeShort(VivomoveConstants.MESSAGE_RESPONSE); - writer.writeShort(VivomoveConstants.MESSAGE_WEATHER_REQUEST); - writer.writeByte(status); - writer.writeByte(requestStatus); - writer.writeByte(totalDataMessages); - writer.writeShort(timeout); - writer.writeShort(0); // CRC will be filled below - final byte[] packet = writer.getBytes(); - BLETypeConversions.writeUint16(packet, 0, packet.length); - BLETypeConversions.writeUint16(packet, packet.length - 2, ChecksumCalculator.computeCrc(packet, 0, packet.length - 2)); - this.packet = packet; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationData.java deleted file mode 100644 index 7e71d96d6..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationData.java +++ /dev/null @@ -1,166 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.notifications; - -import android.util.SparseIntArray; -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsAttribute; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsCategory; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsEventFlag; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - -public class NotificationData { - private static final Logger LOG = LoggerFactory.getLogger(NotificationData.class); - - private static final SparseIntArray POSITIVE_NOTIFICATION_ACTIONS = indexingMap(NotificationSpec.Action.TYPE_SYNTECTIC_OPEN, NotificationSpec.Action.TYPE_WEARABLE_REPLY, NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR, NotificationSpec.Action.TYPE_WEARABLE_SIMPLE); - private static final SparseIntArray NEGATIVE_NOTIFICATION_ACTIONS = indexingMap(NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS, NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS_ALL, NotificationSpec.Action.TYPE_SYNTECTIC_MUTE); - - public final NotificationSpec spec; - public final AncsCategory category; - public final Set flags; - public final String title; - public final String subtitle; - public final String message; - public final NotificationSpec.Action positiveAction; - public final NotificationSpec.Action negativeAction; - - public NotificationData(NotificationSpec spec) { - this.spec = spec; - - final AncsCategory category; - final Set flags = new HashSet<>(); - switch (spec.type) { - case GENERIC_SMS: - category = AncsCategory.SMS; - break; - case GENERIC_PHONE: - category = AncsCategory.INCOMING_CALL; - flags.add(AncsEventFlag.IMPORTANT); - break; - case GENERIC_EMAIL: - case GMAIL: - case BBM: - case MAILBOX: - case OUTLOOK: - category = AncsCategory.EMAIL; - break; - case GENERIC_NAVIGATION: - case GOOGLE_MAPS: - category = AncsCategory.LOCATION; - break; - case GENERIC_CALENDAR: - case GENERIC_ALARM_CLOCK: - category = AncsCategory.SCHEDULE; - break; - case FACEBOOK: - case LINKEDIN: - flags.add(AncsEventFlag.SILENT); - category = AncsCategory.SOCIAL; - break; - // TODO: The rest - default: - category = AncsCategory.OTHER; - break; - } - - this.positiveAction = findNotificationAction(spec, POSITIVE_NOTIFICATION_ACTIONS); - this.negativeAction = findNotificationAction(spec, NEGATIVE_NOTIFICATION_ACTIONS); - - if (this.positiveAction != null) flags.add(AncsEventFlag.POSITIVE_ACTION); - if (this.negativeAction != null) flags.add(AncsEventFlag.NEGATIVE_ACTION); - - this.category = category; - this.flags = flags; - - if (!StringUtils.isEmpty(spec.title)) { - this.title = spec.title; - this.subtitle = spec.subject; - this.message = spec.body; - } else if (!StringUtils.isEmpty(spec.subject)) { - this.title = spec.subject; - this.subtitle = null; - this.message = spec.body; - } else if (!StringUtils.isEmpty(spec.body)) { - this.title = spec.body; - this.subtitle = null; - this.message = spec.body; - } else { - // everything empty!?! - this.title = spec.type.name(); - this.subtitle = null; - this.message = spec.sender; - } - } - - public String getAttribute(AncsAttribute attribute) { - switch (attribute) { - case DATE: - final long notificationTimestamp = spec.when == 0 ? System.currentTimeMillis() : spec.when; - return VivomoveConstants.ANCS_DATE_FORMAT.format(new Date(notificationTimestamp)); - case TITLE: - return title; - case SUBTITLE: - return subtitle; - case APP_IDENTIFIER: - return spec.sourceAppId; - case MESSAGE: - return message; - case MESSAGE_SIZE: - return Integer.toString(message.length()); - case POSITIVE_ACTION_LABEL: - return positiveAction == null ? null : positiveAction.title; - case NEGATIVE_ACTION_LABEL: - return negativeAction == null ? null : negativeAction.title; - case PHONE_NUMBER: - return spec.phoneNumber; - default: - LOG.warn("Unknown attribute {}", attribute); - return null; - } - } - - - private static NotificationSpec.Action findNotificationAction(NotificationSpec notificationSpec, SparseIntArray expectedActionsMap) { - if (notificationSpec == null || notificationSpec.attachedActions == null) return null; - - int bestIndex = Integer.MAX_VALUE; - NotificationSpec.Action bestAction = null; - for (final NotificationSpec.Action action : notificationSpec.attachedActions) { - final int index = expectedActionsMap.get(action.type); - if (index > 0 && index < bestIndex) { - bestIndex = index; - bestAction = action; - } - } - return bestAction; - } - - private static SparseIntArray indexingMap(int... data) { - final SparseIntArray result = new SparseIntArray(data.length); - for (int i = 0; i < data.length; ++i) { - result.put(data[i], i + 1); - } - return result; - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationStorage.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationStorage.java deleted file mode 100644 index f95b69b59..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/notifications/NotificationStorage.java +++ /dev/null @@ -1,117 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.notifications; - -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.util.SparseLongArray; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ancs.AncsCategory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -public class NotificationStorage { - private static final Logger LOG = LoggerFactory.getLogger(NotificationStorage.class); - - private static final long EXPIRATION = 30 * 1000; - - private final Object lock = new Object(); - private final SparseArray storage = new SparseArray<>(); - private final LinkedHashMap> expirationQueue = new LinkedHashMap<>(); - private final SparseLongArray expirationForNotification = new SparseLongArray(); - - private final SparseIntArray categoryCounts = new SparseIntArray(AncsCategory.values().length); - - public void registerNewNotification(NotificationData notificationData) { - final long now = System.currentTimeMillis(); - final long expiration = now + EXPIRATION; - - final int notificationId = notificationData.spec.getId(); - - synchronized (lock) { - cleanup(); - storage.put(notificationId, notificationData); - final int category = notificationData.category.ordinal(); - categoryCounts.put(category, categoryCounts.get(category) + 1); - expirationForNotification.put(notificationId, expiration); - - Set expirationSet = expirationQueue.get(expiration); - if (expirationSet == null) { - expirationSet = new HashSet<>(1); - expirationQueue.put(expiration, expirationSet); - } - expirationSet.add(notificationId); - } - } - - public void deleteNotification(int id) { - synchronized (lock) { - final NotificationData notificationData = storage.get(id); - if (notificationData != null) { - storage.delete(id); - final int categoryOrdinal = notificationData.category.ordinal(); - categoryCounts.put(categoryOrdinal, categoryCounts.get(categoryOrdinal) - 1); - } - final long expiration = expirationForNotification.get(id); - final Set expirationSet = expirationQueue.get(expiration); - if (expirationSet != null) { - expirationSet.remove(id); - } - cleanup(); - } - } - - public NotificationData retrieveNotification(int id) { - synchronized (lock) { - cleanup(); - return storage.get(id); - } - } - - public int getCountInCategory(AncsCategory category) { - synchronized (lock) { - return categoryCounts.get(category.ordinal()); - } - } - - private void cleanup() { - final long now = System.currentTimeMillis(); - Set expiredNotifications = null; - for (final Map.Entry> entry : expirationQueue.entrySet()) { - final long expiration = entry.getKey(); - if (expiration > now) break; - - final Set setToExpire = entry.getValue(); - if (expiredNotifications == null) expiredNotifications = new HashSet<>(setToExpire.size()); - - expiredNotifications.addAll(setToExpire); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("{}/{}/{} notification(s) in storage, removing {}", storage.size(), expirationQueue.size(), expirationForNotification.size(), expiredNotifications == null ? 0 : expiredNotifications.size()); - } - if (expiredNotifications == null) return; - - for (final Integer toExpire : expiredNotifications) { - deleteNotification(toExpire); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/uploads/FileUploadQueue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/uploads/FileUploadQueue.java deleted file mode 100644 index 06fc7ddde..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/vivomovehr/uploads/FileUploadQueue.java +++ /dev/null @@ -1,215 +0,0 @@ -/* Copyright (C) 2023-2024 Petr Kadlec - - 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.vivomovehr.uploads; - -import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.VivomoveConstants; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.ChecksumCalculator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.VivomoveHrCommunicator; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.CreateFileRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.CreateFileResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.DownloadRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.FileTransferDataResponseMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.UploadRequestMessage; -import nodomain.freeyourgadget.gadgetbridge.service.devices.vivomovehr.messages.UploadRequestResponseMessage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.Queue; - -public class FileUploadQueue { - private static final Logger LOG = LoggerFactory.getLogger(FileUploadQueue.class); - private static final int MAX_BLOCK_SIZE = 500; - // TODO: ? - private static final int UPLOAD_FLAGS = 0; - - private final VivomoveHrCommunicator communicator; - - private final Queue queue = new LinkedList<>(); - - private QueueItem currentlyUploadingItem; - private int currentCrc; - private long totalRemainingBytes; - - public FileUploadQueue(VivomoveHrCommunicator communicator) { - this.communicator = communicator; - } - - public void queueCreateFile(int fileSize, int dataType, int subType, int fileIdentifier, String targetPath, byte[] data) { - queue.add(new QueueItem(fileSize, dataType, subType, fileIdentifier, targetPath, data)); - totalRemainingBytes += fileSize; - checkStartNextUpload(); - } - - public void queueUploadFile(int fileSize, int fileIndex, byte[] data) { - queue.add(new QueueItem(fileSize, fileIndex, data)); - totalRemainingBytes += fileSize; - checkStartNextUpload(); - } - - public void cancelAllUploads() { - queue.clear(); - currentlyUploadingItem = null; -// communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ABORT_DOWNLOAD_REQUEST, 0).packet); - } - - private boolean checkStartNextUpload() { - if (currentlyUploadingItem != null) { - LOG.debug("Another upload is pending"); - return false; - } - if (queue.isEmpty()) { - LOG.debug("No upload in queue"); - return true; - } - startNextUpload(); - return false; - } - - private void startNextUpload() { - currentlyUploadingItem = queue.remove(); - currentCrc = 0; - if (currentlyUploadingItem.create) { - LOG.info("Requesting creation of '{}' ({}/{}/{}; {} B)", currentlyUploadingItem.targetPath, currentlyUploadingItem.dataType, currentlyUploadingItem.subType, currentlyUploadingItem.fileIdentifier, currentlyUploadingItem.fileSize); - communicator.sendMessage(new CreateFileRequestMessage(currentlyUploadingItem.fileSize, currentlyUploadingItem.dataType, currentlyUploadingItem.subType, currentlyUploadingItem.fileIdentifier, 0, -1, currentlyUploadingItem.targetPath).packet); - } else { - LOG.info("Requesting upload of {} ({} B)", currentlyUploadingItem.fileIndex, currentlyUploadingItem.fileSize); - communicator.sendMessage(new UploadRequestMessage(currentlyUploadingItem.fileIndex, 0, DownloadRequestMessage.REQUEST_NEW_TRANSFER, 0).packet); - } - } - - public void onCreateFileRequestResponse(CreateFileResponseMessage responseMessage) { - if (currentlyUploadingItem == null) { - LOG.error("Create file request response arrived, but nothing is being uploaded"); - return; - } - if (!currentlyUploadingItem.create) { - LOG.error("Create file request response arrived, but nothing should have been created"); - return; - } - - if (responseMessage.status == VivomoveConstants.STATUS_ACK && responseMessage.response == CreateFileResponseMessage.RESPONSE_FILE_CREATED_SUCCESSFULLY) { - LOG.info("Received successful response for create file request of '{}' ({}/{}/{}; {} B) -> #{}", currentlyUploadingItem.targetPath, currentlyUploadingItem.dataType, currentlyUploadingItem.subType, currentlyUploadingItem.fileIdentifier, currentlyUploadingItem.fileSize, responseMessage.fileIndex); - currentlyUploadingItem.fileIndex = responseMessage.fileIndex; - communicator.sendMessage(new UploadRequestMessage(currentlyUploadingItem.fileIndex, 0, DownloadRequestMessage.REQUEST_NEW_TRANSFER, 0).packet); - } else { - LOG.error("Received error response for upload request request of '{}' ({}/{}/{}; {} B): {}, {}", currentlyUploadingItem.targetPath, currentlyUploadingItem.dataType, currentlyUploadingItem.subType, currentlyUploadingItem.fileIdentifier, currentlyUploadingItem.fileSize, responseMessage.status, responseMessage.response); - totalRemainingBytes -= currentlyUploadingItem.fileSize; - currentlyUploadingItem = null; - checkStartNextUpload(); - } - } - - public void onUploadRequestResponse(UploadRequestResponseMessage responseMessage) { - if (currentlyUploadingItem == null) { - LOG.error("Upload request response arrived, but nothing is being uploaded"); - return; - } - - if (responseMessage.status == VivomoveConstants.STATUS_ACK && responseMessage.response == UploadRequestResponseMessage.RESPONSE_UPLOAD_REQUEST_OKAY) { - LOG.info("Received successful response for upload request of {}: {}/{} (max {}B)", currentlyUploadingItem.fileIndex, responseMessage.status, responseMessage.response, responseMessage.maxFileSize); - currentCrc = responseMessage.crcSeed; - } else { - LOG.error("Received error response for upload request of {}: {}/{}", currentlyUploadingItem.fileIndex, responseMessage.status, responseMessage.response); - totalRemainingBytes -= currentlyUploadingItem.fileSize; - currentlyUploadingItem = null; - checkStartNextUpload(); - } - } - - public void onFileTransferResponse(FileTransferDataResponseMessage dataResponseMessage) { - final QueueItem currentlyUploadingItem = this.currentlyUploadingItem; - if (currentlyUploadingItem == null) { - LOG.error("Upload request response arrived, but nothing is being uploaded"); - return; - } - - if (dataResponseMessage.status == VivomoveConstants.STATUS_ACK && dataResponseMessage.response == FileTransferDataResponseMessage.RESPONSE_TRANSFER_SUCCESSFUL) { - int nextOffset = currentlyUploadingItem.dataOffset + currentlyUploadingItem.blockSize; - if (dataResponseMessage.nextDataOffset != nextOffset) { - LOG.warn("Bad expected data offset of #{}: {} expected, {} received", currentlyUploadingItem.fileIndex, currentlyUploadingItem.dataOffset, dataResponseMessage.nextDataOffset); - communicator.sendMessage(new FileTransferDataResponseMessage(VivomoveConstants.STATUS_ACK, FileTransferDataResponseMessage.RESPONSE_ERROR_DATA_OFFSET_MISMATCH, currentlyUploadingItem.dataOffset).packet); - return; - } - - if (nextOffset >= currentlyUploadingItem.fileSize) { - LOG.info("Transfer of file #{} complete, {}/{}B uploaded", currentlyUploadingItem.fileIndex, nextOffset, currentlyUploadingItem.fileSize); - this.currentlyUploadingItem = null; - checkStartNextUpload(); - return; - } - - // prepare next block - final int blockSize = Math.min(currentlyUploadingItem.fileSize - nextOffset, MAX_BLOCK_SIZE); - currentlyUploadingItem.dataOffset = nextOffset; - currentlyUploadingItem.blockSize = blockSize; - final byte[] blockData = Arrays.copyOfRange(currentlyUploadingItem.data, nextOffset, blockSize); - final int blockCrc = ChecksumCalculator.computeCrc(currentCrc, blockData, 0, blockSize); - currentlyUploadingItem.blockCrc = blockCrc; - - LOG.info("Sending {}B@{}/{} of {}", blockSize, currentlyUploadingItem.dataOffset, currentlyUploadingItem.fileSize, currentlyUploadingItem.fileIndex); - communicator.sendMessage(new FileTransferDataMessage(UPLOAD_FLAGS, blockCrc, currentlyUploadingItem.dataOffset, blockData).packet); - } else { - // TODO: Solve individual responses - LOG.error("Received error response for data transfer of {}: {}/{}", currentlyUploadingItem.fileIndex, dataResponseMessage.status, dataResponseMessage.response); - // ??!? - cancelAllUploads(); - } - } - - private boolean isIdle() { - return currentlyUploadingItem == null; - } - - private static class QueueItem { - public final boolean create; - public final int fileSize; - public final int dataType; - public final int subType; - public final int fileIdentifier; - public final String targetPath; - public final byte[] data; - - public int fileIndex; - public int dataOffset; - public int blockSize; - public int blockCrc; - - public QueueItem(int fileSize, int dataType, int subType, int fileIdentifier, String targetPath, byte[] data) { - this.create = true; - this.fileSize = fileSize; - this.dataType = dataType; - this.subType = subType; - this.fileIdentifier = fileIdentifier; - this.targetPath = targetPath; - this.data = data; - } - - public QueueItem(int fileSize, int fileIndex, byte[] data) { - this.create = false; - this.fileSize = fileSize; - this.fileIndex = fileIndex; - this.data = data; - this.dataType = 0; - this.subType = 0; - this.fileIdentifier = 0; - this.targetPath = null; - } - } -}