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 extends ScanFilter> 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 extends Activity> getPairingActivity() {
- return null;
- }
-
- @Override
- public boolean supportsActivityDataFetching() {
- return true;
- }
-
- @Override
- public boolean supportsActivityTracking() {
- return true;
- }
-
- @Override
- public SampleProvider extends ActivitySample> 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 extends Activity> 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 extends DeviceSupport> 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