mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-24 19:06:53 +01:00
Various improvements and bugfixes to notification handling
Prevent duplicate notifications with a dedicated data structure (not reusing the anti-burst one) #1062, #657 Pebble: Forward the actions attached to notifications (not only reply) inspired by the work of dnastase #705
This commit is contained in:
parent
b9999edf2a
commit
eede85a9c9
@ -122,7 +122,6 @@ public class DebugActivity extends AbstractGBActivity {
|
||||
notificationSpec.subject = testString;
|
||||
notificationSpec.type = NotificationType.values()[sendTypeSpinner.getSelectedItemPosition()];
|
||||
notificationSpec.pebbleColor = notificationSpec.type.color;
|
||||
notificationSpec.id = -1;
|
||||
GBApplication.deviceService().onNotification(notificationSpec);
|
||||
}
|
||||
});
|
||||
|
@ -20,6 +20,7 @@ public class GBDeviceEventNotificationControl extends GBDeviceEvent {
|
||||
public int handle;
|
||||
public String phoneNumber;
|
||||
public String reply;
|
||||
public String title;
|
||||
public Event event = Event.UNKNOWN;
|
||||
|
||||
public enum Event {
|
||||
|
@ -64,10 +64,10 @@ public class AlarmClockReceiver extends BroadcastReceiver {
|
||||
private synchronized void sendAlarm(boolean on) {
|
||||
dismissLastAlarm();
|
||||
if (on) {
|
||||
lastId = generateId();
|
||||
NotificationSpec spec = new NotificationSpec();
|
||||
//TODO: can we attach a dismiss action to the notification and not use the notification ID explicitly?
|
||||
lastId = spec.getId();
|
||||
spec.type = NotificationType.GENERIC_ALARM_CLOCK;
|
||||
spec.id = lastId;
|
||||
spec.sourceName = "ALARMCLOCKRECEIVER";
|
||||
// can we get the alarm title somehow?
|
||||
GBApplication.deviceService().onNotification(spec);
|
||||
@ -81,8 +81,4 @@ public class AlarmClockReceiver extends BroadcastReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
private int generateId() {
|
||||
// lacks negative values, but should be sufficient
|
||||
return (int) (Math.random() * Integer.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ import android.support.v7.graphics.Palette;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -87,7 +88,8 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
private LimitedQueue mActionLookup = new LimitedQueue(16);
|
||||
|
||||
private HashMap<String, Long> notificationTimes = new HashMap<>();
|
||||
private HashMap<String, Long> notificationBurstPrevention = new HashMap<>();
|
||||
private HashMap<String, Long> notificationOldRepeatPrevention = new HashMap<>();
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
|
||||
@ -145,19 +147,20 @@ public class NotificationListener extends NotificationListenerService {
|
||||
break;
|
||||
case ACTION_REPLY:
|
||||
int id = intent.getIntExtra("handle", -1);
|
||||
NotificationCompat.Action wearableAction = (NotificationCompat.Action) mActionLookup.lookup(id);
|
||||
String reply = intent.getStringExtra("reply");
|
||||
NotificationCompat.Action replyAction = (NotificationCompat.Action) mActionLookup.lookup(id);
|
||||
if (replyAction != null && replyAction.getRemoteInputs() != null) {
|
||||
RemoteInput[] remoteInputs = replyAction.getRemoteInputs();
|
||||
PendingIntent actionIntent = replyAction.getActionIntent();
|
||||
if (wearableAction != null) {
|
||||
PendingIntent actionIntent = wearableAction.getActionIntent();
|
||||
Intent localIntent = new Intent();
|
||||
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Bundle extras = new Bundle();
|
||||
extras.putCharSequence(remoteInputs[0].getResultKey(), reply);
|
||||
RemoteInput.addResultsToIntent(remoteInputs, localIntent, extras);
|
||||
|
||||
if(wearableAction.getRemoteInputs()!=null) {
|
||||
RemoteInput[] remoteInputs = wearableAction.getRemoteInputs();
|
||||
Bundle extras = new Bundle();
|
||||
extras.putCharSequence(remoteInputs[0].getResultKey(), reply);
|
||||
RemoteInput.addResultsToIntent(remoteInputs, localIntent, extras);
|
||||
}
|
||||
try {
|
||||
LOG.info("will send reply intent to remote application");
|
||||
LOG.info("will send exec intent to remote application");
|
||||
actionIntent.send(context, 0, localIntent);
|
||||
mActionLookup.remove(id);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
@ -209,15 +212,13 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
String source = sbn.getPackageName().toLowerCase();
|
||||
Notification notification = sbn.getNotification();
|
||||
if (notificationTimes.containsKey(source)) {
|
||||
long last_time = notificationTimes.get(source);
|
||||
if (notification.when <= last_time) {
|
||||
LOG.info("NOT processing notification, too old. notification.when: " + notification.when + " last notification for this source: " + last_time);
|
||||
if (notificationOldRepeatPrevention.containsKey(source)) {
|
||||
if (notification.when <= notificationOldRepeatPrevention.get(source)) {
|
||||
LOG.info("NOT processing notification, already sent newer notifications from this source.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
NotificationSpec notificationSpec = new NotificationSpec();
|
||||
notificationSpec.id = (int) sbn.getPostTime(); //FIXME: a truly unique id would be better
|
||||
|
||||
// determinate Source App Name ("Label")
|
||||
PackageManager pm = getPackageManager();
|
||||
@ -249,7 +250,7 @@ public class NotificationListener extends NotificationListenerService {
|
||||
// Get color
|
||||
notificationSpec.pebbleColor = getPebbleColorForNotification(notificationSpec);
|
||||
|
||||
LOG.info("Processing notification " + notificationSpec.id + " from source " + source + " with flags: " + notification.flags);
|
||||
LOG.info("Processing notification " + notificationSpec.getId() + " age: " + (System.currentTimeMillis() - notification.when) + " from source " + source + " with flags: " + notification.flags);
|
||||
|
||||
dissectNotificationTo(notification, notificationSpec, preferBigText);
|
||||
|
||||
@ -263,16 +264,23 @@ public class NotificationListener extends NotificationListenerService {
|
||||
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification);
|
||||
List<NotificationCompat.Action> actions = wearableExtender.getActions();
|
||||
|
||||
notificationSpec.attachedActions = new ArrayList<>();
|
||||
for (NotificationCompat.Action act : actions) {
|
||||
if (act != null && act.getRemoteInputs() != null) {
|
||||
LOG.info("found wearable action: " + act.getTitle() + " " + sbn.getTag());
|
||||
mActionLookup.add(notificationSpec.id, act);
|
||||
notificationSpec.flags |= NotificationSpec.FLAG_WEARABLE_REPLY;
|
||||
break;
|
||||
if (act != null) {
|
||||
NotificationSpec.Action wearableAction = new NotificationSpec.Action();
|
||||
wearableAction.title = act.getTitle().toString();
|
||||
if(act.getRemoteInputs()!=null) {
|
||||
wearableAction.isReply = true;
|
||||
}
|
||||
notificationSpec.flags |= NotificationSpec.FLAG_WEARABLE_ACTIONS;
|
||||
notificationSpec.attachedActions.add(wearableAction);
|
||||
mActionLookup.add((notificationSpec.getId()<<4) + notificationSpec.attachedActions.size(), act);
|
||||
LOG.info("found wearable action: " + notificationSpec.attachedActions.size() + " - "+ act.getTitle() + " " + sbn.getTag());
|
||||
}
|
||||
}
|
||||
|
||||
if ((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) == 0 && NotificationCompat.isGroupSummary(notification)) { //this could cause #395 to come back
|
||||
|
||||
if ((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_ACTIONS) == 0 && NotificationCompat.isGroupSummary(notification)) { //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;
|
||||
}
|
||||
@ -280,14 +288,15 @@ public class NotificationListener extends NotificationListenerService {
|
||||
// Ignore too frequent notifications, according to user preference
|
||||
long min_timeout = prefs.getInt("notifications_timeout", 0) * 1000;
|
||||
long cur_time = System.currentTimeMillis();
|
||||
if (notificationTimes.containsKey(source)) {
|
||||
long last_time = notificationTimes.get(source);
|
||||
if (notificationBurstPrevention.containsKey(source)) {
|
||||
long last_time = notificationBurstPrevention.get(source);
|
||||
if (cur_time - last_time < min_timeout) {
|
||||
LOG.info("Ignoring frequent notification, last one was " + (cur_time - last_time) + "ms ago");
|
||||
return;
|
||||
}
|
||||
}
|
||||
notificationTimes.put(source, cur_time);
|
||||
notificationBurstPrevention.put(source, cur_time);
|
||||
notificationOldRepeatPrevention.put(source, notification.when);
|
||||
|
||||
GBApplication.deviceService().onNotification(notificationSpec);
|
||||
}
|
||||
|
@ -62,7 +62,6 @@ public class PebbleReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
NotificationSpec notificationSpec = new NotificationSpec();
|
||||
notificationSpec.id = -1;
|
||||
|
||||
String notificationData = intent.getStringExtra("notificationData");
|
||||
try {
|
||||
|
@ -50,7 +50,6 @@ public class SMSReceiver extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
NotificationSpec notificationSpec = new NotificationSpec();
|
||||
notificationSpec.id = -1;
|
||||
notificationSpec.type = NotificationType.GENERIC_SMS;
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
|
@ -150,8 +150,9 @@ public class GBDeviceService implements DeviceService {
|
||||
.putExtra(EXTRA_NOTIFICATION_SUBJECT, notificationSpec.subject)
|
||||
.putExtra(EXTRA_NOTIFICATION_TITLE, notificationSpec.title)
|
||||
.putExtra(EXTRA_NOTIFICATION_BODY, notificationSpec.body)
|
||||
.putExtra(EXTRA_NOTIFICATION_ID, notificationSpec.id)
|
||||
.putExtra(EXTRA_NOTIFICATION_ID, notificationSpec.getId())
|
||||
.putExtra(EXTRA_NOTIFICATION_TYPE, notificationSpec.type)
|
||||
.putExtra(EXTRA_NOTIFICATION_ACTIONS, notificationSpec.attachedActions)
|
||||
.putExtra(EXTRA_NOTIFICATION_SOURCENAME, notificationSpec.sourceName)
|
||||
.putExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR, notificationSpec.pebbleColor)
|
||||
.putExtra(EXTRA_NOTIFICATION_SOURCEAPPID, notificationSpec.sourceAppId);
|
||||
|
@ -77,6 +77,7 @@ public interface DeviceService extends EventHandler {
|
||||
String EXTRA_NOTIFICATION_SUBJECT = "notification_subject";
|
||||
String EXTRA_NOTIFICATION_TITLE = "notification_title";
|
||||
String EXTRA_NOTIFICATION_TYPE = "notification_type";
|
||||
String EXTRA_NOTIFICATION_ACTIONS = "notification_actions";
|
||||
String EXTRA_NOTIFICATION_PEBBLE_COLOR = "notification_pebble_color";
|
||||
String EXTRA_FIND_START = "find_start";
|
||||
String EXTRA_VIBRATION_INTENSITY = "vibration_intensity";
|
||||
|
@ -16,11 +16,16 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class NotificationSpec {
|
||||
public static final int FLAG_WEARABLE_REPLY = 0x00000001;
|
||||
public static final int FLAG_WEARABLE_ACTIONS = 0x00000001;
|
||||
|
||||
public int flags;
|
||||
public int id;
|
||||
private static final AtomicInteger c = new AtomicInteger((int) (System.currentTimeMillis()/1000));
|
||||
private int id;
|
||||
public String sender;
|
||||
public String phoneNumber;
|
||||
public String title;
|
||||
@ -29,7 +34,10 @@ public class NotificationSpec {
|
||||
public NotificationType type;
|
||||
public String sourceName;
|
||||
public String[] cannedReplies;
|
||||
|
||||
/**
|
||||
* Wearable actions that were attached to the incoming notifications and will be passed to the gadget (includes the "reply" action)
|
||||
*/
|
||||
public ArrayList<Action> attachedActions;
|
||||
/**
|
||||
* The application that generated the notification.
|
||||
*/
|
||||
@ -39,4 +47,24 @@ public class NotificationSpec {
|
||||
* The color that should be assigned to this notification when displayed on a Pebble
|
||||
*/
|
||||
public byte pebbleColor;
|
||||
|
||||
public NotificationSpec() {
|
||||
this.id = c.incrementAndGet();
|
||||
}
|
||||
|
||||
public NotificationSpec(int id) {
|
||||
if (id != -1)
|
||||
this.id = id;
|
||||
else
|
||||
this.id = c.incrementAndGet();
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static class Action implements Serializable {
|
||||
public boolean isReply = false;
|
||||
public String title;
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,8 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventDisplayMessage;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFmFrequency;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFmFrequency;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventLEDColor;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
|
||||
@ -337,6 +337,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
if (action != null) {
|
||||
Intent notificationListenerIntent = new Intent(action);
|
||||
notificationListenerIntent.putExtra("handle", deviceEvent.handle);
|
||||
notificationListenerIntent.putExtra("title", deviceEvent.title);
|
||||
if (deviceEvent.reply != null) {
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
String suffix = prefs.getString("canned_reply_suffix", null);
|
||||
|
@ -147,6 +147,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUS
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACK;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKCOUNT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACKNR;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_ACTIONS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_BODY;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_FLAGS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_ID;
|
||||
@ -356,7 +357,8 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
mGBDevice.sendDeviceUpdateIntent(this);
|
||||
break;
|
||||
case ACTION_NOTIFICATION: {
|
||||
NotificationSpec notificationSpec = new NotificationSpec();
|
||||
int desiredId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||
NotificationSpec notificationSpec = new NotificationSpec(desiredId);
|
||||
notificationSpec.phoneNumber = intent.getStringExtra(EXTRA_NOTIFICATION_PHONENUMBER);
|
||||
notificationSpec.sender = intent.getStringExtra(EXTRA_NOTIFICATION_SENDER);
|
||||
notificationSpec.subject = intent.getStringExtra(EXTRA_NOTIFICATION_SUBJECT);
|
||||
@ -364,17 +366,17 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
|
||||
notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
||||
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
|
||||
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
|
||||
notificationSpec.attachedActions = (ArrayList<NotificationSpec.Action>) intent.getSerializableExtra(EXTRA_NOTIFICATION_ACTIONS);
|
||||
notificationSpec.pebbleColor = (byte) intent.getSerializableExtra(EXTRA_NOTIFICATION_PEBBLE_COLOR);
|
||||
notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||
notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
||||
notificationSpec.sourceAppId = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCEAPPID);
|
||||
|
||||
if (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null) {
|
||||
notificationSpec.id = mRandom.nextInt(); // FIXME: add this in external SMS Receiver?
|
||||
GBApplication.getIDSenderLookup().add(notificationSpec.id, notificationSpec.phoneNumber);
|
||||
GBApplication.getIDSenderLookup().add(notificationSpec.getId(), notificationSpec.phoneNumber);
|
||||
}
|
||||
|
||||
if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) > 0)
|
||||
//TODO: check if at least one of the attached actions is a reply action instead?
|
||||
if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_ACTIONS) > 0)
|
||||
|| (notificationSpec.type == NotificationType.GENERIC_SMS && notificationSpec.phoneNumber != null)) {
|
||||
// NOTE: maybe not where it belongs
|
||||
if (prefs.getBoolean("pebble_force_untested", false)) {
|
||||
|
@ -60,6 +60,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec.Action;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
@ -484,8 +485,9 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
|
||||
@Override
|
||||
public byte[] encodeNotification(NotificationSpec notificationSpec) {
|
||||
boolean hasHandle = notificationSpec.id != -1 && notificationSpec.phoneNumber == null;
|
||||
int id = notificationSpec.id != -1 ? notificationSpec.id : mRandom.nextInt();
|
||||
//TODO: simplify this logic? is hasHandle still needed?
|
||||
boolean hasHandle = notificationSpec.getId() != -1 && notificationSpec.phoneNumber == null;
|
||||
int id = notificationSpec.getId() != -1 ? notificationSpec.getId() : mRandom.nextInt();
|
||||
String title;
|
||||
String subtitle = null;
|
||||
|
||||
@ -507,11 +509,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
// 3.x notification
|
||||
return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body,
|
||||
notificationSpec.sourceName, hasHandle, notificationSpec.type, notificationSpec.pebbleColor,
|
||||
notificationSpec.cannedReplies);
|
||||
notificationSpec.cannedReplies, notificationSpec.attachedActions);
|
||||
} else if (mForceProtocol || notificationSpec.type != NotificationType.GENERIC_EMAIL) {
|
||||
// 2.x notification
|
||||
return encodeExtensibleNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body,
|
||||
notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies);
|
||||
notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies, notificationSpec.attachedActions);
|
||||
} else {
|
||||
// 1.x notification on FW 2.X
|
||||
String[] parts = {title, notificationSpec.body, String.valueOf(ts), subtitle};
|
||||
@ -594,7 +596,8 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
*/
|
||||
}
|
||||
|
||||
private byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) {
|
||||
//TODO: add support for attachedActions
|
||||
private byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies, ArrayList attachedActions) {
|
||||
final short ACTION_LENGTH_MIN = 10;
|
||||
|
||||
String[] parts = {title, subtitle, body};
|
||||
@ -930,7 +933,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
|
||||
private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName,
|
||||
boolean hasHandle, NotificationType notificationType, byte backgroundColor,
|
||||
String[] cannedReplies) {
|
||||
String[] cannedReplies, ArrayList<Action> attachedActions) {
|
||||
final short NOTIFICATION_PIN_LENGTH = 46;
|
||||
final short ACTION_LENGTH_MIN = 10;
|
||||
|
||||
@ -943,36 +946,43 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
int icon_id = notificationType.icon;
|
||||
|
||||
// Calculate length first
|
||||
byte actions_count;
|
||||
short actions_length;
|
||||
byte actions_count = 0;
|
||||
short actions_length = 0;
|
||||
String dismiss_string;
|
||||
String open_string = "Open on phone";
|
||||
String mute_string = "Mute";
|
||||
String reply_string = "Reply";
|
||||
if (sourceName != null) {
|
||||
mute_string += " " + sourceName;
|
||||
}
|
||||
|
||||
byte dismiss_action_id;
|
||||
if (hasHandle && !"ALARMCLOCKRECEIVER".equals(sourceName)) {
|
||||
actions_count = 3;
|
||||
actions_count += 3;
|
||||
dismiss_string = "Dismiss";
|
||||
dismiss_action_id = 0x02;
|
||||
actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length + open_string.getBytes().length + mute_string.getBytes().length);
|
||||
//TODO: ACTION_LENGTH_MIN disagrees with my observation of the needed bytes. I used 6 instead of 10
|
||||
actions_length += (short) (6 * 3 + dismiss_string.getBytes().length + open_string.getBytes().length + mute_string.getBytes().length);
|
||||
} else {
|
||||
actions_count = 1;
|
||||
actions_count += 1;
|
||||
dismiss_string = "Dismiss all";
|
||||
dismiss_action_id = 0x03;
|
||||
actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length);
|
||||
actions_length += (short) (ACTION_LENGTH_MIN + dismiss_string.getBytes().length);
|
||||
}
|
||||
if (attachedActions != null && attachedActions.size() > 0) {
|
||||
for (Action act : attachedActions) {
|
||||
actions_count++;
|
||||
actions_length += (short) (6 + act.title.getBytes().length);
|
||||
}
|
||||
}
|
||||
|
||||
int replies_length = -1;
|
||||
if (cannedReplies != null && cannedReplies.length > 0) {
|
||||
actions_count++;
|
||||
//do not increment actions_count! reply is an action and was already added above
|
||||
for (String reply : cannedReplies) {
|
||||
replies_length += reply.getBytes().length + 1;
|
||||
}
|
||||
actions_length += ACTION_LENGTH_MIN + reply_string.getBytes().length + replies_length + 3; // 3 = attribute id (byte) + length(short)
|
||||
//similarly, only the replies lenght has to be added, the lenght for the bare action was already added above
|
||||
actions_length += replies_length + 3; // 3 = attribute id (byte) + length(short)
|
||||
}
|
||||
|
||||
byte attributes_count = 2; // icon
|
||||
@ -1053,21 +1063,31 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
buf.put(mute_string.getBytes());
|
||||
}
|
||||
|
||||
if (cannedReplies != null && replies_length > 0) {
|
||||
buf.put((byte) 0x05);
|
||||
buf.put((byte) 0x03); // reply action
|
||||
buf.put((byte) 0x02); // number attributes
|
||||
buf.put((byte) 0x01); // title
|
||||
buf.putShort((short) reply_string.getBytes().length);
|
||||
buf.put(reply_string.getBytes());
|
||||
buf.put((byte) 0x08); // canned replies
|
||||
buf.putShort((short) replies_length);
|
||||
for (int i = 0; i < cannedReplies.length - 1; i++) {
|
||||
buf.put(cannedReplies[i].getBytes());
|
||||
buf.put((byte) 0x00);
|
||||
if (attachedActions != null && attachedActions.size() > 0) {
|
||||
for (int ai = 0 ; ai<attachedActions.size(); ai++) {
|
||||
Action act = attachedActions.get(ai);
|
||||
buf.put((byte) (0x05 + ai));
|
||||
if(act.isReply) {
|
||||
buf.put((byte) 0x03); // reply action
|
||||
buf.put((byte) 0x02); // number attributes
|
||||
} else {
|
||||
buf.put((byte) 0x02); // generic action, dismiss did not do anything
|
||||
buf.put((byte) 0x01); // number attributes
|
||||
}
|
||||
buf.put((byte) 0x01); // attribute id (title)
|
||||
buf.putShort((short) act.title.getBytes().length);
|
||||
buf.put(act.title.getBytes());
|
||||
if(act.isReply && cannedReplies != null ) {
|
||||
buf.put((byte) 0x08); // canned replies
|
||||
buf.putShort((short) replies_length);
|
||||
for (int i = 0; i < cannedReplies.length - 1; i++) {
|
||||
buf.put(cannedReplies[i].getBytes());
|
||||
buf.put((byte) 0x00);
|
||||
}
|
||||
// last one must not be zero terminated, else we get an additional emply reply
|
||||
buf.put(cannedReplies[cannedReplies.length - 1].getBytes());
|
||||
}
|
||||
}
|
||||
// last one must not be zero terminated, else we get an additional emply reply
|
||||
buf.put(cannedReplies[cannedReplies.length - 1].getBytes());
|
||||
}
|
||||
|
||||
return encodeBlobdb(UUID.randomUUID(), BLOBDB_INSERT, BLOBDB_NOTIFICATION, buf.array());
|
||||
@ -2096,7 +2116,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
id = buf.getInt();
|
||||
}
|
||||
byte action = buf.get();
|
||||
if (action >= 0x00 && action <= 0x05) {
|
||||
if (action >= 0x00 && action <= 0xf) {
|
||||
GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl();
|
||||
devEvtNotificationControl.handle = id;
|
||||
String caption = "undefined";
|
||||
@ -2125,6 +2145,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
caption = "Muted";
|
||||
icon_id = PebbleIconID.RESULT_MUTE;
|
||||
break;
|
||||
//TODO: 0x05 is not a special case anymore, and reply action might have an index that is higher. see default below
|
||||
case 0x05:
|
||||
case 0x00:
|
||||
boolean failed = true;
|
||||
@ -2145,6 +2166,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
}
|
||||
devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY;
|
||||
devEvtNotificationControl.reply = new String(reply);
|
||||
devEvtNotificationControl.handle = (devEvtNotificationControl.handle << 4) + 1;
|
||||
caption = "SENT";
|
||||
icon_id = PebbleIconID.RESULT_SENT;
|
||||
failed = false;
|
||||
@ -2156,6 +2178,19 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
devEvtNotificationControl = null; // error
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (action > 0x05) {
|
||||
int simpleActionId = action - 0x05;
|
||||
caption = "EXECUTED";
|
||||
devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY;
|
||||
devEvtNotificationControl.handle = (devEvtNotificationControl.handle << 4) + simpleActionId;
|
||||
LOG.info("detected simple action, subId:" + simpleActionId + " title:" + devEvtNotificationControl.title);
|
||||
} else {
|
||||
caption = "FAILED";
|
||||
icon_id = PebbleIconID.RESULT_FAILED;
|
||||
devEvtNotificationControl = null; // error
|
||||
}
|
||||
break;
|
||||
}
|
||||
GBDeviceEventSendBytes sendBytesAck = null;
|
||||
if (mFwMajor >= 3 || needsAck2x) {
|
||||
|
Loading…
Reference in New Issue
Block a user