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 18d3b526b..c260735a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/DebugActivity.java @@ -76,7 +76,7 @@ public class DebugActivity extends Activity { @Override public void onClick(View v) { NotificationSpec notificationSpec = new NotificationSpec(); - notificationSpec.sender = getResources().getText(R.string.app_name).toString(); + notificationSpec.phoneNumber = getResources().getText(R.string.app_name).toString(); notificationSpec.body = editContent.getText().toString(); notificationSpec.type = NotificationType.SMS; notificationSpec.id = -1; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java index a3dd4c67a..7744da5a8 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java @@ -50,8 +50,8 @@ public class SettingsActivity extends AbstractSettingsActivity { } }); - final Preference pebbleEmuAddr = findPreference("pebble_emu_addr"); - pebbleEmuAddr.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + pref = findPreference("pebble_emu_addr"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); @@ -62,8 +62,8 @@ public class SettingsActivity extends AbstractSettingsActivity { }); - final Preference pebbleEmuPort = findPreference("pebble_emu_port"); - pebbleEmuPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + pref = findPreference("pebble_emu_port"); + pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newVal) { Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); @@ -110,6 +110,14 @@ public class SettingsActivity extends AbstractSettingsActivity { "pebble_emu_addr", "pebble_emu_port", "pebble_reconnect_attempts", + "canned_reply_1", + "canned_reply_2", + "canned_reply_3", + "canned_reply_4", + "canned_reply_5", + "canned_reply_6", + "canned_reply_7", + "canned_reply_8", }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java index a2dd09a32..664e44e9a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/SMSReceiver.java @@ -9,6 +9,8 @@ import android.os.PowerManager; import android.preference.PreferenceManager; import android.telephony.SmsMessage; +import java.util.ArrayList; + import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; 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 e29a30d85..fde544d50 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/NotificationSpec.java @@ -9,4 +9,5 @@ public class NotificationSpec { public String body; public NotificationType type; public String sourceName; + public String[] cannedReplies; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 41f7df33f..847a035db 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -217,6 +217,21 @@ public class DeviceCommunicationService extends Service { notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME); if (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null) { notificationSpec.sender = getContactDisplayNameByNumber(notificationSpec.phoneNumber); + + // NOTE: maybe not where it belongs + SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + if (sharedPrefs.getBoolean("pebble_force_untested", false)) { + // I would rather like to save that as an array in ShadredPreferences + // this would work but I dont know how to do the same in the Settings Activity's xml + ArrayList replies = new ArrayList<>(); + for (int i = 1; i <= 8; i++) { + String reply = sharedPrefs.getString("canned_reply_" + i, null); + if (reply != null && !reply.equals("")) { + replies.add(reply); + } + } + notificationSpec.cannedReplies = replies.toArray(new String[replies.size()]); + } } mDeviceSupport.onNotification(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 7b52a3723..f938b85c8 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 @@ -421,10 +421,10 @@ public class PebbleProtocol extends GBDeviceProtocol { if (isFw3x) { // 3.x notification //return encodeTimelinePin(id, (int) ((ts + 600) & 0xffffffffL), (short) 90, PebbleIconID.TIMELINE_CALENDAR, title); // really, this is just for testing - return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.type); + return encodeBlobdbNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.type, notificationSpec.cannedReplies); } else if (mForceProtocol || notificationSpec.type != NotificationType.EMAIL) { // 2.x notification - return encodeExtensibleNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle); + return encodeExtensibleNotification(id, (int) (ts & 0xffffffffL), title, subtitle, notificationSpec.body, notificationSpec.sourceName, hasHandle, notificationSpec.cannedReplies); } else { // 1.x notification on FW 2.X String[] parts = {title, notificationSpec.body, ts.toString(), subtitle}; @@ -467,7 +467,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeSetCallState("Where are you?", "Gadgetbridge", start ? ServiceCommand.CALL_INCOMING : ServiceCommand.CALL_END); } - private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle) { + private static byte[] encodeExtensibleNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, String[] cannedReplies) { final short ACTION_LENGTH_MIN = 10; String[] parts = {title, subtitle, body}; @@ -478,6 +478,7 @@ public class PebbleProtocol extends GBDeviceProtocol { String dismiss_string; String open_string = "Open on phone"; String mute_string = "Mute"; + String reply_string = "Reply"; if (sourceName != null) { mute_string += " " + sourceName; } @@ -496,6 +497,15 @@ public class PebbleProtocol extends GBDeviceProtocol { actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length); } + int replies_length = -1; + if (cannedReplies != null) { + actions_count++; + 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) + } + byte attributes_count = 0; int length = 21 + 10 + actions_length; @@ -554,7 +564,7 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putShort((short) dismiss_string.getBytes().length); buf.put(dismiss_string.getBytes()); - // open action + // open and mute actions if (hasHandle) { buf.put((byte) 0x01); buf.put((byte) 0x02); // generic @@ -572,6 +582,23 @@ public class PebbleProtocol extends GBDeviceProtocol { } + if (cannedReplies != null) { + 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); + } + // last one must not be zero terminated, else we get an additional emply reply + buf.put(cannedReplies[cannedReplies.length - 1].getBytes()); + } + return buf.array(); } @@ -645,7 +672,7 @@ public class PebbleProtocol extends GBDeviceProtocol { return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); } - private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, NotificationType notificationType) { + private byte[] encodeBlobdbNotification(int id, int timestamp, String title, String subtitle, String body, String sourceName, boolean hasHandle, NotificationType notificationType, String[] cannedReplies) { final short NOTIFICATION_PIN_LENGTH = 46; final short ACTION_LENGTH_MIN = 10; @@ -697,6 +724,7 @@ public class PebbleProtocol extends GBDeviceProtocol { String dismiss_string; String open_string = "Open on phone"; String mute_string = "Mute"; + String reply_string = "Reply"; if (sourceName != null) { mute_string += " " + sourceName; } @@ -714,6 +742,15 @@ public class PebbleProtocol extends GBDeviceProtocol { actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length); } + int replies_length = -1; + if (cannedReplies != null) { + actions_count++; + 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) + } + byte attributes_count = 2; // icon short attributes_length = (short) (11 + actions_length); if (parts != null) { @@ -798,6 +835,24 @@ public class PebbleProtocol extends GBDeviceProtocol { buf.putShort((short) mute_string.getBytes().length); buf.put(mute_string.getBytes()); } + + if (cannedReplies != null) { + 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); + } + // 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()); } @@ -1436,7 +1491,7 @@ public class PebbleProtocol extends GBDeviceProtocol { if (command == 0x02) { int id = buf.getInt(); byte action = buf.get(); - if (action >= 0x01 && action <= 0x04) { + if (action >= 0x01 && action <= 0x05) { GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl(); devEvtNotificationControl.handle = id; GBDeviceEventSendBytes sendBytesAck = null; @@ -1458,6 +1513,11 @@ public class PebbleProtocol extends GBDeviceProtocol { sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck.encodedBytes = encodeActionResponse2x(id, action, 6, "Muted"); break; + case 0x05: + devEvtNotificationControl = null; // not implemented + sendBytesAck = new GBDeviceEventSendBytes(); + sendBytesAck.encodedBytes = encodeActionResponse2x(id, action, 6, "NOT IMPLEMENTED"); + break; default: return null; } @@ -1479,7 +1539,7 @@ public class PebbleProtocol extends GBDeviceProtocol { long uuid_low = buf.getLong(); int id = (int) (uuid_low & 0xffffffffL); byte action = buf.get(); - if (action >= 0x01 && action <= 0x04) { + if (action >= 0x01 && action <= 0x05) { GBDeviceEventNotificationControl dismissNotification = new GBDeviceEventNotificationControl(); dismissNotification.handle = id; String caption = "undefined"; @@ -1505,6 +1565,11 @@ public class PebbleProtocol extends GBDeviceProtocol { caption = "Muted"; icon_id = PebbleIconID.RESULT_MUTE; break; + case 0x05: + dismissNotification = null; // not implemented + caption = "NOT IMPLEMENTED"; + icon_id = PebbleIconID.GENERIC_WARNING; + break; } GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck.encodedBytes = encodeActionResponse(new UUID(uuid_high, uuid_low), icon_id, caption); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index edf7823e2..e7e267a95 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -55,6 +55,8 @@ Blacklist Apps + Canned Replies + Developer Options Mi Band address diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 599e88b4c..cb0f7082e 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -65,6 +65,35 @@ + + + + + + + + + +