mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 18:15:49 +01:00
Pebble: EXPERIMENTAL support for replying to wearable notifications
Tested with Signal, more could work.
This commit is contained in:
parent
46bbab7df0
commit
0b53f60b0d
@ -16,15 +16,20 @@ import android.os.PowerManager;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.RemoteInput;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||
|
||||
public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
@ -38,6 +43,10 @@ public class NotificationListener extends NotificationListenerService {
|
||||
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.open";
|
||||
public static final String ACTION_MUTE
|
||||
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.mute";
|
||||
public static final String ACTION_REPLY
|
||||
= "nodomain.freeyourgadget.gadgetbridge.notificationlistener.action.reply";
|
||||
|
||||
private LimitedQueue mActionLookup = new LimitedQueue(16);
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@SuppressLint("NewApi")
|
||||
@ -90,6 +99,28 @@ public class NotificationListener extends NotificationListenerService {
|
||||
case ACTION_DISMISS_ALL:
|
||||
NotificationListener.this.cancelAllNotifications();
|
||||
break;
|
||||
case ACTION_REPLY:
|
||||
int id = intent.getIntExtra("handle", -1);
|
||||
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();
|
||||
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);
|
||||
|
||||
try {
|
||||
LOG.info("will send reply intent to remote application");
|
||||
actionIntent.send(context, 0, localIntent);
|
||||
mActionLookup.remove(id);
|
||||
} catch (PendingIntent.CanceledException e) {
|
||||
LOG.warn("replyToLastNotification error: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -103,6 +134,7 @@ public class NotificationListener extends NotificationListenerService {
|
||||
filterLocal.addAction(ACTION_DISMISS);
|
||||
filterLocal.addAction(ACTION_DISMISS_ALL);
|
||||
filterLocal.addAction(ACTION_MUTE);
|
||||
filterLocal.addAction(ACTION_REPLY);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
|
||||
}
|
||||
|
||||
@ -222,6 +254,18 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
dissectNotificationTo(notification, notificationSpec);
|
||||
notificationSpec.id = (int) sbn.getPostTime(); //FIMXE: a truly unique id would be better
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender = new NotificationCompat.WearableExtender(notification);
|
||||
List<NotificationCompat.Action> actions = wearableExtender.getActions();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
GBApplication.deviceService().onNotification(notificationSpec);
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,7 @@ public class GBDeviceService implements DeviceService {
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
Intent intent = createIntent().setAction(ACTION_NOTIFICATION)
|
||||
.putExtra(EXTRA_NOTIFICATION_FLAGS, notificationSpec.flags)
|
||||
.putExtra(EXTRA_NOTIFICATION_PHONENUMBER, notificationSpec.phoneNumber)
|
||||
.putExtra(EXTRA_NOTIFICATION_SENDER, notificationSpec.sender)
|
||||
.putExtra(EXTRA_NOTIFICATION_SUBJECT, notificationSpec.subject)
|
||||
|
@ -35,6 +35,7 @@ public interface DeviceService extends EventHandler {
|
||||
|
||||
String EXTRA_DEVICE_ADDRESS = "device_address";
|
||||
String EXTRA_NOTIFICATION_BODY = "notification_body";
|
||||
String EXTRA_NOTIFICATION_FLAGS = "notification_flags";
|
||||
String EXTRA_NOTIFICATION_ID = "notification_id";
|
||||
String EXTRA_NOTIFICATION_PHONENUMBER = "notification_phonenumber";
|
||||
String EXTRA_NOTIFICATION_SENDER = "notification_sender";
|
||||
|
@ -1,6 +1,9 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
public class NotificationSpec {
|
||||
public static final int FLAG_WEARABLE_REPLY = 0x00000001;
|
||||
|
||||
public int flags;
|
||||
public int id;
|
||||
public String sender;
|
||||
public String phoneNumber;
|
||||
|
@ -231,14 +231,20 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
||||
case REPLY:
|
||||
String phoneNumber = (String) GBApplication.getIDSenderLookup().lookup(deviceEvent.handle);
|
||||
if (phoneNumber != null) {
|
||||
LOG.info("got notfication reply for " + phoneNumber + " : " + deviceEvent.reply);
|
||||
LOG.info("got notfication reply for SMS from " + phoneNumber + " : " + deviceEvent.reply);
|
||||
SmsManager.getDefault().sendTextMessage(phoneNumber, null, deviceEvent.reply, null, null);
|
||||
} else {
|
||||
LOG.info("got notfication reply for notification id " + deviceEvent.handle + " : " + deviceEvent.reply);
|
||||
action = NotificationListener.ACTION_REPLY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (action != null) {
|
||||
Intent notificationListenerIntent = new Intent(action);
|
||||
notificationListenerIntent.putExtra("handle", deviceEvent.handle);
|
||||
if (deviceEvent.reply != null) {
|
||||
notificationListenerIntent.putExtra("reply", deviceEvent.reply);
|
||||
}
|
||||
LocalBroadcastManager.getInstance(context).sendBroadcast(notificationListenerIntent);
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUS
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_ARTIST;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_MUSIC_TRACK;
|
||||
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;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_PHONENUMBER;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.EXTRA_NOTIFICATION_SENDER;
|
||||
@ -218,13 +219,16 @@ public class DeviceCommunicationService extends Service {
|
||||
notificationSpec.body = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
||||
notificationSpec.type = (NotificationType) intent.getSerializableExtra(EXTRA_NOTIFICATION_TYPE);
|
||||
notificationSpec.id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
|
||||
notificationSpec.flags = intent.getIntExtra(EXTRA_NOTIFICATION_FLAGS, 0);
|
||||
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
|
||||
if (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null) {
|
||||
notificationSpec.sender = getContactDisplayNameByNumber(notificationSpec.phoneNumber);
|
||||
|
||||
notificationSpec.id = mRandom.nextInt(); // FIXME: add this in external SMS Receiver?
|
||||
GBApplication.getIDSenderLookup().add(notificationSpec.id, notificationSpec.phoneNumber);
|
||||
|
||||
}
|
||||
if (((notificationSpec.flags & NotificationSpec.FLAG_WEARABLE_REPLY) > 0)
|
||||
|| (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null)) {
|
||||
// NOTE: maybe not where it belongs
|
||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (sharedPrefs.getBoolean("pebble_force_untested", false)) {
|
||||
|
@ -1591,13 +1591,13 @@ public class PebbleProtocol extends GBDeviceProtocol {
|
||||
buf.get(reply);
|
||||
// FIXME: this does not belong here, but we want at least check if there is no chance at all to send out the SMS later before we report success
|
||||
String phoneNumber = (String) GBApplication.getIDSenderLookup().lookup(id);
|
||||
if (phoneNumber != null) {
|
||||
//if (phoneNumber != null) {
|
||||
devEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.REPLY;
|
||||
devEvtNotificationControl.reply = new String(reply);
|
||||
caption = "SENT";
|
||||
icon_id = PebbleIconID.RESULT_SENT;
|
||||
failed = false;
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
|
@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.util;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class LimitedQueue {
|
||||
@ -12,11 +13,20 @@ public class LimitedQueue {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public void add(int id, Object sender) {
|
||||
public void add(int id, Object obj) {
|
||||
if (list.size() > limit - 1) {
|
||||
list.removeFirst();
|
||||
}
|
||||
list.add(new Pair<>(id, sender));
|
||||
list.add(new Pair<>(id, obj));
|
||||
}
|
||||
|
||||
public void remove(int id) {
|
||||
for (Iterator<Pair> iter = list.iterator(); iter.hasNext(); ) {
|
||||
Pair pair = iter.next();
|
||||
if (pair.first == id) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object lookup(int id) {
|
||||
|
Loading…
Reference in New Issue
Block a user