diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java index 1f01c9dae..0a6ee13db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java @@ -30,6 +30,7 @@ public enum DeviceSpecificSettingsScreen { DEVELOPER("pref_screen_developer", R.xml.devicesettings_root_developer), DISPLAY("pref_screen_display", R.xml.devicesettings_root_display), GENERIC("pref_screen_generic", R.xml.devicesettings_root_generic), + LOCATION("pref_screen_location", R.xml.devicesettings_root_location), NOTIFICATIONS("pref_screen_notifications", R.xml.devicesettings_root_notifications), DATE_TIME("pref_screen_date_time", R.xml.devicesettings_root_date_time), WORKOUT("pref_screen_workout", R.xml.devicesettings_root_workout), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java index 316b8c713..ee906741e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminCoordinator.java @@ -38,10 +38,8 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator { public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); - final List connection = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CONNECTION); - connection.add(R.xml.devicesettings_high_mtu); - final List notifications = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CALLS_AND_NOTIFICATIONS); + notifications.add(R.xml.devicesettings_send_app_notifications); if (getCannedRepliesSlotCount(device) > 0) { @@ -50,6 +48,12 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator { notifications.add(R.xml.devicesettings_canned_dismisscall_16); } + final List location = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.LOCATION); + location.add(R.xml.devicesettings_workout_send_gps_to_band); + + final List connection = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CONNECTION); + connection.add(R.xml.devicesettings_high_mtu); + final List developer = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DEVELOPER); developer.add(R.xml.devicesettings_keep_activity_data_on_device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java index 5485569c3..47605e416 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java @@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; +import android.location.Location; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminPreferences; import nodomain.freeyourgadget.gadgetbridge.devices.vivomovehr.GarminCapability; +import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationService; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; @@ -34,6 +36,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.Weather; 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.GdiSettingsService; @@ -98,8 +101,10 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni @Override public void dispose() { - super.dispose(); + LOG.info("Garmin dispose()"); + GBLocationService.stop(getContext(), getDevice()); stopMusicTimer(); + super.dispose(); } private void stopMusicTimer() { @@ -598,5 +603,18 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni return dir; } + @Override + public void onSetGpsLocation(final Location location) { + final GdiCore.CoreService.LocationUpdatedNotification.Builder locationUpdatedNotification = GdiCore.CoreService.LocationUpdatedNotification.newBuilder() + .addLocationData( + GarminUtils.toLocationData(location, GdiCore.CoreService.DataType.REALTIME_TRACKING) + ); + final ProtobufMessage locationUpdatedNotificationRequest = protocolBufferHandler.prepareProtobufRequest( + GdiSmartProto.Smart.newBuilder().setCoreService( + GdiCore.CoreService.newBuilder().setLocationUpdatedNotification(locationUpdatedNotification) + ).build() + ); + sendOutgoingMessage(locationUpdatedNotificationRequest); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminUtils.java new file mode 100644 index 000000000..c218f2c95 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminUtils.java @@ -0,0 +1,35 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin; + +import android.location.Location; +import android.os.Build; + +import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiCore; + +public final class GarminUtils { + private GarminUtils() { + // utility class + } + + public static GdiCore.CoreService.LocationData toLocationData(final Location location, final GdiCore.CoreService.DataType dataType) { + final GdiCore.CoreService.LatLon positionForWatch = GdiCore.CoreService.LatLon.newBuilder() + .setLat((int) ((location.getLatitude() * 2.147483648E9d) / 180.0d)) + .setLon((int) ((location.getLongitude() * 2.147483648E9d) / 180.0d)) + .build(); + + float vAccuracy = 0; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vAccuracy = location.getVerticalAccuracyMeters(); + } + + return GdiCore.CoreService.LocationData.newBuilder() + .setPosition(positionForWatch) + .setAltitude((float) location.getAltitude()) + .setTimestamp(GarminTimeUtils.javaMillisToGarminTimestamp(location.getTime())) + .setHAccuracy(location.getAccuracy()) + .setVAccuracy(vAccuracy) + .setPositionType(dataType) + .setBearing(location.getBearing()) + .setSpeed(location.getSpeed()) + .build(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java index 0d8dc5922..20d7927ad 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java @@ -1,5 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin; +import android.location.Location; + import com.google.protobuf.InvalidProtocolBufferException; import org.apache.commons.lang3.ArrayUtils; @@ -13,9 +15,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventUpdatePreferences; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminPreferences; +import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationProviderType; +import nodomain.freeyourgadget.gadgetbridge.externalevents.gps.GBLocationService; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiCalendarService; import nodomain.freeyourgadget.gadgetbridge.proto.vivomovehr.GdiCore; @@ -28,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.http.HttpHand import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.GFDIMessage; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.ProtobufMessage; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.status.ProtobufStatusMessage; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.CurrentPosition; import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent; import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager; @@ -76,9 +82,7 @@ public class ProtocolBufferHandler implements MessageHandler { } boolean processed = false; if (smart.hasCoreService()) { //TODO: unify request and response??? - processed = true; - processProtobufCoreResponse(smart.getCoreService()); -// return prepareProtobufResponse(processProtobufCoreRequest(smart.getCoreService()), message.getRequestId()); + return prepareProtobufResponse(processProtobufCoreRequest(smart.getCoreService()), message.getRequestId()); } if (smart.hasCalendarService()) { return prepareProtobufResponse(processProtobufCalendarRequest(smart.getCalendarService()), message.getRequestId()); @@ -203,14 +207,6 @@ public class ProtocolBufferHandler implements MessageHandler { ).build(); } - 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(); @@ -229,32 +225,81 @@ public class ProtocolBufferHandler implements MessageHandler { LOG.warn("Unknown DeviceStatusService response: {}", deviceStatusService); } -// private GdiSmartProto.Smart processProtobufCoreRequest(GdiCore.CoreService coreService) { -// if (coreService.hasLocationUpdatedSetEnabledRequest()) { //TODO: enable location support in devicesupport -// LOG.debug("Location CoreService: {}", coreService); -// -// final GdiCore.CoreService.LocationUpdatedSetEnabledRequest locationUpdatedSetEnabledRequest = coreService.getLocationUpdatedSetEnabledRequest(); -// -// LOG.info("Received locationUpdatedSetEnabledRequest status: {}", locationUpdatedSetEnabledRequest.getEnabled()); -// -// GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Builder response = GdiCore.CoreService.LocationUpdatedSetEnabledResponse.newBuilder() -// .setStatus(GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Status.OK); -// -// //TODO: check and follow the preference in coordinator (see R.xml.devicesettings_workout_send_gps_to_band ) -// if(locationUpdatedSetEnabledRequest.getEnabled()) { -// response.addRequests(GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.newBuilder() -// .setRequested(locationUpdatedSetEnabledRequest.getRequests(0).getRequested()) -// .setStatus(GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.RequestedStatus.OK)); -// } -// -// deviceSupport.processLocationUpdateRequest(locationUpdatedSetEnabledRequest.getEnabled(), locationUpdatedSetEnabledRequest.getRequestsList()); -// -// return GdiSmartProto.Smart.newBuilder().setCoreService( -// GdiCore.CoreService.newBuilder().setLocationUpdatedSetEnabledResponse(response)).build(); -// } -// LOG.warn("Unknown CoreService request: {}", coreService); -// return null; -// } + private GdiSmartProto.Smart processProtobufCoreRequest(GdiCore.CoreService coreService) { + if (coreService.hasSyncResponse()) { + final GdiCore.CoreService.SyncResponse syncResponse = coreService.getSyncResponse(); + LOG.info("Received sync status: {}", syncResponse.getStatus()); + return null; + } + + if (coreService.hasGetLocationRequest()) { + LOG.info("Got location request"); + final Location location = new CurrentPosition().getLastKnownLocation(); + final GdiCore.CoreService.GetLocationResponse.Builder response = GdiCore.CoreService.GetLocationResponse.newBuilder(); + if (location.getLatitude() == 0 && location.getLongitude() == 0) { + response.setStatus(GdiCore.CoreService.GetLocationResponse.Status.NO_VALID_LOCATION); + } else { + response.setStatus(GdiCore.CoreService.GetLocationResponse.Status.OK) + .setLocationData(GarminUtils.toLocationData(location, GdiCore.CoreService.DataType.GENERAL_LOCATION)); + } + return GdiSmartProto.Smart.newBuilder().setCoreService( + GdiCore.CoreService.newBuilder().setGetLocationResponse(response)).build(); + } + + if (coreService.hasLocationUpdatedSetEnabledRequest()) { + final GdiCore.CoreService.LocationUpdatedSetEnabledRequest locationUpdatedSetEnabledRequest = coreService.getLocationUpdatedSetEnabledRequest(); + + LOG.info("Received locationUpdatedSetEnabledRequest status: {}", locationUpdatedSetEnabledRequest.getEnabled()); + + GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Builder response = GdiCore.CoreService.LocationUpdatedSetEnabledResponse.newBuilder() + .setStatus(GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Status.OK); + + final boolean sendGpsPref = deviceSupport.getDevicePrefs().getBoolean(DeviceSettingsPreferenceConst.PREF_WORKOUT_SEND_GPS_TO_BAND, false); + + GdiCore.CoreService.Request realtimeRequest = null; + + if (locationUpdatedSetEnabledRequest.getEnabled()) { + for (final GdiCore.CoreService.Request request : locationUpdatedSetEnabledRequest.getRequestsList()) { + final GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.RequestedStatus requestedStatus; + if (GdiCore.CoreService.DataType.REALTIME_TRACKING.equals(request.getRequested())) { + realtimeRequest = request; + if (sendGpsPref) { + requestedStatus = GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.RequestedStatus.OK; + } else { + requestedStatus = GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.RequestedStatus.KO; + } + } else { + requestedStatus = GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.RequestedStatus.KO; + } + + response.addRequests( + GdiCore.CoreService.LocationUpdatedSetEnabledResponse.Requested.newBuilder() + .setRequested(request.getRequested()) + .setStatus(requestedStatus) + ); + } + } + + if (sendGpsPref) { + if (realtimeRequest != null) { + GBLocationService.start( + deviceSupport.getContext(), + deviceSupport.getDevice(), + GBLocationProviderType.GPS, + 1000 // TODO from realtimeRequest + ); + } else { + GBLocationService.stop(deviceSupport.getContext(), deviceSupport.getDevice()); + } + } + + return GdiSmartProto.Smart.newBuilder().setCoreService( + GdiCore.CoreService.newBuilder().setLocationUpdatedSetEnabledResponse(response)).build(); + } + + LOG.warn("Unknown CoreService request: {}", coreService); + return null; + } private GdiSmartProto.Smart processProtobufSmsNotificationMessage(GdiSmsNotification.SmsNotificationService smsNotificationService) { if (smsNotificationService.hasSmsCannedListRequest()) { diff --git a/app/src/main/proto/garmin_vivomovehr/gdi_core.proto b/app/src/main/proto/garmin_vivomovehr/gdi_core.proto index c53c24ce4..ab43f8b55 100644 --- a/app/src/main/proto/garmin_vivomovehr/gdi_core.proto +++ b/app/src/main/proto/garmin_vivomovehr/gdi_core.proto @@ -70,12 +70,11 @@ message CoreService { optional Status status = 1; repeated Requested requests = 2; - enum Status { - OK = 1; - UNAVAILABLE = 2; - UNKNOWN3 = 3; - UNKNOWN4 = 4; + OK = 1; + UNAVAILABLE = 2; + UNKNOWN3 = 3; + UNKNOWN4 = 4; } message Requested { @@ -83,8 +82,8 @@ message CoreService { optional RequestedStatus status = 2; enum RequestedStatus { - OK = 1; - KO = 2; + OK = 1; + KO = 2; } } diff --git a/app/src/main/res/xml/devicesettings_root_location.xml b/app/src/main/res/xml/devicesettings_root_location.xml new file mode 100644 index 000000000..2a936edaa --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_location.xml @@ -0,0 +1,9 @@ + + + + +