From 32721afffed4eee2c6224aea1aac8a5f550c5744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sun, 12 Jan 2025 18:00:44 +0000 Subject: [PATCH] Garmin: Add custom notification actions --- .../activities/DebugActivity.java | 6 + .../externalevents/NotificationListener.java | 107 ++++++++++++++---- .../gadgetbridge/model/NotificationSpec.java | 6 + .../service/AbstractDeviceSupport.java | 4 +- .../devices/garmin/NotificationsHandler.java | 49 +++++++- .../services/ZeppOsNotificationService.java | 12 +- .../huawei/HuaweiNotificationsManager.java | 2 +- .../requests/SendNotificationRequest.java | 2 +- .../devices/pebble/PebbleProtocol.java | 6 +- .../garmin/messages/GFDIMessageTest.java | 19 ++++ 10 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/GFDIMessageTest.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java index b1242ccd0..290031e13 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -217,6 +217,12 @@ public class DebugActivity extends AbstractGBActivity { notificationSpec.pebbleColor = notificationSpec.type.color; notificationSpec.attachedActions = new ArrayList<>(); + // DISMISS action + NotificationSpec.Action dismissAction = new NotificationSpec.Action(); + dismissAction.title = "Dismiss"; + dismissAction.type = NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS; + notificationSpec.attachedActions.add(dismissAction); + if (notificationSpec.type == NotificationType.GENERIC_SMS) { // REPLY action NotificationSpec.Action replyAction = new NotificationSpec.Action(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java index 4ba7d90a4..503ebca10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/NotificationListener.java @@ -22,6 +22,7 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.externalevents; +import android.app.ActivityOptions; import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -46,6 +47,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.app.RemoteInput; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -114,7 +116,7 @@ public class NotificationListener extends NotificationListenerService { public static final String ACTION_REPLY = "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply"; - private final LimitedQueue mActionLookup = new LimitedQueue<>(32); + private final LimitedQueue mActionLookup = new LimitedQueue<>(128); private final LimitedQueue mPackageLookup = new LimitedQueue<>(64); private final LimitedQueue mNotificationHandleLookup = new LimitedQueue<>(128); @@ -220,31 +222,43 @@ public class NotificationListener extends NotificationListenerService { NotificationListener.this.cancelAllNotifications(); break; case ACTION_REPLY: - NotificationCompat.Action wearableAction = mActionLookup.lookup(handle); + NotificationAction wearableAction = mActionLookup.lookup(handle); String reply = intent.getStringExtra("reply"); if (wearableAction != null) { - PendingIntent actionIntent = wearableAction.getActionIntent(); + PendingIntent actionIntent = wearableAction.getIntent(); if (actionIntent == null) { LOG.warn("Action intent is null"); break; } - Intent localIntent = new Intent(); - localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (wearableAction.getRemoteInputs() != null && wearableAction.getRemoteInputs().length > 0) { - RemoteInput[] remoteInputs = wearableAction.getRemoteInputs(); - Bundle extras = new Bundle(); - extras.putCharSequence(remoteInputs[0].getResultKey(), reply); - RemoteInput.addResultsToIntent(remoteInputs, localIntent, extras); - } + + final RemoteInput remoteInput = wearableAction.getRemoteInput(); + try { - LOG.info("will send exec intent to remote application"); - actionIntent.send(context, 0, localIntent); + LOG.info("Will send exec intent to remote application"); + + if (remoteInput != null) { + final Intent localIntent = new Intent(); + localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + final Bundle extras = new Bundle(); + extras.putCharSequence(remoteInput.getResultKey(), reply); + RemoteInput.addResultsToIntent(new RemoteInput[]{remoteInput}, localIntent, extras); + actionIntent.send(context, 0, localIntent); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + final ActivityOptions activityOptions = ActivityOptions.makeBasic(); + final Bundle bundle = activityOptions.setPendingIntentBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) + .toBundle(); + actionIntent.send(bundle); + } else { + actionIntent.send(); + } + } mActionLookup.remove(handle); } catch (final PendingIntent.CanceledException e) { LOG.warn("replyToLastNotification error", e); } } else { - LOG.warn("Received ACTION_REPLY but cannot find the corresponding wearableAction"); + LOG.warn("Received ACTION_REPLY for handle {}, but cannot find the corresponding wearableAction", handle); } break; } @@ -431,11 +445,11 @@ public class NotificationListener extends NotificationListenerService { } NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification); - List actions = wearableExtender.getActions(); + List wearableActions = wearableExtender.getActions(); // Some apps such as Telegram send both a group + normal notifications, which would get sent in duplicate to the devices // Others only send the group summary, so they need to be whitelisted - if (actions.isEmpty() && NotificationCompat.isGroupSummary(notification) + if (wearableActions.isEmpty() && NotificationCompat.isGroupSummary(notification) && !GROUP_SUMMARY_WHITELIST.contains(source)) { //this could cause #395 to come back LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set and no wearable action present. Notification flags: " + notification.flags); return; @@ -450,19 +464,51 @@ public class NotificationListener extends NotificationListenerService { dismissAction.type = NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS; notificationSpec.attachedActions.add(dismissAction); - for (NotificationCompat.Action act : actions) { + boolean hasWearableActions = false; + for (NotificationCompat.Action act : wearableActions) { if (act != null) { NotificationSpec.Action wearableAction = new NotificationSpec.Action(); - wearableAction.title = act.getTitle().toString(); + wearableAction.title = String.valueOf(act.getTitle()); + final RemoteInput remoteInput; if (act.getRemoteInputs() != null && act.getRemoteInputs().length > 0) { wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_REPLY; + remoteInput = act.getRemoteInputs()[0]; } else { wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_SIMPLE; + remoteInput = null; } notificationSpec.attachedActions.add(wearableAction); - wearableAction.handle = (notificationSpec.getId() << 4) + notificationSpec.attachedActions.size(); - mActionLookup.add((int)wearableAction.handle, act); - LOG.info("Found wearable action: {} - {} {}", notificationSpec.attachedActions.size(), act.getTitle(), sbn.getTag()); + wearableAction.handle = ((long) notificationSpec.getId() << 4) + notificationSpec.attachedActions.size(); + mActionLookup.add((int) wearableAction.handle, new NotificationAction(act.getActionIntent(), remoteInput)); + LOG.debug("Found wearable action {}: {} - {} {}", notificationSpec.attachedActions.size(), (int) wearableAction.handle, act.getTitle(), sbn.getTag()); + hasWearableActions = true; + } + } + + if (!hasWearableActions && notification.actions != null) { + // If no wearable actions are sent, fallback to normal custom actions + for (final Notification.Action act : notification.actions) { + final NotificationSpec.Action customAction = new NotificationSpec.Action(); + customAction.title = String.valueOf(act.title); + final RemoteInput remoteInput; + if (act.getRemoteInputs() != null && act.getRemoteInputs().length > 0) { + customAction.type = NotificationSpec.Action.TYPE_CUSTOM_REPLY; + android.app.RemoteInput ri = act.getRemoteInputs()[0]; + // FIXME this is not very clean + remoteInput = new RemoteInput.Builder(ri.getResultKey()) + .setLabel(ri.getLabel()) + .setChoices(ri.getChoices()) + .setAllowFreeFormInput(ri.getAllowFreeFormInput()) + .addExtras(ri.getExtras()) + .build(); + } else { + customAction.type = NotificationSpec.Action.TYPE_CUSTOM_SIMPLE; + remoteInput = null; + } + notificationSpec.attachedActions.add(customAction); + customAction.handle = ((long) notificationSpec.getId() << 4) + notificationSpec.attachedActions.size(); + mActionLookup.add((int) customAction.handle, new NotificationAction(act.actionIntent, remoteInput)); + LOG.info("Found custom action {}: {} - {}", notificationSpec.attachedActions.size(), (int) customAction.handle, act.title); } } @@ -1118,4 +1164,23 @@ public class NotificationListener extends NotificationListenerService { return PebbleUtils.getPebbleColor(iconPrimaryColor); } + + private static class NotificationAction { + private final PendingIntent intent; + @Nullable + private final RemoteInput remoteInput; + + private NotificationAction(final PendingIntent pendingIntent, @Nullable final RemoteInput remoteInput) { + this.intent = pendingIntent; + this.remoteInput = remoteInput; + } + + public PendingIntent getIntent() { + return intent; + } + + public RemoteInput getRemoteInput() { + return remoteInput; + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java index 30061ff3d..30034561e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java @@ -83,9 +83,15 @@ public class NotificationSpec { public static final int TYPE_SYNTECTIC_DISMISS_ALL = 4; public static final int TYPE_SYNTECTIC_MUTE = 5; public static final int TYPE_SYNTECTIC_OPEN = 6; + public static final int TYPE_CUSTOM_SIMPLE = 7; + public static final int TYPE_CUSTOM_REPLY = 8; public int type = TYPE_UNDEFINED; public long handle; public String title; + + public boolean isReply() { + return type == TYPE_WEARABLE_REPLY || type == TYPE_SYNTECTIC_REPLY_PHONENR || type == TYPE_CUSTOM_REPLY; + } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index a079eb84f..e0a5e984a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -522,10 +522,10 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { deviceEvent.phoneNumber = GBApplication.getIDSenderLookup().lookup((int) (deviceEvent.handle >> 4)); } if (deviceEvent.phoneNumber != null) { - LOG.info("Got notification reply for SMS from " + deviceEvent.phoneNumber + " : " + deviceEvent.reply); + LOG.info("Got notification reply for SMS from {} : {}", deviceEvent.phoneNumber, deviceEvent.reply); SmsManager.getDefault().sendTextMessage(deviceEvent.phoneNumber, null, deviceEvent.reply, null, null); } else { - LOG.info("Got notification reply for notification id " + deviceEvent.handle + " : " + deviceEvent.reply); + LOG.info("Got notification reply for notification id {} : {}", deviceEvent.handle, deviceEvent.reply); action = NotificationListener.ACTION_REPLY; } break; 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 de2aebeed..969a9e817 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 @@ -42,7 +42,7 @@ public class NotificationsHandler implements MessageHandler { private boolean enabled = false; // Keep track of Notification ID -> action handle, as BangleJSDeviceSupport. // TODO: This needs to be simplified. - private final LimitedQueue mNotificationReplyAction = new LimitedQueue<>(16); + private final LimitedQueue mNotificationReplyAction = new LimitedQueue<>(32); public NotificationsHandler() { @@ -111,7 +111,7 @@ public class NotificationsHandler implements MessageHandler { for (int i = 0; i < notificationSpec.attachedActions.size(); i++) { final NotificationSpec.Action action = notificationSpec.attachedActions.get(i); - if (action.type == NotificationSpec.Action.TYPE_WEARABLE_REPLY || action.type == NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (action.isReply()) { mNotificationReplyAction.add(notificationSpec.getId(), action.handle); } } @@ -222,6 +222,30 @@ public class NotificationsHandler implements MessageHandler { deviceEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.MUTE; message.addGbDeviceEvent(deviceEvtNotificationControl); break; + case CUSTOM_ACTION_1: + case CUSTOM_ACTION_2: + case CUSTOM_ACTION_3: + case CUSTOM_ACTION_4: + case CUSTOM_ACTION_5: + deviceEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY; + + // We need to map back to the handle of the action - the custom actions are added in order + final int customActionIndex = message.getNotificationAction().ordinal(); + int i = 0; + for (NotificationSpec.Action attachedAction : notificationSpec.attachedActions) { + if (attachedAction.type == NotificationSpec.Action.TYPE_WEARABLE_SIMPLE || attachedAction.type == NotificationSpec.Action.TYPE_CUSTOM_SIMPLE) { + if (i == customActionIndex) { + deviceEvtNotificationControl.handle = attachedAction.handle; + break; + } else { + i++; + } + } + } + message.addGbDeviceEvent(deviceEvtNotificationControl); + break; + default: + LOG.warn("Unknown notification action {}", message.getNotificationAction()); } } @@ -330,7 +354,7 @@ public class NotificationsHandler implements MessageHandler { break; case TITLE: if (NotificationType.GENERIC_SMS.equals(notificationSpec.type)) - toReturn = notificationSpec.sender == null ? "" : notificationSpec.sender; + toReturn = StringUtils.firstNonBlank(notificationSpec.sender, notificationSpec.phoneNumber, "-"); else toReturn = notificationSpec.title == null ? "" : notificationSpec.title; break; @@ -368,9 +392,12 @@ public class NotificationsHandler implements MessageHandler { garminActions.add(encodeNotificationAction(NotificationAction.ACCEPT_INCOMING_CALL, " ")); //text is not shown on watch } if (null != notificationSpec.attachedActions) { + int customActionsIdx = 1; + for (NotificationSpec.Action action : notificationSpec.attachedActions) { switch (action.type) { case NotificationSpec.Action.TYPE_WEARABLE_REPLY: + case NotificationSpec.Action.TYPE_CUSTOM_REPLY: case NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR: garminActions.add(encodeNotificationAction(NotificationAction.REPLY_MESSAGES, action.title)); break; @@ -380,11 +407,20 @@ public class NotificationsHandler implements MessageHandler { case NotificationSpec.Action.TYPE_SYNTECTIC_MUTE: garminActions.add(encodeNotificationAction(NotificationAction.BLOCK_APPLICATION, action.title)); break; - + case NotificationSpec.Action.TYPE_WEARABLE_SIMPLE: + case NotificationSpec.Action.TYPE_CUSTOM_SIMPLE: + if (customActionsIdx <= 5) { + garminActions.add(encodeNotificationAction(NotificationAction.valueOf("CUSTOM_ACTION_" + customActionsIdx), action.title)); + } else { + LOG.error("Too many custom actions!"); + } + customActionsIdx++; + break; } // LOG.info("Notification has action {} with title {}", action.type, action.title); } } + if (garminActions.isEmpty()) return new String(new byte[]{0x00, 0x00, 0x00, 0x00}); @@ -414,6 +450,11 @@ public class NotificationsHandler implements MessageHandler { } public enum NotificationAction { + CUSTOM_ACTION_1(1, null), + CUSTOM_ACTION_2(2, null), + CUSTOM_ACTION_3(3, null), + CUSTOM_ACTION_4(4, null), + CUSTOM_ACTION_5(5, null), REPLY_INCOMING_CALL(94, NotificationActionIconPosition.BOTTOM), REPLY_MESSAGES(95, NotificationActionIconPosition.BOTTOM), ACCEPT_INCOMING_CALL(96, NotificationActionIconPosition.RIGHT), diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/services/ZeppOsNotificationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/services/ZeppOsNotificationService.java index 15d658f3c..30231b5d0 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/services/ZeppOsNotificationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/zeppos/services/ZeppOsNotificationService.java @@ -343,14 +343,10 @@ public class ZeppOsNotificationService extends AbstractZeppOsService { for (int i = 0; i < notificationSpec.attachedActions.size(); i++) { final NotificationSpec.Action action = notificationSpec.attachedActions.get(i); - switch (action.type) { - case NotificationSpec.Action.TYPE_WEARABLE_REPLY: - case NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR: - hasReply = true; - mNotificationReplyAction.add(notificationSpec.getId(), action.handle); - break; - default: - break; + if (action.isReply()) { + hasReply = true; + mNotificationReplyAction.add(notificationSpec.getId(), action.handle); + break; } } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiNotificationsManager.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiNotificationsManager.java index b7b1202eb..b666cf345 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiNotificationsManager.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/HuaweiNotificationsManager.java @@ -141,7 +141,7 @@ public class HuaweiNotificationsManager { if (hasActions) { for (int i = 0; i < notificationSpec.attachedActions.size(); i++) { final NotificationSpec.Action action = notificationSpec.attachedActions.get(i); - if (action.type == NotificationSpec.Action.TYPE_WEARABLE_REPLY || action.type == NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (action.isReply()) { deviceEvtNotificationControl.handle = action.handle; //handle of wearable action is needed break; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendNotificationRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendNotificationRequest.java index 03a0e3198..61fa5f334 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendNotificationRequest.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huawei/requests/SendNotificationRequest.java @@ -72,7 +72,7 @@ public class SendNotificationRequest extends Request { if (hasActions) { for (int i = 0; i < notificationSpec.attachedActions.size(); i++) { final NotificationSpec.Action action = notificationSpec.attachedActions.get(i); - if (action.type == NotificationSpec.Action.TYPE_WEARABLE_REPLY || action.type == NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (action.isReply()) { //NOTE: store notification key instead action key. The watch returns this key so it is more easier to find action by notification key replyKey = getNotificationKey(notificationSpec); break; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java index 7de164f0f..e65baa894 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pebble/PebbleProtocol.java @@ -840,7 +840,7 @@ public class PebbleProtocol extends GBDeviceProtocol { for (Action act : attachedActions) { actions_count++; actions_length += (short) (ACTION_LENGTH_MIN + act.title.getBytes().length); - if (act.type == Action.TYPE_WEARABLE_REPLY || act.type == Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (act.isReply()) { actions_length += (short) replies_length + 3; // 3 = attribute id (byte) + length(short) } } @@ -955,7 +955,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put((byte) (0x05 + ai)); } - if (act.type == Action.TYPE_WEARABLE_REPLY || act.type == Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (act.isReply()) { buf.put((byte) 0x03); // reply action buf.put((byte) 0x02); // number attributes } else { @@ -970,7 +970,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.put((byte) 0x01); // attribute id (title) buf.putShort((short) act.title.getBytes().length); buf.put(act.title.getBytes()); - if (act.type == Action.TYPE_WEARABLE_REPLY || act.type == Action.TYPE_SYNTECTIC_REPLY_PHONENR) { + if (act.isReply()) { buf.put((byte) 0x08); // canned replies buf.putShort((short) replies_length); if (cannedReplies != null && cannedReplies.length > 0) { diff --git a/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/GFDIMessageTest.java b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/GFDIMessageTest.java new file mode 100644 index 000000000..d1ac88bef --- /dev/null +++ b/app/src/test/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/messages/GFDIMessageTest.java @@ -0,0 +1,19 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages; + +import junit.framework.TestCase; + +import org.junit.Test; + +import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.communicator.CobsCoDec; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class GFDIMessageTest extends TestCase { + @Test + @Ignore("helper test for development, remove this while debugging") + public void testDecode() { + final CobsCoDec cobsCoDec = new CobsCoDec(); + cobsCoDec.receivedBytes(GB.hexStringToByteArray("00020c0baa1380a4bd796705196600")); + final byte[] deCobs = cobsCoDec.retrieveMessage(); + final GFDIMessage gfdiMessage = GFDIMessage.parseIncoming(deCobs); + } +} \ No newline at end of file