WIP: Work towards SMS replies / canned replies

- Implement the PebbleProtocol side (2.x and 3.x)
- Add Preferences for canned replies

This can be tested by enabling untested features in Pebble Settings
It lets you see and select the replies set up in "Canned Repies" on the Pebble
You will get a "NOT IMPLENTED" message on your Pebble.

THIS DOES NOT ACTUALLY DO ANYTHING USEFUL YET.
here
Andreas Shimokawa 2015-12-13 12:03:57 +01:00
parent f258e62633
commit 53fb63781e
8 changed files with 134 additions and 12 deletions

View File

@ -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;

View File

@ -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",
};
}

View File

@ -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;

View File

@ -9,4 +9,5 @@ public class NotificationSpec {
public String body;
public NotificationType type;
public String sourceName;
public String[] cannedReplies;
}

View File

@ -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<String> 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;

View File

@ -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);

View File

@ -55,6 +55,8 @@
<string name="pref_blacklist">Blacklist Apps</string>
<string name="pref_title_canned_replies">Canned Replies</string>
<string name="pref_header_development">Developer Options</string>
<string name="pref_title_development_miaddr">Mi Band address</string>

View File

@ -65,6 +65,35 @@
<Preference
android:key="pref_key_blacklist"
android:title="@string/pref_blacklist" />
<PreferenceScreen
android:key="pref_key_canned_replies"
android:title="@string/pref_title_canned_replies"
android:dependency="pebble_force_untested" >
<EditTextPreference
android:key="canned_reply_1"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_2"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_3"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_4"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_5"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_6"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_7"
android:maxLength="64" />
<EditTextPreference
android:key="canned_reply_8"
android:maxLength="64" />
</PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory