1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-03 17:02:13 +01:00

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.
This commit is contained in:
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 @Override
public void onClick(View v) { public void onClick(View v) {
NotificationSpec notificationSpec = new NotificationSpec(); 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.body = editContent.getText().toString();
notificationSpec.type = NotificationType.SMS; notificationSpec.type = NotificationType.SMS;
notificationSpec.id = -1; notificationSpec.id = -1;

View File

@ -50,8 +50,8 @@ public class SettingsActivity extends AbstractSettingsActivity {
} }
}); });
final Preference pebbleEmuAddr = findPreference("pebble_emu_addr"); pref = findPreference("pebble_emu_addr");
pebbleEmuAddr.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newVal) { public boolean onPreferenceChange(Preference preference, Object newVal) {
Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST);
@ -62,8 +62,8 @@ public class SettingsActivity extends AbstractSettingsActivity {
}); });
final Preference pebbleEmuPort = findPreference("pebble_emu_port"); pref = findPreference("pebble_emu_port");
pebbleEmuPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newVal) { public boolean onPreferenceChange(Preference preference, Object newVal) {
Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST); Intent refreshIntent = new Intent(ControlCenter.ACTION_REFRESH_DEVICELIST);
@ -110,6 +110,14 @@ public class SettingsActivity extends AbstractSettingsActivity {
"pebble_emu_addr", "pebble_emu_addr",
"pebble_emu_port", "pebble_emu_port",
"pebble_reconnect_attempts", "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.preference.PreferenceManager;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import java.util.ArrayList;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType; import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;

View File

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

View File

@ -217,6 +217,21 @@ public class DeviceCommunicationService extends Service {
notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME); notificationSpec.sourceName = intent.getStringExtra(EXTRA_NOTIFICATION_SOURCENAME);
if (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null) { if (notificationSpec.type == NotificationType.SMS && notificationSpec.phoneNumber != null) {
notificationSpec.sender = getContactDisplayNameByNumber(notificationSpec.phoneNumber); 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); mDeviceSupport.onNotification(notificationSpec);
break; break;

View File

@ -421,10 +421,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
if (isFw3x) { if (isFw3x) {
// 3.x notification // 3.x notification
//return encodeTimelinePin(id, (int) ((ts + 600) & 0xffffffffL), (short) 90, PebbleIconID.TIMELINE_CALENDAR, title); // really, this is just for testing //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) { } else if (mForceProtocol || notificationSpec.type != NotificationType.EMAIL) {
// 2.x notification // 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 { } else {
// 1.x notification on FW 2.X // 1.x notification on FW 2.X
String[] parts = {title, notificationSpec.body, ts.toString(), subtitle}; 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); 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; final short ACTION_LENGTH_MIN = 10;
String[] parts = {title, subtitle, body}; String[] parts = {title, subtitle, body};
@ -478,6 +478,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
String dismiss_string; String dismiss_string;
String open_string = "Open on phone"; String open_string = "Open on phone";
String mute_string = "Mute"; String mute_string = "Mute";
String reply_string = "Reply";
if (sourceName != null) { if (sourceName != null) {
mute_string += " " + sourceName; mute_string += " " + sourceName;
} }
@ -496,6 +497,15 @@ public class PebbleProtocol extends GBDeviceProtocol {
actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length); 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; byte attributes_count = 0;
int length = 21 + 10 + actions_length; int length = 21 + 10 + actions_length;
@ -554,7 +564,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.putShort((short) dismiss_string.getBytes().length); buf.putShort((short) dismiss_string.getBytes().length);
buf.put(dismiss_string.getBytes()); buf.put(dismiss_string.getBytes());
// open action // open and mute actions
if (hasHandle) { if (hasHandle) {
buf.put((byte) 0x01); buf.put((byte) 0x01);
buf.put((byte) 0x02); // generic 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(); return buf.array();
} }
@ -645,7 +672,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
return encodeBlobdb(uuid, BLOBDB_INSERT, BLOBDB_PIN, buf.array()); 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 NOTIFICATION_PIN_LENGTH = 46;
final short ACTION_LENGTH_MIN = 10; final short ACTION_LENGTH_MIN = 10;
@ -697,6 +724,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
String dismiss_string; String dismiss_string;
String open_string = "Open on phone"; String open_string = "Open on phone";
String mute_string = "Mute"; String mute_string = "Mute";
String reply_string = "Reply";
if (sourceName != null) { if (sourceName != null) {
mute_string += " " + sourceName; mute_string += " " + sourceName;
} }
@ -714,6 +742,15 @@ public class PebbleProtocol extends GBDeviceProtocol {
actions_length = (short) (ACTION_LENGTH_MIN * actions_count + dismiss_string.getBytes().length); 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 byte attributes_count = 2; // icon
short attributes_length = (short) (11 + actions_length); short attributes_length = (short) (11 + actions_length);
if (parts != null) { if (parts != null) {
@ -798,6 +835,24 @@ public class PebbleProtocol extends GBDeviceProtocol {
buf.putShort((short) mute_string.getBytes().length); buf.putShort((short) mute_string.getBytes().length);
buf.put(mute_string.getBytes()); 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()); return encodeBlobdb(UUID.randomUUID(), BLOBDB_INSERT, BLOBDB_NOTIFICATION, buf.array());
} }
@ -1436,7 +1491,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
if (command == 0x02) { if (command == 0x02) {
int id = buf.getInt(); int id = buf.getInt();
byte action = buf.get(); byte action = buf.get();
if (action >= 0x01 && action <= 0x04) { if (action >= 0x01 && action <= 0x05) {
GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl(); GBDeviceEventNotificationControl devEvtNotificationControl = new GBDeviceEventNotificationControl();
devEvtNotificationControl.handle = id; devEvtNotificationControl.handle = id;
GBDeviceEventSendBytes sendBytesAck = null; GBDeviceEventSendBytes sendBytesAck = null;
@ -1458,6 +1513,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
sendBytesAck = new GBDeviceEventSendBytes(); sendBytesAck = new GBDeviceEventSendBytes();
sendBytesAck.encodedBytes = encodeActionResponse2x(id, action, 6, "Muted"); sendBytesAck.encodedBytes = encodeActionResponse2x(id, action, 6, "Muted");
break; break;
case 0x05:
devEvtNotificationControl = null; // not implemented
sendBytesAck = new GBDeviceEventSendBytes();
sendBytesAck.encodedBytes = encodeActionResponse2x(id, action, 6, "NOT IMPLEMENTED");
break;
default: default:
return null; return null;
} }
@ -1479,7 +1539,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
long uuid_low = buf.getLong(); long uuid_low = buf.getLong();
int id = (int) (uuid_low & 0xffffffffL); int id = (int) (uuid_low & 0xffffffffL);
byte action = buf.get(); byte action = buf.get();
if (action >= 0x01 && action <= 0x04) { if (action >= 0x01 && action <= 0x05) {
GBDeviceEventNotificationControl dismissNotification = new GBDeviceEventNotificationControl(); GBDeviceEventNotificationControl dismissNotification = new GBDeviceEventNotificationControl();
dismissNotification.handle = id; dismissNotification.handle = id;
String caption = "undefined"; String caption = "undefined";
@ -1505,6 +1565,11 @@ public class PebbleProtocol extends GBDeviceProtocol {
caption = "Muted"; caption = "Muted";
icon_id = PebbleIconID.RESULT_MUTE; icon_id = PebbleIconID.RESULT_MUTE;
break; break;
case 0x05:
dismissNotification = null; // not implemented
caption = "NOT IMPLEMENTED";
icon_id = PebbleIconID.GENERIC_WARNING;
break;
} }
GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes(); GBDeviceEventSendBytes sendBytesAck = new GBDeviceEventSendBytes();
sendBytesAck.encodedBytes = encodeActionResponse(new UUID(uuid_high, uuid_low), icon_id, caption); 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_blacklist">Blacklist Apps</string>
<string name="pref_title_canned_replies">Canned Replies</string>
<string name="pref_header_development">Developer Options</string> <string name="pref_header_development">Developer Options</string>
<string name="pref_title_development_miaddr">Mi Band address</string> <string name="pref_title_development_miaddr">Mi Band address</string>

View File

@ -65,6 +65,35 @@
<Preference <Preference
android:key="pref_key_blacklist" android:key="pref_key_blacklist"
android:title="@string/pref_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>
<PreferenceCategory <PreferenceCategory