Garmin: initial plumbing for notifications' images (blind implementation)

This commit adds the signaling of notifications with images to the watch.
According to https://codeberg.org/Freeyourgadget/Gadgetbridge/issues/3469 the watch should followup with a protobuf request "MediaRequest".
The protobuf is documented in https://gadgetbridge.org/internals/specifics/garmin-protocol/#mediarequest

As examples and only intended to help further developments, two new methods getNotificationAttachmentPath and getNotificationAttachmentBitmap are added to GarminSupport
This commit is contained in:
Daniele Gobbetti 2024-05-04 17:39:18 +02:00
parent 272b45a196
commit 71799a2484
3 changed files with 32 additions and 39 deletions

View File

@ -2,6 +2,8 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.net.Uri;
import android.widget.Toast;
@ -228,6 +230,15 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni
}
protected String getNotificationAttachmentPath(int notificationId) {
return notificationsHandler.getNotificationAttachmentPath(notificationId);
}
protected Bitmap getNotificationAttachmentBitmap(int notificationId) {
Bitmap pippo = BitmapFactory.decodeFile(getNotificationAttachmentPath(notificationId));
return pippo;
}
@Override
public void onSetCallState(CallSpec callSpec) {
LOG.info("INCOMING CALLSPEC: {}", callSpec.command);

View File

@ -117,7 +117,9 @@ public class NotificationsHandler implements MessageHandler {
}
}
}
return new NotificationUpdateMessage(notificationUpdateType, notificationSpec.type, getNotificationsCount(notificationSpec.type), notificationSpec.getId(), hasActions);
final boolean hasPicture = !nodomain.freeyourgadget.gadgetbridge.util.StringUtils.isEmpty(notificationSpec.picturePath);
return new NotificationUpdateMessage(notificationUpdateType, notificationSpec.type, getNotificationsCount(notificationSpec.type), notificationSpec.getId(), hasActions, hasPicture);
}
private int getNotificationsCount(NotificationType notificationType) {
@ -137,6 +139,13 @@ public class NotificationsHandler implements MessageHandler {
return null;
}
public String getNotificationAttachmentPath(int notificationId) {
NotificationSpec notificationSpec = getNotificationSpecFromQueue(notificationId);
if (null != notificationSpec)
return notificationSpec.picturePath;
return null;
}
public NotificationUpdateMessage onDeleteNotification(int id) {
if (!enabled)
return null;
@ -146,7 +155,7 @@ public class NotificationsHandler implements MessageHandler {
NotificationSpec e = iterator.next();
if (e.getId() == id) {
iterator.remove();
return new NotificationUpdateMessage(NotificationUpdateMessage.NotificationUpdateType.REMOVE, e.type, getNotificationsCount(e.type), id, false);
return new NotificationUpdateMessage(NotificationUpdateMessage.NotificationUpdateType.REMOVE, e.type, getNotificationsCount(e.type), id, false, false);
}
}
return null;
@ -278,6 +287,7 @@ public class NotificationsHandler implements MessageHandler {
// Garmin extensions
// PHONE_NUMBER(126, true),
ACTIONS(127, false, true),
ATTACHMENTS(128),
;
private static final SparseArray<NotificationAttribute> valueByCode;
@ -339,6 +349,10 @@ public class NotificationsHandler implements MessageHandler {
case ACTIONS:
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?
break;
}
if (maxLength == 0)
return toReturn.getBytes(StandardCharsets.UTF_8);

View File

@ -13,15 +13,17 @@ public class NotificationUpdateMessage extends GFDIMessage {
final private int count; //how many notifications of the same type are present
final private int notificationId;
final private boolean hasActions;
final private boolean hasPicture;
final private boolean useLegacyActions = false;
public NotificationUpdateMessage(NotificationUpdateType notificationUpdateType, NotificationType notificationType, int count, int notificationId, boolean hasActions) {
public NotificationUpdateMessage(NotificationUpdateType notificationUpdateType, NotificationType notificationType, int count, int notificationId, boolean hasActions, boolean hasPicture) {
this.garminMessage = GarminMessage.NOTIFICATION_UPDATE;
this.notificationUpdateType = notificationUpdateType;
this.notificationType = notificationType;
this.count = count;
this.notificationId = notificationId;
this.hasActions = hasActions;
this.hasPicture = hasPicture;
}
@Override
@ -45,47 +47,13 @@ public class NotificationUpdateMessage extends GFDIMessage {
flags.add(NotificationPhoneFlags.NEW_ACTIONS);
if (this.useLegacyActions)
flags.add(NotificationPhoneFlags.LEGACY_ACTIONS);
if (this.hasPicture)
flags.add(NotificationPhoneFlags.HAS_ATTACHMENTS);
return (int) EnumUtils.generateBitVector(NotificationPhoneFlags.class, flags);
}
//no image
//00 updatetype
// 12 flags
// 00 notif type
// 00 count
// 03000000
// 02
//image
//00
// 12
// 00
// 00
// 04000000
// 06
//0F00
// A913
// 00
// 12
// 0C
// 00
// 471D2A66
// 02
// BC14
//0F00
// A913
// 00
// 11
// 00
// 00
// 461D2A66
// 00
// 8C00
private int getCategoryFlags(NotificationType notificationType) {
EnumSet<NotificationFlag> flags = EnumSet.noneOf(NotificationFlag.class);
if (this.hasActions && this.useLegacyActions) { //only needed for legacy actions