mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Garmin: Add custom notification actions
This commit is contained in:
parent
7893ace986
commit
965e22bf34
@ -217,6 +217,12 @@ public class DebugActivity extends AbstractGBActivity {
|
|||||||
notificationSpec.pebbleColor = notificationSpec.type.color;
|
notificationSpec.pebbleColor = notificationSpec.type.color;
|
||||||
notificationSpec.attachedActions = new ArrayList<>();
|
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) {
|
if (notificationSpec.type == NotificationType.GENERIC_SMS) {
|
||||||
// REPLY action
|
// REPLY action
|
||||||
NotificationSpec.Action replyAction = new NotificationSpec.Action();
|
NotificationSpec.Action replyAction = new NotificationSpec.Action();
|
||||||
|
@ -46,6 +46,7 @@ import android.service.notification.NotificationListenerService;
|
|||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.core.app.RemoteInput;
|
import androidx.core.app.RemoteInput;
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
@ -114,7 +115,7 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
public static final String ACTION_REPLY
|
public static final String ACTION_REPLY
|
||||||
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply";
|
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply";
|
||||||
|
|
||||||
private final LimitedQueue<Integer, NotificationCompat.Action> mActionLookup = new LimitedQueue<>(32);
|
private final LimitedQueue<Integer, NotificationAction> mActionLookup = new LimitedQueue<>(32);
|
||||||
private final LimitedQueue<Integer, String> mPackageLookup = new LimitedQueue<>(64);
|
private final LimitedQueue<Integer, String> mPackageLookup = new LimitedQueue<>(64);
|
||||||
private final LimitedQueue<Integer, Long> mNotificationHandleLookup = new LimitedQueue<>(128);
|
private final LimitedQueue<Integer, Long> mNotificationHandleLookup = new LimitedQueue<>(128);
|
||||||
|
|
||||||
@ -220,31 +221,36 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
NotificationListener.this.cancelAllNotifications();
|
NotificationListener.this.cancelAllNotifications();
|
||||||
break;
|
break;
|
||||||
case ACTION_REPLY:
|
case ACTION_REPLY:
|
||||||
NotificationCompat.Action wearableAction = mActionLookup.lookup(handle);
|
NotificationAction wearableAction = mActionLookup.lookup(handle);
|
||||||
String reply = intent.getStringExtra("reply");
|
String reply = intent.getStringExtra("reply");
|
||||||
if (wearableAction != null) {
|
if (wearableAction != null) {
|
||||||
PendingIntent actionIntent = wearableAction.getActionIntent();
|
PendingIntent actionIntent = wearableAction.getIntent();
|
||||||
if (actionIntent == null) {
|
if (actionIntent == null) {
|
||||||
LOG.warn("Action intent is null");
|
LOG.warn("Action intent is null");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Intent localIntent = new Intent();
|
|
||||||
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
final RemoteInput remoteInput = wearableAction.getRemoteInput();
|
||||||
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);
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
LOG.info("will send exec intent to remote application");
|
LOG.info("Will send exec intent to remote application");
|
||||||
actionIntent.send(context, 0, localIntent);
|
|
||||||
|
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 {
|
||||||
|
actionIntent.send();
|
||||||
|
}
|
||||||
mActionLookup.remove(handle);
|
mActionLookup.remove(handle);
|
||||||
} catch (final PendingIntent.CanceledException e) {
|
} catch (final PendingIntent.CanceledException e) {
|
||||||
LOG.warn("replyToLastNotification error", e);
|
LOG.warn("replyToLastNotification error", e);
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
break;
|
||||||
}
|
}
|
||||||
@ -431,11 +437,11 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification);
|
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification);
|
||||||
List<NotificationCompat.Action> actions = wearableExtender.getActions();
|
List<NotificationCompat.Action> wearableActions = wearableExtender.getActions();
|
||||||
|
|
||||||
// Some apps such as Telegram send both a group + normal notifications, which would get sent in duplicate to the devices
|
// 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
|
// 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
|
&& !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);
|
LOG.info("Not forwarding notification, FLAG_GROUP_SUMMARY is set and no wearable action present. Notification flags: " + notification.flags);
|
||||||
return;
|
return;
|
||||||
@ -450,19 +456,51 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
dismissAction.type = NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS;
|
dismissAction.type = NotificationSpec.Action.TYPE_SYNTECTIC_DISMISS;
|
||||||
notificationSpec.attachedActions.add(dismissAction);
|
notificationSpec.attachedActions.add(dismissAction);
|
||||||
|
|
||||||
for (NotificationCompat.Action act : actions) {
|
boolean hasWearableActions = false;
|
||||||
|
for (NotificationCompat.Action act : wearableActions) {
|
||||||
if (act != null) {
|
if (act != null) {
|
||||||
NotificationSpec.Action wearableAction = new NotificationSpec.Action();
|
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) {
|
if (act.getRemoteInputs() != null && act.getRemoteInputs().length > 0) {
|
||||||
wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_REPLY;
|
wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_REPLY;
|
||||||
|
remoteInput = act.getRemoteInputs()[0];
|
||||||
} else {
|
} else {
|
||||||
wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_SIMPLE;
|
wearableAction.type = NotificationSpec.Action.TYPE_WEARABLE_SIMPLE;
|
||||||
|
remoteInput = null;
|
||||||
}
|
}
|
||||||
notificationSpec.attachedActions.add(wearableAction);
|
notificationSpec.attachedActions.add(wearableAction);
|
||||||
wearableAction.handle = (notificationSpec.getId() << 4) + notificationSpec.attachedActions.size();
|
wearableAction.handle = ((long) notificationSpec.getId() << 4) + notificationSpec.attachedActions.size();
|
||||||
mActionLookup.add((int)wearableAction.handle, act);
|
mActionLookup.add((int) wearableAction.handle, new NotificationAction(act.getActionIntent(), remoteInput));
|
||||||
LOG.info("Found wearable action: {} - {} {}", notificationSpec.attachedActions.size(), act.getTitle(), sbn.getTag());
|
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, sbn.getTag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1118,4 +1156,23 @@ public class NotificationListener extends NotificationListenerService {
|
|||||||
|
|
||||||
return PebbleUtils.getPebbleColor(iconPrimaryColor);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,9 +83,15 @@ public class NotificationSpec {
|
|||||||
public static final int TYPE_SYNTECTIC_DISMISS_ALL = 4;
|
public static final int TYPE_SYNTECTIC_DISMISS_ALL = 4;
|
||||||
public static final int TYPE_SYNTECTIC_MUTE = 5;
|
public static final int TYPE_SYNTECTIC_MUTE = 5;
|
||||||
public static final int TYPE_SYNTECTIC_OPEN = 6;
|
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 int type = TYPE_UNDEFINED;
|
||||||
public long handle;
|
public long handle;
|
||||||
public String title;
|
public String title;
|
||||||
|
|
||||||
|
public boolean isReply() {
|
||||||
|
return type == TYPE_WEARABLE_REPLY || type == TYPE_SYNTECTIC_REPLY_PHONENR || type == TYPE_CUSTOM_REPLY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -522,10 +522,10 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
|||||||
deviceEvent.phoneNumber = GBApplication.getIDSenderLookup().lookup((int) (deviceEvent.handle >> 4));
|
deviceEvent.phoneNumber = GBApplication.getIDSenderLookup().lookup((int) (deviceEvent.handle >> 4));
|
||||||
}
|
}
|
||||||
if (deviceEvent.phoneNumber != null) {
|
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);
|
SmsManager.getDefault().sendTextMessage(deviceEvent.phoneNumber, null, deviceEvent.reply, null, null);
|
||||||
} else {
|
} 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;
|
action = NotificationListener.ACTION_REPLY;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1467,7 +1467,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
if (notificationSpec.attachedActions!=null)
|
if (notificationSpec.attachedActions!=null)
|
||||||
for (int i=0;i<notificationSpec.attachedActions.size();i++) {
|
for (int i=0;i<notificationSpec.attachedActions.size();i++) {
|
||||||
NotificationSpec.Action action = notificationSpec.attachedActions.get(i);
|
NotificationSpec.Action action = notificationSpec.attachedActions.get(i);
|
||||||
if (action.type==NotificationSpec.Action.TYPE_WEARABLE_REPLY) {
|
if (action.isReply()) {
|
||||||
mNotificationReplyAction.add(notificationSpec.getId(), action.handle);
|
mNotificationReplyAction.add(notificationSpec.getId(), action.handle);
|
||||||
canReply = true;
|
canReply = true;
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,30 @@ public class NotificationsHandler implements MessageHandler {
|
|||||||
deviceEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.MUTE;
|
deviceEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.MUTE;
|
||||||
message.addGbDeviceEvent(deviceEvtNotificationControl);
|
message.addGbDeviceEvent(deviceEvtNotificationControl);
|
||||||
break;
|
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;
|
break;
|
||||||
case TITLE:
|
case TITLE:
|
||||||
if (NotificationType.GENERIC_SMS.equals(notificationSpec.type))
|
if (NotificationType.GENERIC_SMS.equals(notificationSpec.type))
|
||||||
toReturn = notificationSpec.sender == null ? "" : notificationSpec.sender;
|
toReturn = StringUtils.firstNonBlank(notificationSpec.sender, notificationSpec.phoneNumber, "-");
|
||||||
else
|
else
|
||||||
toReturn = notificationSpec.title == null ? "" : notificationSpec.title;
|
toReturn = notificationSpec.title == null ? "" : notificationSpec.title;
|
||||||
break;
|
break;
|
||||||
@ -368,9 +392,12 @@ public class NotificationsHandler implements MessageHandler {
|
|||||||
garminActions.add(encodeNotificationAction(NotificationAction.ACCEPT_INCOMING_CALL, " ")); //text is not shown on watch
|
garminActions.add(encodeNotificationAction(NotificationAction.ACCEPT_INCOMING_CALL, " ")); //text is not shown on watch
|
||||||
}
|
}
|
||||||
if (null != notificationSpec.attachedActions) {
|
if (null != notificationSpec.attachedActions) {
|
||||||
|
int customActionsIdx = 1;
|
||||||
|
|
||||||
for (NotificationSpec.Action action : notificationSpec.attachedActions) {
|
for (NotificationSpec.Action action : notificationSpec.attachedActions) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case NotificationSpec.Action.TYPE_WEARABLE_REPLY:
|
case NotificationSpec.Action.TYPE_WEARABLE_REPLY:
|
||||||
|
case NotificationSpec.Action.TYPE_CUSTOM_REPLY:
|
||||||
case NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR:
|
case NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR:
|
||||||
garminActions.add(encodeNotificationAction(NotificationAction.REPLY_MESSAGES, action.title));
|
garminActions.add(encodeNotificationAction(NotificationAction.REPLY_MESSAGES, action.title));
|
||||||
break;
|
break;
|
||||||
@ -380,11 +407,20 @@ public class NotificationsHandler implements MessageHandler {
|
|||||||
case NotificationSpec.Action.TYPE_SYNTECTIC_MUTE:
|
case NotificationSpec.Action.TYPE_SYNTECTIC_MUTE:
|
||||||
garminActions.add(encodeNotificationAction(NotificationAction.BLOCK_APPLICATION, action.title));
|
garminActions.add(encodeNotificationAction(NotificationAction.BLOCK_APPLICATION, action.title));
|
||||||
break;
|
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);
|
// LOG.info("Notification has action {} with title {}", action.type, action.title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (garminActions.isEmpty())
|
if (garminActions.isEmpty())
|
||||||
return new String(new byte[]{0x00, 0x00, 0x00, 0x00});
|
return new String(new byte[]{0x00, 0x00, 0x00, 0x00});
|
||||||
|
|
||||||
@ -414,6 +450,11 @@ public class NotificationsHandler implements MessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum NotificationAction {
|
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_INCOMING_CALL(94, NotificationActionIconPosition.BOTTOM),
|
||||||
REPLY_MESSAGES(95, NotificationActionIconPosition.BOTTOM),
|
REPLY_MESSAGES(95, NotificationActionIconPosition.BOTTOM),
|
||||||
ACCEPT_INCOMING_CALL(96, NotificationActionIconPosition.RIGHT),
|
ACCEPT_INCOMING_CALL(96, NotificationActionIconPosition.RIGHT),
|
||||||
|
@ -343,14 +343,10 @@ public class ZeppOsNotificationService extends AbstractZeppOsService {
|
|||||||
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
||||||
final NotificationSpec.Action action = notificationSpec.attachedActions.get(i);
|
final NotificationSpec.Action action = notificationSpec.attachedActions.get(i);
|
||||||
|
|
||||||
switch (action.type) {
|
if (action.isReply()) {
|
||||||
case NotificationSpec.Action.TYPE_WEARABLE_REPLY:
|
hasReply = true;
|
||||||
case NotificationSpec.Action.TYPE_SYNTECTIC_REPLY_PHONENR:
|
mNotificationReplyAction.add(notificationSpec.getId(), action.handle);
|
||||||
hasReply = true;
|
break;
|
||||||
mNotificationReplyAction.add(notificationSpec.getId(), action.handle);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ public class HuaweiNotificationsManager {
|
|||||||
if (hasActions) {
|
if (hasActions) {
|
||||||
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
||||||
final NotificationSpec.Action action = notificationSpec.attachedActions.get(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
|
deviceEvtNotificationControl.handle = action.handle; //handle of wearable action is needed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public class SendNotificationRequest extends Request {
|
|||||||
if (hasActions) {
|
if (hasActions) {
|
||||||
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
for (int i = 0; i < notificationSpec.attachedActions.size(); i++) {
|
||||||
final NotificationSpec.Action action = notificationSpec.attachedActions.get(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
|
//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);
|
replyKey = getNotificationKey(notificationSpec);
|
||||||
break;
|
break;
|
||||||
|
@ -840,7 +840,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
for (Action act : attachedActions) {
|
for (Action act : attachedActions) {
|
||||||
actions_count++;
|
actions_count++;
|
||||||
actions_length += (short) (ACTION_LENGTH_MIN + act.title.getBytes().length);
|
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)
|
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));
|
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) 0x03); // reply action
|
||||||
buf.put((byte) 0x02); // number attributes
|
buf.put((byte) 0x02); // number attributes
|
||||||
} else {
|
} else {
|
||||||
@ -970,7 +970,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
|||||||
buf.put((byte) 0x01); // attribute id (title)
|
buf.put((byte) 0x01); // attribute id (title)
|
||||||
buf.putShort((short) act.title.getBytes().length);
|
buf.putShort((short) act.title.getBytes().length);
|
||||||
buf.put(act.title.getBytes());
|
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.put((byte) 0x08); // canned replies
|
||||||
buf.putShort((short) replies_length);
|
buf.putShort((short) replies_length);
|
||||||
if (cannedReplies != null && cannedReplies.length > 0) {
|
if (cannedReplies != null && cannedReplies.length > 0) {
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user