Refactor group messaging protocol.
// FREEBIE
This commit is contained in:
parent
b855f8a163
commit
a6e1d56cde
18 changed files with 314 additions and 254 deletions
|
@ -30,11 +30,9 @@ message PushMessageContent {
|
||||||
message GroupContext {
|
message GroupContext {
|
||||||
enum Type {
|
enum Type {
|
||||||
UNKNOWN = 0;
|
UNKNOWN = 0;
|
||||||
CREATE = 1;
|
UPDATE = 1;
|
||||||
MODIFY = 2;
|
DELIVER = 2;
|
||||||
DELIVER = 3;
|
QUIT = 3;
|
||||||
ADD = 4;
|
|
||||||
QUIT = 5;
|
|
||||||
}
|
}
|
||||||
optional bytes id = 1;
|
optional bytes id = 1;
|
||||||
optional Type type = 2;
|
optional Type type = 2;
|
||||||
|
|
|
@ -1463,19 +1463,15 @@ public final class PushMessageProtos {
|
||||||
public enum Type
|
public enum Type
|
||||||
implements com.google.protobuf.ProtocolMessageEnum {
|
implements com.google.protobuf.ProtocolMessageEnum {
|
||||||
UNKNOWN(0, 0),
|
UNKNOWN(0, 0),
|
||||||
CREATE(1, 1),
|
UPDATE(1, 1),
|
||||||
MODIFY(2, 2),
|
DELIVER(2, 2),
|
||||||
DELIVER(3, 3),
|
QUIT(3, 3),
|
||||||
ADD(4, 4),
|
|
||||||
QUIT(5, 5),
|
|
||||||
;
|
;
|
||||||
|
|
||||||
public static final int UNKNOWN_VALUE = 0;
|
public static final int UNKNOWN_VALUE = 0;
|
||||||
public static final int CREATE_VALUE = 1;
|
public static final int UPDATE_VALUE = 1;
|
||||||
public static final int MODIFY_VALUE = 2;
|
public static final int DELIVER_VALUE = 2;
|
||||||
public static final int DELIVER_VALUE = 3;
|
public static final int QUIT_VALUE = 3;
|
||||||
public static final int ADD_VALUE = 4;
|
|
||||||
public static final int QUIT_VALUE = 5;
|
|
||||||
|
|
||||||
|
|
||||||
public final int getNumber() { return value; }
|
public final int getNumber() { return value; }
|
||||||
|
@ -1483,11 +1479,9 @@ public final class PushMessageProtos {
|
||||||
public static Type valueOf(int value) {
|
public static Type valueOf(int value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 0: return UNKNOWN;
|
case 0: return UNKNOWN;
|
||||||
case 1: return CREATE;
|
case 1: return UPDATE;
|
||||||
case 2: return MODIFY;
|
case 2: return DELIVER;
|
||||||
case 3: return DELIVER;
|
case 3: return QUIT;
|
||||||
case 4: return ADD;
|
|
||||||
case 5: return QUIT;
|
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1518,7 +1512,7 @@ public final class PushMessageProtos {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Type[] VALUES = {
|
private static final Type[] VALUES = {
|
||||||
UNKNOWN, CREATE, MODIFY, DELIVER, ADD, QUIT,
|
UNKNOWN, UPDATE, DELIVER, QUIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Type valueOf(
|
public static Type valueOf(
|
||||||
|
@ -3073,22 +3067,22 @@ public final class PushMessageProtos {
|
||||||
"evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" +
|
"evice\030\007 \001(\r\022\r\n\005relay\030\003 \001(\t\022\021\n\ttimestamp\030" +
|
||||||
"\005 \001(\004\022\017\n\007message\030\006 \001(\014\"W\n\004Type\022\013\n\007UNKNOW" +
|
"\005 \001(\004\022\017\n\007message\030\006 \001(\014\"W\n\004Type\022\013\n\007UNKNOW" +
|
||||||
"N\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n" +
|
"N\020\000\022\016\n\nCIPHERTEXT\020\001\022\020\n\014KEY_EXCHANGE\020\002\022\021\n" +
|
||||||
"\rPREKEY_BUNDLE\020\003\022\r\n\tPLAINTEXT\020\004\"\234\004\n\022Push" +
|
"\rPREKEY_BUNDLE\020\003\022\r\n\tPLAINTEXT\020\004\"\207\004\n\022Push" +
|
||||||
"MessageContent\022\014\n\004body\030\001 \001(\t\022E\n\013attachme" +
|
"MessageContent\022\014\n\004body\030\001 \001(\t\022E\n\013attachme" +
|
||||||
"nts\030\002 \003(\01320.textsecure.PushMessageConten",
|
"nts\030\002 \003(\01320.textsecure.PushMessageConten",
|
||||||
"t.AttachmentPointer\022:\n\005group\030\003 \001(\0132+.tex" +
|
"t.AttachmentPointer\022:\n\005group\030\003 \001(\0132+.tex" +
|
||||||
"tsecure.PushMessageContent.GroupContext\022" +
|
"tsecure.PushMessageContent.GroupContext\022" +
|
||||||
"\r\n\005flags\030\004 \001(\r\032A\n\021AttachmentPointer\022\n\n\002i" +
|
"\r\n\005flags\030\004 \001(\r\032A\n\021AttachmentPointer\022\n\n\002i" +
|
||||||
"d\030\001 \001(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003key\030\003 \001(" +
|
"d\030\001 \001(\006\022\023\n\013contentType\030\002 \001(\t\022\013\n\003key\030\003 \001(" +
|
||||||
"\014\032\210\002\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022>\n\004type\030\002" +
|
"\014\032\363\001\n\014GroupContext\022\n\n\002id\030\001 \001(\014\022>\n\004type\030\002" +
|
||||||
" \001(\01620.textsecure.PushMessageContent.Gro" +
|
" \001(\01620.textsecure.PushMessageContent.Gro" +
|
||||||
"upContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007members\030" +
|
"upContext.Type\022\014\n\004name\030\003 \001(\t\022\017\n\007members\030" +
|
||||||
"\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.textsecure.PushM" +
|
"\004 \003(\t\022@\n\006avatar\030\005 \001(\01320.textsecure.PushM" +
|
||||||
"essageContent.AttachmentPointer\"K\n\004Type\022" +
|
"essageContent.AttachmentPointer\"6\n\004Type\022" +
|
||||||
"\013\n\007UNKNOWN\020\000\022\n\n\006CREATE\020\001\022\n\n\006MODIFY\020\002\022\013\n\007",
|
"\013\n\007UNKNOWN\020\000\022\n\n\006UPDATE\020\001\022\013\n\007DELIVER\020\002\022\010\n",
|
||||||
"DELIVER\020\003\022\007\n\003ADD\020\004\022\010\n\004QUIT\020\005\"\030\n\005Flags\022\017\n" +
|
"\004QUIT\020\003\"\030\n\005Flags\022\017\n\013END_SESSION\020\001B7\n\"org" +
|
||||||
"\013END_SESSION\020\001B7\n\"org.whispersystems.tex" +
|
".whispersystems.textsecure.pushB\021PushMes" +
|
||||||
"tsecure.pushB\021PushMessageProtos"
|
"sageProtos"
|
||||||
};
|
};
|
||||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||||
|
|
|
@ -337,6 +337,11 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleGroupUpdate() {
|
||||||
|
Log.w("GroupCreateActivity", "Creating...");
|
||||||
|
new UpdateWhisperGroupAsyncTask().execute();
|
||||||
|
}
|
||||||
|
|
||||||
private static List<String> recipientsToNormalizedStrings(Collection<Recipient> recipients, String localNumber) {
|
private static List<String> recipientsToNormalizedStrings(Collection<Recipient> recipients, String localNumber) {
|
||||||
final List<String> e164numbers = new ArrayList<String>(recipients.size());
|
final List<String> e164numbers = new ArrayList<String>(recipients.size());
|
||||||
for (Recipient contact : recipients) {
|
for (Recipient contact : recipients) {
|
||||||
|
@ -349,63 +354,6 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
return e164numbers;
|
return e164numbers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGroupUpdate() {
|
|
||||||
Log.i(TAG, "Updating group info.");
|
|
||||||
GroupDatabase db = DatabaseFactory.getGroupDatabase(this);
|
|
||||||
final String localNumber = TextSecurePreferences.getLocalNumber(this);
|
|
||||||
List<String> e164numbers = recipientsToNormalizedStrings(selectedContacts, localNumber);
|
|
||||||
if (selectedContacts.size() > 0) {
|
|
||||||
db.add(groupId, localNumber, e164numbers);
|
|
||||||
GroupContext context = GroupContext.newBuilder()
|
|
||||||
.setId(ByteString.copyFrom(groupId))
|
|
||||||
.setType(GroupContext.Type.ADD)
|
|
||||||
.addAllMembers(e164numbers)
|
|
||||||
.build();
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, null);
|
|
||||||
try {
|
|
||||||
MessageSender.send(this, masterSecret, outgoingMessage, groupThread);
|
|
||||||
} catch (MmsException me) {
|
|
||||||
Log.w(TAG, "MmsException encountered when trying to add members to group.", me);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupContext.Builder builder = GroupContext.newBuilder()
|
|
||||||
.setId(ByteString.copyFrom(groupId))
|
|
||||||
.setType(GroupContext.Type.MODIFY);
|
|
||||||
boolean shouldSendUpdate = false;
|
|
||||||
final String title = groupName.getText().toString();
|
|
||||||
if (existingTitle == null || (groupName.getText() != null && !existingTitle.equals(title))) {
|
|
||||||
builder.setName(title);
|
|
||||||
db.updateTitle(groupId, title);
|
|
||||||
shouldSendUpdate = true;
|
|
||||||
}
|
|
||||||
byte[] avatarBytes = null;
|
|
||||||
if (existingAvatarBmp == null || !existingAvatarBmp.equals(avatarBmp)) {
|
|
||||||
if (avatarBmp != null) {
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
|
||||||
avatarBytes = stream.toByteArray();
|
|
||||||
}
|
|
||||||
db.updateAvatar(groupId, avatarBytes);
|
|
||||||
shouldSendUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldSendUpdate) {
|
|
||||||
GroupContext context = builder.build();
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatarBytes);
|
|
||||||
try {
|
|
||||||
MessageSender.send(this, masterSecret, outgoingMessage, groupThread);
|
|
||||||
} catch (MmsException me) {
|
|
||||||
Log.w(TAG, "MmsException encountered when trying to add members to group.", me);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RecipientFactory.clearCache(groupRecipient.getPrimaryRecipient());
|
|
||||||
|
|
||||||
setResult(RESULT_OK, getIntent());
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableWhisperGroupCreatingUi() {
|
private void enableWhisperGroupCreatingUi() {
|
||||||
findViewById(R.id.group_details_layout).setVisibility(View.GONE);
|
findViewById(R.id.group_details_layout).setVisibility(View.GONE);
|
||||||
findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
|
findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
|
||||||
|
@ -475,29 +423,56 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, Recipients> handleCreatePushGroup(String groupName,
|
private Pair<Long, Recipients> handleCreatePushGroup(String groupName, byte[] avatar,
|
||||||
byte[] avatar,
|
|
||||||
Set<Recipient> members)
|
Set<Recipient> members)
|
||||||
throws InvalidNumberException, MmsException
|
throws InvalidNumberException, MmsException
|
||||||
{
|
{
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
||||||
byte[] groupId = groupDatabase.allocateGroupId();
|
byte[] groupId = groupDatabase.allocateGroupId();
|
||||||
|
List<String> memberE164Numbers = getE164Numbers(members);
|
||||||
|
|
||||||
|
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
||||||
|
memberE164Numbers, null, null);
|
||||||
|
groupDatabase.updateAvatar(groupId, avatar);
|
||||||
|
|
||||||
|
return handlePushOperation(groupId, groupName, avatar, memberE164Numbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<Long, Recipients> handleUpdatePushGroup(byte[] groupId, String groupName,
|
||||||
|
byte[] avatar, Set<Recipient> members)
|
||||||
|
throws InvalidNumberException, MmsException
|
||||||
|
{
|
||||||
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
||||||
|
List<String> memberE164Numbers = getE164Numbers(members);
|
||||||
|
|
||||||
|
GroupDatabase.GroupRecord record = groupDatabase.getGroup(groupId);
|
||||||
|
Set<String> newMembers = new HashSet<String>(memberE164Numbers);
|
||||||
|
newMembers.removeAll(record.getMembers());
|
||||||
|
|
||||||
|
groupDatabase.add(groupId, TextSecurePreferences.getLocalNumber(this),
|
||||||
|
new LinkedList<String>(newMembers));
|
||||||
|
|
||||||
|
groupDatabase.updateTitle(groupId, groupName);
|
||||||
|
groupDatabase.updateAvatar(groupId, avatar);
|
||||||
|
|
||||||
|
|
||||||
|
return handlePushOperation(groupId, groupName, avatar, memberE164Numbers);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<Long, Recipients> handlePushOperation(byte[] groupId, String groupName, byte[] avatar,
|
||||||
|
List<String> e164numbers)
|
||||||
|
throws MmsException, InvalidNumberException
|
||||||
|
{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<String> memberE164Numbers = getE164Numbers(members);
|
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
||||||
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
|
||||||
|
|
||||||
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
|
||||||
memberE164Numbers, null, null);
|
|
||||||
groupDatabase.updateAvatar(groupId, avatar);
|
|
||||||
|
|
||||||
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
|
|
||||||
|
|
||||||
GroupContext context = GroupContext.newBuilder()
|
GroupContext context = GroupContext.newBuilder()
|
||||||
.setId(ByteString.copyFrom(groupId))
|
.setId(ByteString.copyFrom(groupId))
|
||||||
.setType(GroupContext.Type.CREATE)
|
.setType(GroupContext.Type.UPDATE)
|
||||||
.setName(groupName)
|
.setName(groupName)
|
||||||
.addAllMembers(memberE164Numbers)
|
.addAllMembers(e164numbers)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar);
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar);
|
||||||
|
@ -508,7 +483,6 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
} catch (MmsException e) {
|
} catch (MmsException e) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, e);
|
||||||
groupDatabase.remove(groupId, TextSecurePreferences.getLocalNumber(this));
|
|
||||||
throw new MmsException(e);
|
throw new MmsException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -588,6 +562,30 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class UpdateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
||||||
|
private long RES_BAD_NUMBER = -2;
|
||||||
|
private long RES_MMS_EXCEPTION = -3;
|
||||||
|
@Override
|
||||||
|
protected Pair<Long, Recipients> doInBackground(Void... params) {
|
||||||
|
byte[] avatarBytes = null;
|
||||||
|
if (avatarBmp != null) {
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||||
|
avatarBytes = stream.toByteArray();
|
||||||
|
}
|
||||||
|
final String name = (groupName.getText() != null) ? groupName.getText().toString() : null;
|
||||||
|
try {
|
||||||
|
return handleUpdatePushGroup(groupId, name, avatarBytes, selectedContacts);
|
||||||
|
} catch (MmsException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return new Pair<Long,Recipients>(RES_MMS_EXCEPTION, null);
|
||||||
|
} catch (InvalidNumberException e) {
|
||||||
|
Log.w(TAG, e);
|
||||||
|
return new Pair<Long,Recipients>(RES_BAD_NUMBER, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class CreateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
private class CreateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
||||||
private long RES_BAD_NUMBER = -2;
|
private long RES_BAD_NUMBER = -2;
|
||||||
private long RES_MMS_EXCEPTION = -3;
|
private long RES_MMS_EXCEPTION = -3;
|
||||||
|
@ -664,8 +662,7 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||||
existingContacts.addAll(recipientList);
|
existingContacts.addAll(recipientList);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final GroupDatabase.Reader groupReader = db.getGroup(groupId);
|
GroupDatabase.GroupRecord group = db.getGroup(groupId);
|
||||||
GroupDatabase.GroupRecord group = groupReader.getNext();
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
existingTitle = group.getTitle();
|
existingTitle = group.getTitle();
|
||||||
final byte[] existingAvatar = group.getAvatar();
|
final byte[] existingAvatar = group.getAvatar();
|
||||||
|
|
|
@ -66,12 +66,16 @@ public class GroupDatabase extends Database {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reader getGroup(byte[] groupId) {
|
public GroupRecord getGroup(byte[] groupId) {
|
||||||
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
|
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
|
||||||
new String[] {GroupUtil.getEncodedId(groupId)},
|
new String[] {GroupUtil.getEncodedId(groupId)},
|
||||||
null, null, null);
|
null, null, null);
|
||||||
|
|
||||||
return new Reader(cursor);
|
Reader reader = new Reader(cursor);
|
||||||
|
GroupRecord record = reader.getNext();
|
||||||
|
|
||||||
|
reader.close();
|
||||||
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipients getGroupMembers(byte[] groupId) {
|
public Recipients getGroupMembers(byte[] groupId) {
|
||||||
|
@ -107,7 +111,6 @@ public class GroupDatabase extends Database {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(GROUP_ID, GroupUtil.getEncodedId(groupId));
|
contentValues.put(GROUP_ID, GroupUtil.getEncodedId(groupId));
|
||||||
contentValues.put(OWNER, owner);
|
contentValues.put(OWNER, owner);
|
||||||
|
|
|
@ -532,9 +532,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.isGroup()) {
|
if (message.isGroup()) {
|
||||||
if (((OutgoingGroupMediaMessage)message).isGroupAdd()) type |= Types.GROUP_ADD_MEMBERS_BIT;
|
if (((OutgoingGroupMediaMessage)message).isGroupUpdate()) type |= Types.GROUP_UPDATE_BIT;
|
||||||
else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT;
|
else if (((OutgoingGroupMediaMessage)message).isGroupQuit()) type |= Types.GROUP_QUIT_BIT;
|
||||||
else if (((OutgoingGroupMediaMessage)message).isGroupModify()) type |= Types.GROUP_MODIFY_BIT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendReq sendRequest = new SendReq();
|
SendReq sendRequest = new SendReq();
|
||||||
|
|
|
@ -41,9 +41,8 @@ public interface MmsSmsColumns {
|
||||||
protected static final long PUSH_MESSAGE_BIT = 0x200000;
|
protected static final long PUSH_MESSAGE_BIT = 0x200000;
|
||||||
|
|
||||||
// Group Message Information
|
// Group Message Information
|
||||||
protected static final long GROUP_ADD_MEMBERS_BIT = 0x10000;
|
protected static final long GROUP_UPDATE_BIT = 0x10000;
|
||||||
protected static final long GROUP_QUIT_BIT = 0x20000;
|
protected static final long GROUP_QUIT_BIT = 0x20000;
|
||||||
protected static final long GROUP_MODIFY_BIT = 0x40000;
|
|
||||||
|
|
||||||
// Encrypted Storage Information
|
// Encrypted Storage Information
|
||||||
protected static final long ENCRYPTION_MASK = 0xFF000000;
|
protected static final long ENCRYPTION_MASK = 0xFF000000;
|
||||||
|
@ -116,12 +115,8 @@ public interface MmsSmsColumns {
|
||||||
return (type & KEY_EXCHANGE_IDENTITY_UPDATE_BIT) != 0;
|
return (type & KEY_EXCHANGE_IDENTITY_UPDATE_BIT) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isGroupAdd(long type) {
|
public static boolean isGroupUpdate(long type) {
|
||||||
return (type & GROUP_ADD_MEMBERS_BIT) != 0;
|
return (type & GROUP_UPDATE_BIT) != 0;
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isGroupModify(long type) {
|
|
||||||
return (type & GROUP_MODIFY_BIT) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isGroupQuit(long type) {
|
public static boolean isGroupQuit(long type) {
|
||||||
|
|
|
@ -262,9 +262,8 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
|
||||||
type |= Types.ENCRYPTION_REMOTE_BIT;
|
type |= Types.ENCRYPTION_REMOTE_BIT;
|
||||||
} else if (message.isGroup()) {
|
} else if (message.isGroup()) {
|
||||||
type |= Types.SECURE_MESSAGE_BIT;
|
type |= Types.SECURE_MESSAGE_BIT;
|
||||||
if (((IncomingGroupMessage)message).isAdd()) type |= Types.GROUP_ADD_MEMBERS_BIT;
|
if (((IncomingGroupMessage)message).isUpdate()) type |= Types.GROUP_UPDATE_BIT;
|
||||||
else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT;
|
else if (((IncomingGroupMessage)message).isQuit()) type |= Types.GROUP_QUIT_BIT;
|
||||||
else if (((IncomingGroupMessage)message).isModify()) type |= Types.GROUP_MODIFY_BIT;
|
|
||||||
} else if (message.isEndSession()) {
|
} else if (message.isEndSession()) {
|
||||||
type |= Types.SECURE_MESSAGE_BIT;
|
type |= Types.SECURE_MESSAGE_BIT;
|
||||||
type |= Types.END_SESSION_BIT;
|
type |= Types.END_SESSION_BIT;
|
||||||
|
|
|
@ -83,12 +83,8 @@ public abstract class DisplayRecord {
|
||||||
return SmsDatabase.Types.isEndSessionType(type);
|
return SmsDatabase.Types.isEndSessionType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupAdd() {
|
public boolean isGroupUpdate() {
|
||||||
return SmsDatabase.Types.isGroupAdd(type);
|
return SmsDatabase.Types.isGroupUpdate(type);
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGroupModify() {
|
|
||||||
return SmsDatabase.Types.isGroupModify(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupQuit() {
|
public boolean isGroupQuit() {
|
||||||
|
@ -96,7 +92,7 @@ public abstract class DisplayRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupAction() {
|
public boolean isGroupAction() {
|
||||||
return isGroupAdd() || isGroupModify() || isGroupQuit();
|
return isGroupUpdate() || isGroupQuit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Body {
|
public static class Body {
|
||||||
|
|
|
@ -84,12 +84,10 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpannableString getDisplayBody() {
|
public SpannableString getDisplayBody() {
|
||||||
if (isGroupAdd()) {
|
if (isGroupUpdate()) {
|
||||||
return emphasisAdded(context.getString(R.string.ConversationItem_group_action_joined, Util.join(GroupUtil.getSerializedArgumentMembers(getBody().getBody()), ", ")));
|
return emphasisAdded(GroupUtil.getDescription(getBody().getBody()));
|
||||||
} else if (isGroupQuit()) {
|
} else if (isGroupQuit()) {
|
||||||
return emphasisAdded(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString()));
|
return emphasisAdded(context.getString(R.string.ConversationItem_group_action_left, getIndividualRecipient().toShortString()));
|
||||||
} else if (isGroupModify()) {
|
|
||||||
return emphasisAdded(context.getString(R.string.ConversationItem_group_action_modify, getIndividualRecipient().toShortString()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SpannableString(getBody().getBody());
|
return new SpannableString(getBody().getBody());
|
||||||
|
|
|
@ -56,12 +56,10 @@ public class ThreadRecord extends DisplayRecord {
|
||||||
// TODO jake is going to fill these in
|
// TODO jake is going to fill these in
|
||||||
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
|
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
|
||||||
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
|
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
|
||||||
} else if (isGroupAdd()) {
|
} else if (isGroupUpdate()) {
|
||||||
return emphasisAdded(Util.join(GroupUtil.getSerializedArgumentMembers(getBody().getBody()), ", ") + " have joined the group");
|
return emphasisAdded(GroupUtil.getDescription(getBody().getBody()));
|
||||||
} else if (isGroupQuit()) {
|
} else if (isGroupQuit()) {
|
||||||
return emphasisAdded(getRecipients().toShortString() + " left the group.");
|
return emphasisAdded(getRecipients().toShortString() + " left the group.");
|
||||||
} else if (isGroupModify()) {
|
|
||||||
return emphasisAdded(getRecipients().toShortString() + " modified the group.");
|
|
||||||
} else if (isKeyExchange()) {
|
} else if (isKeyExchange()) {
|
||||||
return emphasisAdded(context.getString(R.string.ConversationListItem_key_exchange_message));
|
return emphasisAdded(context.getString(R.string.ConversationListItem_key_exchange_message));
|
||||||
} else if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
} else if (SmsDatabase.Types.isFailedDecryptType(type)) {
|
||||||
|
|
|
@ -37,17 +37,11 @@ public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupAdd() {
|
public boolean isGroupUpdate() {
|
||||||
return
|
return group.getType().getNumber() == GroupContext.Type.UPDATE_VALUE;
|
||||||
group.getType().getNumber() == GroupContext.Type.ADD_VALUE ||
|
|
||||||
group.getType().getNumber() == GroupContext.Type.CREATE_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupQuit() {
|
public boolean isGroupQuit() {
|
||||||
return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE;
|
return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isGroupModify() {
|
|
||||||
return group.getType().getNumber() == GroupContext.Type.MODIFY_VALUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,23 +144,17 @@ public class RecipientProvider {
|
||||||
|
|
||||||
private RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
|
private RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
|
||||||
try {
|
try {
|
||||||
GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(context)
|
GroupDatabase.GroupRecord record = DatabaseFactory.getGroupDatabase(context)
|
||||||
.getGroup(GroupUtil.getDecodedId(groupId));
|
.getGroup(GroupUtil.getDecodedId(groupId));
|
||||||
|
|
||||||
GroupDatabase.GroupRecord record;
|
if (record != null) {
|
||||||
|
byte[] avatarBytes = record.getAvatar();
|
||||||
|
Bitmap avatar;
|
||||||
|
|
||||||
try {
|
if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||||
if ((record = reader.getNext()) != null) {
|
else avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
|
||||||
byte[] avatarBytes = record.getAvatar();
|
|
||||||
Bitmap avatar;
|
|
||||||
|
|
||||||
if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultContactPhoto(context);
|
return new RecipientDetails(record.getTitle(), null, avatar);
|
||||||
else avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
|
|
||||||
|
|
||||||
return new RecipientDetails(record.getTitle(), null, avatar);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
reader.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -37,19 +37,17 @@ public class AvatarDownloader {
|
||||||
if (!SendReceiveService.DOWNLOAD_AVATAR_ACTION.equals(intent.getAction()))
|
if (!SendReceiveService.DOWNLOAD_AVATAR_ACTION.equals(intent.getAction()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
byte[] groupId = intent.getByteArrayExtra("group_id");
|
byte[] groupId = intent.getByteArrayExtra("group_id");
|
||||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
GroupDatabase.Reader reader = database.getGroup(groupId);
|
GroupDatabase.GroupRecord record = database.getGroup(groupId);
|
||||||
|
|
||||||
GroupDatabase.GroupRecord record;
|
if (record != null) {
|
||||||
|
|
||||||
while ((record = reader.getNext()) != null) {
|
|
||||||
long avatarId = record.getAvatarId();
|
long avatarId = record.getAvatarId();
|
||||||
byte[] key = record.getAvatarKey();
|
byte[] key = record.getAvatarKey();
|
||||||
String relay = record.getRelay();
|
String relay = record.getRelay();
|
||||||
|
|
||||||
if (avatarId == -1 || key == null) {
|
if (avatarId == -1 || key == null) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File attachment = downloadAttachment(relay, avatarId);
|
File attachment = downloadAttachment(relay, avatarId);
|
||||||
|
|
159
src/org/thoughtcrime/securesms/service/GroupReceiver.java
Normal file
159
src/org/thoughtcrime/securesms/service/GroupReceiver.java
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
|
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
||||||
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||||
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||||
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
|
public class GroupReceiver {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public GroupReceiver(Context context) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(MasterSecret masterSecret,
|
||||||
|
IncomingPushMessage message,
|
||||||
|
PushMessageContent messageContent,
|
||||||
|
boolean secure)
|
||||||
|
{
|
||||||
|
if (!messageContent.getGroup().hasId()) {
|
||||||
|
Log.w("GroupReceiver", "Received group message with no id! Ignoring...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!secure) {
|
||||||
|
Log.w("GroupReceiver", "Received insecure group push action! Ignoring...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
GroupContext group = messageContent.getGroup();
|
||||||
|
byte[] id = group.getId().toByteArray();
|
||||||
|
int type = group.getType().getNumber();
|
||||||
|
GroupRecord record = database.getGroup(id);
|
||||||
|
|
||||||
|
if (record != null && type == GroupContext.Type.UPDATE_VALUE) {
|
||||||
|
handleGroupUpdate(masterSecret, message, group, record);
|
||||||
|
} else if (record == null && type == GroupContext.Type.UPDATE_VALUE) {
|
||||||
|
handleGroupCreate(masterSecret, message, group);
|
||||||
|
} else if (type == GroupContext.Type.QUIT_VALUE) {
|
||||||
|
handleGroupLeave(masterSecret, message, group, record);
|
||||||
|
} else if (type == GroupContext.Type.UNKNOWN_VALUE) {
|
||||||
|
Log.w("GroupReceiver", "Received unknown type, ignoring...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGroupCreate(MasterSecret masterSecret,
|
||||||
|
IncomingPushMessage message,
|
||||||
|
GroupContext group)
|
||||||
|
{
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
byte[] id = group.getId().toByteArray();
|
||||||
|
|
||||||
|
database.create(id, message.getSource(), group.getName(), group.getMembersList(),
|
||||||
|
group.getAvatar(), message.getRelay());
|
||||||
|
|
||||||
|
storeMessage(masterSecret, message, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGroupUpdate(MasterSecret masterSecret,
|
||||||
|
IncomingPushMessage message,
|
||||||
|
GroupContext group,
|
||||||
|
GroupRecord groupRecord)
|
||||||
|
{
|
||||||
|
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
byte[] id = group.getId().toByteArray();
|
||||||
|
|
||||||
|
Set<String> recordMembers = new HashSet<String>(groupRecord.getMembers());
|
||||||
|
Set<String> messageMembers = new HashSet<String>(group.getMembersList());
|
||||||
|
|
||||||
|
Set<String> addedMembers = new HashSet<String>(messageMembers);
|
||||||
|
addedMembers.removeAll(recordMembers);
|
||||||
|
|
||||||
|
Set<String> missingMembers = new HashSet<String>(recordMembers);
|
||||||
|
missingMembers.removeAll(messageMembers);
|
||||||
|
|
||||||
|
if (addedMembers.size() > 0) {
|
||||||
|
Set<String> unionMembers = new HashSet<String>(recordMembers);
|
||||||
|
unionMembers.addAll(messageMembers);
|
||||||
|
database.add(id, message.getSource(), new LinkedList<String>(unionMembers));
|
||||||
|
|
||||||
|
group = group.toBuilder().clearMembers().addAllMembers(addedMembers).build();
|
||||||
|
} else {
|
||||||
|
group = group.toBuilder().clearMembers().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingMembers.size() > 0) {
|
||||||
|
// TODO We should tell added and missing about each-other.
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.hasName() || group.hasAvatar()) {
|
||||||
|
database.update(id, message.getSource(), group.getName(), group.getAvatar());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.hasName() && group.getName() != null && group.getName().equals(groupRecord.getTitle())) {
|
||||||
|
group = group.toBuilder().clearName().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
storeMessage(masterSecret, message, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGroupLeave(MasterSecret masterSecret,
|
||||||
|
IncomingPushMessage message,
|
||||||
|
GroupContext group,
|
||||||
|
GroupRecord record)
|
||||||
|
{
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
byte[] id = group.getId().toByteArray();
|
||||||
|
List<String> members = record.getMembers();
|
||||||
|
|
||||||
|
if (members.contains(message.getSource())) {
|
||||||
|
database.remove(id, message.getSource());
|
||||||
|
|
||||||
|
storeMessage(masterSecret, message, group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void storeMessage(MasterSecret masterSecret, IncomingPushMessage message, GroupContext group) {
|
||||||
|
if (group.hasAvatar()) {
|
||||||
|
Intent intent = new Intent(context, SendReceiveService.class);
|
||||||
|
intent.setAction(SendReceiveService.DOWNLOAD_AVATAR_ACTION);
|
||||||
|
intent.putExtra("group_id", group.getId().toByteArray());
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||||
|
String body = Base64.encodeBytes(group.toByteArray());
|
||||||
|
IncomingTextMessage incoming = new IncomingTextMessage(message, body, group);
|
||||||
|
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, group, body);
|
||||||
|
|
||||||
|
Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);
|
||||||
|
smsDatabase.updateMessageBody(masterSecret, messageAndThreadId.first, body);
|
||||||
|
|
||||||
|
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
|
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
|
@ -22,7 +21,6 @@ import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
|
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
|
|
||||||
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
|
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
|
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
|
||||||
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
||||||
|
@ -38,11 +36,9 @@ import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
||||||
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
||||||
import org.whispersystems.textsecure.storage.RecipientDevice;
|
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||||
import org.whispersystems.textsecure.storage.Session;
|
import org.whispersystems.textsecure.storage.Session;
|
||||||
import org.whispersystems.textsecure.util.Base64;
|
|
||||||
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
import ws.com.google.android.mms.MmsException;
|
||||||
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
|
||||||
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext.Type;
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext.Type;
|
||||||
|
|
||||||
public class PushReceiver {
|
public class PushReceiver {
|
||||||
|
@ -51,10 +47,12 @@ public class PushReceiver {
|
||||||
public static final int RESULT_NO_SESSION = 1;
|
public static final int RESULT_NO_SESSION = 1;
|
||||||
public static final int RESULT_DECRYPT_FAILED = 2;
|
public static final int RESULT_DECRYPT_FAILED = 2;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private final GroupReceiver groupReceiver;
|
||||||
|
|
||||||
public PushReceiver(Context context) {
|
public PushReceiver(Context context) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
|
this.groupReceiver = new GroupReceiver(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(MasterSecret masterSecret, Intent intent) {
|
public void process(MasterSecret masterSecret, Intent intent) {
|
||||||
|
@ -160,7 +158,7 @@ public class PushReceiver {
|
||||||
handleEndSessionMessage(masterSecret, message, messageContent);
|
handleEndSessionMessage(masterSecret, message, messageContent);
|
||||||
} else if (messageContent.hasGroup() && messageContent.getGroup().getType().getNumber() != Type.DELIVER_VALUE) {
|
} else if (messageContent.hasGroup() && messageContent.getGroup().getType().getNumber() != Type.DELIVER_VALUE) {
|
||||||
Log.w("PushReceiver", "Received push group message...");
|
Log.w("PushReceiver", "Received push group message...");
|
||||||
handleReceivedGroupMessage(masterSecret, message, messageContent, secure);
|
groupReceiver.process(masterSecret, message, messageContent, secure);
|
||||||
} else if (messageContent.getAttachmentsCount() > 0) {
|
} else if (messageContent.getAttachmentsCount() > 0) {
|
||||||
Log.w("PushReceiver", "Received push media message...");
|
Log.w("PushReceiver", "Received push media message...");
|
||||||
handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
|
handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
|
||||||
|
@ -174,57 +172,6 @@ public class PushReceiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReceivedGroupMessage(MasterSecret masterSecret,
|
|
||||||
IncomingPushMessage message,
|
|
||||||
PushMessageContent messageContent,
|
|
||||||
boolean secure)
|
|
||||||
{
|
|
||||||
if (!messageContent.getGroup().hasId()) {
|
|
||||||
Log.w("PushReceiver", "Received group message with no id!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!secure) {
|
|
||||||
Log.w("PushReceiver", "Received insecure group push action!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
|
||||||
GroupContext group = messageContent.getGroup();
|
|
||||||
byte[] id = group.getId().toByteArray();
|
|
||||||
int type = group.getType().getNumber();
|
|
||||||
|
|
||||||
if (type == Type.CREATE_VALUE) {
|
|
||||||
database.create(id, message.getSource(), group.getName(), group.getMembersList(), group.getAvatar(), message.getRelay());
|
|
||||||
} else if (type == Type.ADD_VALUE) {
|
|
||||||
database.add(id, message.getSource(), group.getMembersList());
|
|
||||||
} else if (type == Type.QUIT_VALUE) {
|
|
||||||
database.remove(id, message.getSource());
|
|
||||||
} else if (type == Type.MODIFY_VALUE) {
|
|
||||||
database.update(id, message.getSource(), group.getName(), group.getAvatar());
|
|
||||||
} else if (type == Type.UNKNOWN_VALUE) {
|
|
||||||
Log.w("PushReceiver", "Receied group message from unknown type: " + type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.hasAvatar()) {
|
|
||||||
Intent intent = new Intent(context, SendReceiveService.class);
|
|
||||||
intent.setAction(SendReceiveService.DOWNLOAD_AVATAR_ACTION);
|
|
||||||
intent.putExtra("group_id", group.getId().toByteArray());
|
|
||||||
context.startService(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
EncryptingSmsDatabase smsDatabase = DatabaseFactory.getEncryptingSmsDatabase(context);
|
|
||||||
String body = Base64.encodeBytes(group.toByteArray());
|
|
||||||
IncomingTextMessage incoming = new IncomingTextMessage(message, body, group);
|
|
||||||
IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, group, body);
|
|
||||||
|
|
||||||
Pair<Long, Long> messageAndThreadId = smsDatabase.insertMessageInbox(masterSecret, groupMessage);
|
|
||||||
smsDatabase.updateMessageBody(masterSecret, messageAndThreadId.first, body);
|
|
||||||
|
|
||||||
MessageNotifier.updateNotification(context, masterSecret, messageAndThreadId.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleEndSessionMessage(MasterSecret masterSecret,
|
private void handleEndSessionMessage(MasterSecret masterSecret,
|
||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
PushMessageContent messageContent)
|
PushMessageContent messageContent)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.sms;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.whispersystems.textsecure.push.PushMessageProtos;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -28,20 +27,14 @@ public class IncomingGroupMessage extends IncomingTextMessage {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdd() {
|
public boolean isUpdate() {
|
||||||
return
|
return groupContext.getType().getNumber() == GroupContext.Type.UPDATE_VALUE;
|
||||||
groupContext.getType().getNumber() == GroupContext.Type.ADD_VALUE ||
|
|
||||||
groupContext.getType().getNumber() == GroupContext.Type.CREATE_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isQuit() {
|
public boolean isQuit() {
|
||||||
return groupContext.getType().getNumber() == GroupContext.Type.QUIT_VALUE;
|
return groupContext.getType().getNumber() == GroupContext.Type.QUIT_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isModify() {
|
|
||||||
return groupContext.getType().getNumber() == GroupContext.Type.MODIFY_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IncomingGroupMessage createForQuit(String groupId, String user) throws IOException {
|
public static IncomingGroupMessage createForQuit(String groupId, String user) throws IOException {
|
||||||
IncomingTextMessage base = new IncomingTextMessage(user, groupId);
|
IncomingTextMessage base = new IncomingTextMessage(user, groupId);
|
||||||
GroupContext context = GroupContext.newBuilder()
|
GroupContext context = GroupContext.newBuilder()
|
||||||
|
|
|
@ -250,8 +250,7 @@ public class PushTransport extends BaseTransport {
|
||||||
groupBuilder.setId(ByteString.copyFrom(groupId));
|
groupBuilder.setId(ByteString.copyFrom(groupId));
|
||||||
groupBuilder.setType(GroupContext.Type.DELIVER);
|
groupBuilder.setType(GroupContext.Type.DELIVER);
|
||||||
|
|
||||||
if (MmsSmsColumns.Types.isGroupAdd(message.getDatabaseMessageBox()) ||
|
if (MmsSmsColumns.Types.isGroupUpdate(message.getDatabaseMessageBox()) ||
|
||||||
MmsSmsColumns.Types.isGroupModify(message.getDatabaseMessageBox()) ||
|
|
||||||
MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()))
|
MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()))
|
||||||
{
|
{
|
||||||
if (messageBody != null && messageBody.trim().length() > 0) {
|
if (messageBody != null && messageBody.trim().length() > 0) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.util;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
import org.whispersystems.textsecure.util.Base64;
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
import org.whispersystems.textsecure.util.Hex;
|
import org.whispersystems.textsecure.util.Hex;
|
||||||
|
@ -33,34 +34,32 @@ public class GroupUtil {
|
||||||
return groupId.startsWith(ENCODED_GROUP_PREFIX);
|
return groupId.startsWith(ENCODED_GROUP_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isMetaGroupAction(int groupAction) {
|
public static String getDescription(String encodedGroup) {
|
||||||
return groupAction > 0
|
if (encodedGroup == null) {
|
||||||
&& groupAction != GroupContext.Type.DELIVER_VALUE;
|
return "Group updated.";
|
||||||
}
|
|
||||||
|
|
||||||
public static String serializeArguments(byte[] id, String name, List<String> members) {
|
|
||||||
return Base64.encodeBytes(GroupContext.newBuilder()
|
|
||||||
.setId(ByteString.copyFrom(id))
|
|
||||||
.setName(name)
|
|
||||||
.addAllMembers(members)
|
|
||||||
.build().toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String serializeArguments(GroupContext context) {
|
|
||||||
return Base64.encodeBytes(context.toByteArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> getSerializedArgumentMembers(String serialized) {
|
|
||||||
if (serialized == null) {
|
|
||||||
return new LinkedList<String>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GroupContext context = GroupContext.parseFrom(Base64.decode(serialized));
|
String description = "";
|
||||||
return context.getMembersList();
|
GroupContext context = GroupContext.parseFrom(Base64.decode(encodedGroup));
|
||||||
|
List<String> members = context.getMembersList();
|
||||||
|
String title = context.getName();
|
||||||
|
|
||||||
|
if (!members.isEmpty()) {
|
||||||
|
description += org.whispersystems.textsecure.util.Util.join(members, ", ") + " joined the group.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (title != null && !title.trim().isEmpty()) {
|
||||||
|
description += " Updated title to '" + title + "'.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return description;
|
||||||
|
} catch (InvalidProtocolBufferException e) {
|
||||||
|
Log.w("GroupUtil", e);
|
||||||
|
return "Group updated.";
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w("GroupUtil", e);
|
Log.w("GroupUtil", e);
|
||||||
return new LinkedList<String>();
|
return "Group updated.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue