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 3cbd02070..fc4a45ac5 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 @@ -240,8 +240,7 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni } protected Bitmap getNotificationAttachmentBitmap(int notificationId) { - Bitmap pippo = BitmapFactory.decodeFile(getNotificationAttachmentPath(notificationId)); - return pippo; + return BitmapFactory.decodeFile(getNotificationAttachmentPath(notificationId)); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/NotificationsHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/NotificationsHandler.java index 4e429d8b5..84d1ee776 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/NotificationsHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/NotificationsHandler.java @@ -117,7 +117,7 @@ public class NotificationsHandler implements MessageHandler { } } - final boolean hasPicture = !nodomain.freeyourgadget.gadgetbridge.util.StringUtils.isEmpty(notificationSpec.picturePath); + final boolean hasPicture = !StringUtils.isEmpty(notificationSpec.picturePath); return new NotificationUpdateMessage(notificationUpdateType, notificationSpec.type, getNotificationsCount(notificationSpec.type), notificationSpec.getId(), hasActions, hasPicture); } @@ -349,8 +349,8 @@ public class NotificationsHandler implements MessageHandler { toReturn = encodeNotificationActionsString(notificationSpec); break; case ATTACHMENTS: - LOG.debug("NOTIFICATION ATTACHMENTS REQUESTED. Notification Id: {}", notificationSpec.getId()); - toReturn = "1"; //TODO: possibly the number of attachments, or is it a progressive ID of the attachment to be requested? + LOG.debug("Notification attachments requested for notification id {}", notificationSpec.getId()); + toReturn = "1"; // the number of attachments break; } if (maxLength == 0) 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 566b88c11..72f9ee57b 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,6 +1,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin; import android.content.Intent; +import android.graphics.Bitmap; import android.location.Location; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -11,6 +12,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -32,6 +34,7 @@ import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiDataTransferService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiDeviceStatus; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiFindMyWatch; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiHttpService; +import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiNotificationsService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSettingsService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSmartProto; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSmsNotification; @@ -129,6 +132,9 @@ public class ProtocolBufferHandler implements MessageHandler { processed = true; processProtobufSettingsService(smart.getSettingsService()); } + if (smart.hasNotificationsService()) { + return prepareProtobufResponse(processProtobufNotificationsServiceMessage(smart.getNotificationsService()), message.getRequestId()); + } if (processed) { message.setStatusMessage(new ProtobufStatusMessage( message.getMessageType(), @@ -358,6 +364,47 @@ public class ProtocolBufferHandler implements MessageHandler { return null; } + private GdiSmartProto.Smart processProtobufNotificationsServiceMessage(final GdiNotificationsService.NotificationsService notificationsService) { + if (notificationsService.hasPictureRequest()) { + final GdiNotificationsService.PictureRequest pictureRequest = notificationsService.getPictureRequest(); + final int notificationId = pictureRequest.getNotificationId(); + final Bitmap bmp = deviceSupport.getNotificationAttachmentBitmap(notificationId); + if (bmp == null) { + LOG.warn("Failed to find bitmap for notification id {}", notificationId); + return null; + } + + final GdiNotificationsService.PictureParameters parameters = pictureRequest.getParameters(); + final int targetHeight = (int) Math.round(bmp.getHeight() * ((double) parameters.getWidth() / bmp.getWidth())); + + final Bitmap scaledBmp = Bitmap.createScaledBitmap(bmp, parameters.getWidth(), targetHeight, true); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + scaledBmp.compress(Bitmap.CompressFormat.JPEG, parameters.getQuality(), baos); + final byte[] imageBytes = baos.toByteArray(); + + final int transferId = DataTransferHandler.registerData(imageBytes); + + final GdiNotificationsService.PictureResponse response = GdiNotificationsService.PictureResponse.newBuilder() + .setUnk1(1) + .setNotificationId(notificationId) + .setUnk3(0) + .setUnk4(1) + .setDataTransferItem( + GdiNotificationsService.DataTransferItem.newBuilder() + .setId(transferId) + .setSize(imageBytes.length) + .build() + ) + .build(); + return GdiSmartProto.Smart.newBuilder().setNotificationsService( + GdiNotificationsService.NotificationsService.newBuilder().setPictureResponse(response) + ).build(); + } + + LOG.warn("Protobuf notificationsService request not implemented: {}", notificationsService); + return null; + } + private GdiSmartProto.Smart processProtobufSmsNotificationMessage(GdiSmsNotification.SmsNotificationService smsNotificationService) { if (smsNotificationService.hasSmsCannedListRequest()) { LOG.debug("Got request for sms canned list"); diff --git a/app/src/main/proto/garmin/gdi_notifications_service.proto b/app/src/main/proto/garmin/gdi_notifications_service.proto new file mode 100644 index 000000000..03235cf90 --- /dev/null +++ b/app/src/main/proto/garmin/gdi_notifications_service.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + +package garmin_vivomovehr; + +option java_package = "nodomain.freeyourgadget.gadgetbridge.proto.garmin"; + +message NotificationsService { + optional PictureRequest pictureRequest = 1; + optional PictureResponse pictureResponse = 2; +} + +message PictureRequest { + optional uint32 notification_id = 1; + optional uint32 unk2 = 2; // 0 + optional PictureParameters parameters = 3; + // unk4 "\001" +} + +message PictureParameters { + optional uint32 width = 1; + optional uint32 height = 2; + optional uint32 unk3 = 3; // 204800 max size? + optional uint32 quality = 4; // 80 +} + +message PictureResponse { + optional uint32 unk1 = 1; // 1 + optional uint32 notification_id = 2; + optional uint32 unk3 = 3; // 0 + optional uint32 unk4 = 4; // 1 + optional DataTransferItem dataTransferItem = 5; +} + +message DataTransferItem { + optional uint32 id = 1; + optional uint32 size = 2; +} diff --git a/app/src/main/proto/garmin/gdi_smart_proto.proto b/app/src/main/proto/garmin/gdi_smart_proto.proto index 218592db3..4d823a61c 100644 --- a/app/src/main/proto/garmin/gdi_smart_proto.proto +++ b/app/src/main/proto/garmin/gdi_smart_proto.proto @@ -12,6 +12,7 @@ import "garmin/gdi_data_transfer_service.proto"; import "garmin/gdi_sms_notification.proto"; import "garmin/gdi_calendar_service.proto"; import "garmin/gdi_settings_service.proto"; +import "garmin/gdi_notifications_service.proto"; message Smart { optional CalendarService calendar_service = 1; @@ -22,6 +23,7 @@ message Smart { optional CoreService core_service = 13; optional SmsNotificationService sms_notification_service = 16; optional SettingsService settings_service = 42; + optional NotificationsService notifications_service = 49; } /*