Refactor group database model and flow.

1) Use existing DB types instead of adding new columns.

2) Store group attributes in message body, like everything else.
This commit is contained in:
Moxie Marlinspike 2014-02-19 21:06:54 -08:00
parent 0cdc6fd87d
commit 9614dc9055
28 changed files with 432 additions and 381 deletions

View File

@ -81,6 +81,8 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingSecureMediaMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.BitmapDecodingException;
@ -922,20 +924,26 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
if (recipients == null)
throw new RecipientFormattingException("Badly formatted");
String body = getMessage();
String body = getMessage();
long allocatedThreadId;
if ((!recipients.isSingleRecipient() || recipients.isEmailRecipient()) && !isMmsEnabled) {
handleManualMmsRequired();
return;
} else if (attachmentManager.isAttachmentPresent()) {
allocatedThreadId = MessageSender.sendMms(ConversationActivity.this, masterSecret, recipients,
threadId, attachmentManager.getSlideDeck(), body,
distributionType, isEncryptedConversation && !forcePlaintext);
} else if (recipients.isEmailRecipient() || !recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
allocatedThreadId = MessageSender.sendMms(ConversationActivity.this, masterSecret, recipients,
threadId, new SlideDeck(), body, distributionType,
isEncryptedConversation && !forcePlaintext);
} else if (attachmentManager.isAttachmentPresent() || !recipients.isSingleRecipient() || recipients.isGroupRecipient()) {
SlideDeck slideDeck;
if (attachmentManager.isAttachmentPresent()) slideDeck = attachmentManager.getSlideDeck();
else slideDeck = new SlideDeck();
OutgoingMediaMessage outgoingMessage = new OutgoingMediaMessage(this, recipients, slideDeck,
body, distributionType);
if (isEncryptedConversation && !forcePlaintext) {
outgoingMessage = new OutgoingSecureMediaMessage(outgoingMessage);
}
allocatedThreadId = MessageSender.send(this, masterSecret, outgoingMessage, threadId);
} else {
OutgoingTextMessage message;
@ -949,6 +957,7 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
allocatedThreadId = MessageSender.send(ConversationActivity.this, masterSecret,
message, threadId);
}
sendComplete(recipients, allocatedThreadId, allocatedThreadId != this.threadId);
} catch (RecipientFormattingException ex) {
Toast.makeText(ConversationActivity.this,

View File

@ -123,8 +123,9 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsColumns.ID));
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
if (GroupUtil.isMetaGroupAction(messageRecord.getGroupAction())) return MESSAGE_TYPE_GROUP_ACTION;
if (messageRecord.isOutgoing()) return MESSAGE_TYPE_OUTGOING;
if (messageRecord.isGroupAction()) return MESSAGE_TYPE_GROUP_ACTION;
else if (messageRecord.isOutgoing()) return MESSAGE_TYPE_OUTGOING;
else return MESSAGE_TYPE_INCOMING;
}

View File

@ -31,19 +31,16 @@ import android.os.Handler;
import android.os.Message;
import android.provider.Contacts.Intents;
import android.provider.ContactsContract.QuickContact;
import org.thoughtcrime.securesms.util.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.webkit.MimeTypeMap;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
@ -53,10 +50,11 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.Emoji;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.util.FutureTaskListener;
import org.whispersystems.textsecure.util.ListenableFutureTask;
import org.whispersystems.textsecure.util.Util;
import java.io.File;
import java.io.FileOutputStream;
@ -64,8 +62,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
/**
* A view that displays an individual conversation item within a conversation
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
@ -145,7 +141,7 @@ public class ConversationItem extends LinearLayout {
setBodyText(messageRecord);
if (!GroupUtil.isMetaGroupAction(messageRecord.getGroupAction())) {
if (!messageRecord.isGroupAction()) {
setStatusIcons(messageRecord);
setContactPhoto(messageRecord);
setGroupMessageStatus(messageRecord);
@ -175,22 +171,6 @@ public class ConversationItem extends LinearLayout {
/// MessageRecord Attribute Parsers
private void setBodyText(MessageRecord messageRecord) {
switch (messageRecord.getGroupAction()) {
case GroupContext.Type.QUIT_VALUE:
bodyText.setText(context.getString(R.string.ConversationItem_group_action_left,
messageRecord.getIndividualRecipient().toShortString()));
return;
case GroupContext.Type.ADD_VALUE:
case GroupContext.Type.CREATE_VALUE:
bodyText.setText(context.getString(R.string.ConversationItem_group_action_joined,
Util.join(GroupUtil.getSerializedArgumentMembers(messageRecord.getGroupActionArguments()), ", ")));
return;
case GroupContext.Type.MODIFY_VALUE:
bodyText.setText(context.getString(R.string.ConversationItem_group_action_modify,
messageRecord.getIndividualRecipient()));
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(), Emoji.EMOJI_LARGE),
TextView.BufferType.SPANNABLE);

View File

@ -17,15 +17,14 @@ import android.util.Pair;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
@ -37,6 +36,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage;
@ -367,8 +367,8 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
}
private Pair<Long, Recipients> handleCreatePushGroup(String groupName,
byte[] avatar,
Set<Recipient> members)
byte[] avatar,
Set<Recipient> members)
throws InvalidNumberException, MmsException
{
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
@ -378,17 +378,23 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
List<String> memberE164Numbers = getE164Numbers(members);
String groupRecipientId = GroupUtil.getEncodedId(groupId);
String groupActionArguments = GroupUtil.serializeArguments(groupId, groupName, memberE164Numbers);
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
memberE164Numbers, null, null);
groupDatabase.updateAvatar(groupId, avatar);
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
return new Pair<Long, Recipients>(MessageSender.sendGroupAction(this, masterSecret, groupRecipient, -1,
GroupContext.Type.CREATE_VALUE,
groupActionArguments, avatar), groupRecipient);
GroupContext context = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId))
.setType(GroupContext.Type.CREATE)
.setName(groupName)
.addAllMembers(memberE164Numbers)
.build();
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar);
long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1);
return new Pair<Long, Recipients>(threadId, groupRecipient);
} catch (RecipientFormattingException e) {
throw new AssertionError(e);
} catch (MmsException e) {

View File

@ -647,12 +647,6 @@ public class DatabaseFactory {
db.execSQL("ALTER TABLE push ADD COLUMN device_id INTEGER DEFAULT 1;");
db.execSQL("ALTER TABLE sms ADD COLUMN address_device_id INTEGER DEFAULT 1;");
db.execSQL("ALTER TABLE mms ADD COLUMN address_device_id INTEGER DEFAULT 1;");
db.execSQL("ALTER TABLE sms ADD COLUMN group_action INTEGER DEFAULT -1;");
db.execSQL("ALTER TABLE sms ADD COLUMN group_action_arguments TEXT;");
db.execSQL("ALTER TABLE mms ADD COLuMN group_action INTEGER DEFAULT -1;");
db.execSQL("ALTER TABLE mms ADD COLUMN group_action_arguments TEXT;");
db.execSQL("ALTER TABLE thread ADD COLUMN group_action INTEGER DEFAULT -1;");
db.execSQL("ALTER TABLE thread ADD COLUMN group_action_arguments TEXT;");
}
db.setTransactionSuccessful();

View File

@ -72,7 +72,7 @@ public class GroupDatabase extends Database {
}
public Recipients getGroupMembers(byte[] groupId) {
List<String> members = getCurrentMembers(groupId);
List<String> members = getCurrentMembers(groupId);
List<Recipient> recipients = new LinkedList<Recipient>();
for (String member : members) {
@ -94,12 +94,17 @@ public class GroupDatabase extends Database {
List<String> filteredMembers = new LinkedList<String>();
String localNumber = TextSecurePreferences.getLocalNumber(context);
if (!localNumber.equals(owner)) {
filteredMembers.add(owner);
}
for (String member : members) {
if (!member.equals(localNumber)) {
filteredMembers.add(member);
}
}
ContentValues contentValues = new ContentValues();
contentValues.put(GROUP_ID, GroupUtil.getEncodedId(groupId));
contentValues.put(OWNER, owner);

View File

@ -26,7 +26,8 @@ import android.util.Log;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
@ -57,6 +58,7 @@ import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.CharacterSets;
@ -116,7 +118,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + RETRIEVE_STATUS + " INTEGER, " +
RETRIEVE_TEXT + " TEXT, " + RETRIEVE_TEXT_CS + " INTEGER, " + READ_STATUS + " INTEGER, " +
CONTENT_CLASS + " INTEGER, " + RESPONSE_TEXT + " TEXT, " + DELIVERY_TIME + " INTEGER, " +
DELIVERY_REPORT + " INTEGER, " + GROUP_ACTION + " INTEGER DEFAULT -1, " + GROUP_ACTION_ARGUMENTS + " TEXT);";
DELIVERY_REPORT + " INTEGER);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@ -133,7 +135,6 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
MESSAGE_SIZE, PRIORITY, REPORT_ALLOWED, STATUS, TRANSACTION_ID, RETRIEVE_STATUS,
RETRIEVE_TEXT, RETRIEVE_TEXT_CS, READ_STATUS, CONTENT_CLASS, RESPONSE_TEXT,
DELIVERY_TIME, DELIVERY_REPORT, BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
GROUP_ACTION, GROUP_ACTION_ARGUMENTS
};
public static final ExecutorService slideResolver = org.thoughtcrime.securesms.util.Util.newSingleThreadedLifoExecutor();
@ -346,11 +347,9 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
while (cursor.moveToNext()) {
messageId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_ACTION));
String groupActionArguments = cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ACTION_ARGUMENTS));
PduHeaders headers = getHeadersFromCursor(cursor);
long outboxType = cursor.getLong(cursor.getColumnIndexOrThrow(MESSAGE_BOX));
String messageText = cursor.getString(cursor.getColumnIndexOrThrow(BODY));
PduHeaders headers = getHeadersFromCursor(cursor);
addr.getAddressesForId(messageId, headers);
PduBody body = getPartsAsBody(partDatabase.getParts(messageId, true));
@ -365,7 +364,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
Log.w("MmsDatabase", e);
}
requests[i++] = new SendReq(headers, body, messageId, outboxType, groupAction, groupActionArguments);
requests[i++] = new SendReq(headers, body, messageId, outboxType);
}
return requests;
@ -409,8 +408,6 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(DATE_RECEIVED, System.currentTimeMillis() / 1000);
contentValues.put(READ, unread ? 0 : 1);
contentValues.put(GROUP_ACTION, retrieved.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, retrieved.getGroupActionArguments());
if (!contentValues.containsKey(DATE_SENT)) {
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
@ -502,24 +499,46 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
Trimmer.trimThread(context, threadId);
}
public long insertMessageOutbox(MasterSecret masterSecret, SendReq sendRequest,
long threadId, boolean isSecure)
public long insertMessageOutbox(MasterSecret masterSecret, OutgoingMediaMessage message, long threadId)
throws MmsException
{
long type = Types.BASE_OUTBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT;
PduHeaders headers = sendRequest.getPduHeaders();
ContentValues contentValues = getContentValuesFromHeader(headers);
long type = Types.BASE_OUTBOX_TYPE | Types.ENCRYPTION_SYMMETRIC_BIT;
if (isSecure) {
if (message.isSecure()) {
type |= Types.SECURE_MESSAGE_BIT;
}
if (message.isGroup()) {
if (((OutgoingGroupMediaMessage)message).isGroupAdd()) type |= Types.GROUP_ADD_MEMBERS_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();
sendRequest.setDate(System.currentTimeMillis() / 1000L);
sendRequest.setBody(message.getPduBody());
sendRequest.setContentType(ContentType.MULTIPART_MIXED.getBytes());
String[] recipientsArray = message.getRecipients().toNumberStringArray(true);
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
if (message.getRecipients().isSingleRecipient()) {
sendRequest.setTo(encodedNumbers);
} else if (message.getDistributionType() == ThreadDatabase.DistributionTypes.BROADCAST) {
sendRequest.setBcc(encodedNumbers);
} else if (message.getDistributionType() == ThreadDatabase.DistributionTypes.CONVERSATION ||
message.getDistributionType() == 0)
{
sendRequest.setTo(encodedNumbers);
}
PduHeaders headers = sendRequest.getPduHeaders();
ContentValues contentValues = getContentValuesFromHeader(headers);
contentValues.put(MESSAGE_BOX, type);
contentValues.put(THREAD_ID, threadId);
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
contentValues.put(GROUP_ACTION, sendRequest.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, sendRequest.getGroupActionArguments());
contentValues.remove(ADDRESS);
long messageId = insertMediaMessage(masterSecret, sendRequest.getPduHeaders(),
@ -811,8 +830,6 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE));
long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.GROUP_ACTION));
String groupActionArgs = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.GROUP_ACTION_ARGUMENTS));
byte[]contentLocationBytes = null;
byte[]transactionIdBytes = null;
@ -827,7 +844,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
addressDeviceId, dateSent, dateReceived, threadId,
contentLocationBytes, messageSize, expiry, status,
transactionIdBytes, mailbox, groupAction, groupActionArgs);
transactionIdBytes, mailbox);
}
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
@ -838,8 +855,6 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.GROUP_ACTION));
String groupActionArgs = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.GROUP_ACTION_ARGUMENTS));
DisplayRecord.Body body = getBody(cursor);
int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
Recipients recipients = getRecipientsFor(address);
@ -848,7 +863,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
addressDeviceId, dateSent, dateReceived, threadId, body,
slideDeck, partCount, box, groupAction, groupActionArgs);
slideDeck, partCount, box);
}
private Recipients getRecipientsFor(String address) {

View File

@ -10,8 +10,6 @@ public interface MmsSmsColumns {
public static final String BODY = "body";
public static final String ADDRESS = "address";
public static final String ADDRESS_DEVICE_ID = "address_device_id";
public static final String GROUP_ACTION = "group_action";
public static final String GROUP_ACTION_ARGUMENTS = "group_action_arguments";
public static class Types {
protected static final long TOTAL_MASK = 0xFFFFFFFF;
@ -41,6 +39,11 @@ public interface MmsSmsColumns {
protected static final long SECURE_MESSAGE_BIT = 0x800000;
protected static final long END_SESSION_BIT = 0x400000;
// Group Message Information
protected static final long GROUP_ADD_MEMBERS_BIT = 0x10000;
protected static final long GROUP_QUIT_BIT = 0x20000;
protected static final long GROUP_MODIFY_BIT = 0x40000;
// Encrypted Storage Information
protected static final long ENCRYPTION_MASK = 0xFF000000;
protected static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000;
@ -108,6 +111,18 @@ public interface MmsSmsColumns {
return (type & KEY_EXCHANGE_IDENTITY_UPDATE_BIT) != 0;
}
public static boolean isGroupAdd(long type) {
return (type & GROUP_ADD_MEMBERS_BIT) != 0;
}
public static boolean isGroupModify(long type) {
return (type & GROUP_MODIFY_BIT) != 0;
}
public static boolean isGroupQuit(long type) {
return (type & GROUP_QUIT_BIT) != 0;
}
public static boolean isSymmetricEncryption(long type) {
return (type & ENCRYPTION_SYMMETRIC_BIT) != 0;
}

View File

@ -49,8 +49,7 @@ public class MmsSmsDatabase extends Database {
SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
MmsDatabase.STATUS, MmsSmsColumns.GROUP_ACTION,
MmsSmsColumns.GROUP_ACTION_ARGUMENTS, TRANSPORT};
MmsDatabase.STATUS, TRANSPORT};
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
@ -72,8 +71,7 @@ public class MmsSmsDatabase extends Database {
SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
MmsDatabase.STATUS, MmsSmsColumns.GROUP_ACTION,
MmsSmsColumns.GROUP_ACTION_ARGUMENTS, TRANSPORT};
MmsDatabase.STATUS, TRANSPORT};
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
@ -91,8 +89,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
MmsDatabase.STATUS, MmsSmsColumns.GROUP_ACTION,
MmsSmsColumns.GROUP_ACTION_ARGUMENTS, TRANSPORT};
MmsDatabase.STATUS, TRANSPORT};
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " ASC";
String selection = MmsSmsColumns.READ + " = 0";
@ -115,7 +112,6 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
MmsSmsColumns.GROUP_ACTION, MmsSmsColumns.GROUP_ACTION_ARGUMENTS,
TRANSPORT};
String[] smsProjection = {SmsDatabase.DATE_SENT + " * 1 AS " + MmsSmsColumns.NORMALIZED_DATE_SENT,
@ -125,7 +121,6 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
MmsSmsColumns.GROUP_ACTION, MmsSmsColumns.GROUP_ACTION_ARGUMENTS,
TRANSPORT};
@ -154,8 +149,6 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsDatabase.TRANSACTION_ID);
mmsColumnsPresent.add(MmsDatabase.MESSAGE_SIZE);
mmsColumnsPresent.add(MmsDatabase.EXPIRY);
mmsColumnsPresent.add(MmsSmsColumns.GROUP_ACTION);
mmsColumnsPresent.add(MmsSmsColumns.GROUP_ACTION_ARGUMENTS);
mmsColumnsPresent.add(MmsDatabase.STATUS);
Set<String> smsColumnsPresent = new HashSet<String>();
@ -169,8 +162,6 @@ public class MmsSmsDatabase extends Database {
smsColumnsPresent.add(SmsDatabase.SUBJECT);
smsColumnsPresent.add(SmsDatabase.DATE_SENT);
smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED);
smsColumnsPresent.add(MmsSmsColumns.GROUP_ACTION);
smsColumnsPresent.add(MmsSmsColumns.GROUP_ACTION_ARGUMENTS);
smsColumnsPresent.add(SmsDatabase.STATUS);
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 2, MMS_TRANSPORT, selection, null, null, null);
@ -225,7 +216,7 @@ public class MmsSmsDatabase extends Database {
public MessageRecord getCurrent() {
String type = cursor.getString(cursor.getColumnIndexOrThrow(TRANSPORT));
if (type.equals(MmsSmsDatabase.MMS_TRANSPORT)) {
if (MmsSmsDatabase.MMS_TRANSPORT.equals(type)) {
return mmsReader.getCurrent();
} else {
return smsReader.getCurrent();

View File

@ -32,6 +32,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
@ -65,8 +66,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " +
DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " +
SUBJECT + " TEXT, " + BODY + " TEXT, " + SERVICE_CENTER + " TEXT, " +
GROUP_ACTION + " INTEGER DEFAULT -1, " + GROUP_ACTION_ARGUMENTS + " TEXT);";
SUBJECT + " TEXT, " + BODY + " TEXT, " + SERVICE_CENTER + " TEXT);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@ -80,7 +80,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
DATE_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
PROTOCOL, READ, STATUS, TYPE,
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, GROUP_ACTION, GROUP_ACTION_ARGUMENTS
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER
};
public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
@ -256,9 +256,14 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
} else if (message.isSecureMessage()) {
type |= Types.SECURE_MESSAGE_BIT;
type |= Types.ENCRYPTION_REMOTE_BIT;
} else if (message.isEndSession()) {
type |= Types.END_SESSION_BIT;
} else if (message.isGroup()) {
type |= Types.SECURE_MESSAGE_BIT;
if (((IncomingGroupMessage)message).isAdd()) type |= Types.GROUP_ADD_MEMBERS_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()) {
type |= Types.SECURE_MESSAGE_BIT;
type |= Types.END_SESSION_BIT;
type |= Types.ENCRYPTION_REMOTE_BIT;
}
@ -308,8 +313,6 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
values.put(BODY, message.getMessageBody());
values.put(TYPE, type);
values.put(THREAD_ID, threadId);
values.put(GROUP_ACTION, message.getGroupAction());
values.put(GROUP_ACTION_ARGUMENTS, message.getGroupActionArgument());
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, null, values);
@ -346,8 +349,6 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
contentValues.put(DATE_SENT, date);
contentValues.put(READ, 1);
contentValues.put(TYPE, type);
contentValues.put(GROUP_ACTION, message.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, message.getGroupActionArguments());
SQLiteDatabase db = databaseHelper.getWritableDatabase();
messageIds.add(db.insert(TABLE_NAME, ADDRESS, contentValues));
@ -504,8 +505,6 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.GROUP_ACTION));
String groupActionArgs = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.GROUP_ACTION_ARGUMENTS));
Recipients recipients = getRecipientsFor(address);
DisplayRecord.Body body = getBody(cursor);
@ -513,7 +512,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
recipients.getPrimaryRecipient(),
addressDeviceId,
dateSent, dateReceived, type,
threadId, status, groupAction, groupActionArgs);
threadId, status);
}
private Recipients getRecipientsFor(String address) {

View File

@ -53,15 +53,12 @@ public class ThreadDatabase extends Database {
private static final String ERROR = "error";
private static final String HAS_ATTACHMENT = "has_attachment";
public static final String SNIPPET_TYPE = "snippet_type";
private static final String GROUP_ACTION = "group_action";
private static final String GROUP_ACTION_ARG = "group_action_arguments";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " +
RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " +
READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + GROUP_ACTION + " INTEGER DEFAULT -1, " +
GROUP_ACTION_ARG + " TEXT, " + HAS_ATTACHMENT + " INTEGER DEFAULT 0);";
SNIPPET_TYPE + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
@ -117,16 +114,13 @@ public class ThreadDatabase extends Database {
return db.insert(TABLE_NAME, null, contentValues);
}
private void updateThread(long threadId, long count, String body, long date, long type,
int groupAction, String groupActionArguments)
private void updateThread(long threadId, long count, String body, long date, long type)
{
ContentValues contentValues = new ContentValues(3);
contentValues.put(DATE, date - date % 1000);
contentValues.put(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_TYPE, type);
contentValues.put(GROUP_ACTION, groupAction);
contentValues.put(GROUP_ACTION_ARG, groupActionArguments);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
@ -389,8 +383,7 @@ public class ThreadDatabase extends Database {
MessageRecord record = null;
if (reader != null && (record = reader.getNext()) != null) {
updateThread(threadId, count, record.getBody().getBody(), record.getDateReceived(),
record.getType(), record.getGroupAction(), record.getGroupActionArguments());
updateThread(threadId, count, record.getBody().getBody(), record.getDateReceived(), record.getType());
} else {
deleteThread(threadId);
}
@ -446,12 +439,9 @@ public class ThreadDatabase extends Database {
long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE));
int groupAction = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.GROUP_ACTION));
String groupActionArg = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.GROUP_ACTION_ARG));
return new ThreadRecord(context, body, recipients, date, count,
read == 1, threadId, type, distributionType,
groupAction, groupActionArg);
read == 1, threadId, type, distributionType);
}
private DisplayRecord.Body getPlaintextBody(Cursor cursor) {

View File

@ -40,12 +40,9 @@ public abstract class DisplayRecord {
private final long dateReceived;
private final long threadId;
private final Body body;
private final int groupAction;
private final String groupActionArguments;
public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent,
long dateReceived, long threadId, long type, int groupAction,
String groupActionArguments)
long dateReceived, long threadId, long type)
{
this.context = context.getApplicationContext();
this.threadId = threadId;
@ -54,8 +51,6 @@ public abstract class DisplayRecord {
this.dateReceived = dateReceived;
this.type = type;
this.body = body;
this.groupAction = groupAction;
this.groupActionArguments = groupActionArguments;
}
public Body getBody() {
@ -88,12 +83,20 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isEndSessionType(type);
}
public int getGroupAction() {
return groupAction;
public boolean isGroupAdd() {
return SmsDatabase.Types.isGroupAdd(type);
}
public String getGroupActionArguments() {
return groupActionArguments;
public boolean isGroupModify() {
return SmsDatabase.Types.isGroupModify(type);
}
public boolean isGroupQuit() {
return SmsDatabase.Types.isGroupQuit(type);
}
public boolean isGroupAction() {
return isGroupAdd() || isGroupModify() || isGroupQuit();
}
public static class Body {

View File

@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.text.SpannableString;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsDatabase;
@ -44,12 +45,10 @@ public class MediaMmsMessageRecord extends MessageRecord {
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long threadId, Body body,
ListenableFutureTask<SlideDeck> slideDeck,
int partCount, long mailbox, int groupAction,
String groupActionArguments)
int partCount, long mailbox)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox,
groupAction, groupActionArguments);
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox);
this.context = context.getApplicationContext();
this.partCount = partCount;

View File

@ -24,10 +24,13 @@ import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.text.style.TextAppearanceSpan;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.util.Util;
/**
* The base class for message record models that are displayed in
@ -52,10 +55,9 @@ public abstract class MessageRecord extends DisplayRecord {
MessageRecord(Context context, long id, Body body, Recipients recipients,
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived,
long threadId, int deliveryStatus,
long type, int groupAction, String groupActionArguments)
long threadId, int deliveryStatus, long type)
{
super(context, body, recipients, dateSent, dateReceived, threadId, type, groupAction, groupActionArguments);
super(context, body, recipients, dateSent, dateReceived, threadId, type);
this.id = id;
this.individualRecipient = individualRecipient;
this.recipientDeviceId = recipientDeviceId;
@ -84,6 +86,14 @@ public abstract class MessageRecord extends DisplayRecord {
@Override
public SpannableString getDisplayBody() {
if (isGroupAdd()) {
return emphasisAdded(context.getString(R.string.ConversationItem_group_action_joined, Util.join(GroupUtil.getSerializedArgumentMembers(getBody().getBody()), ", ")));
} else if (isGroupQuit()) {
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());
}

View File

@ -44,11 +44,10 @@ public class NotificationMmsMessageRecord extends MessageRecord {
Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long threadId,
byte[] contentLocation, long messageSize, long expiry,
int status, byte[] transactionId, long mailbox,
int groupAction, String groupActionArguments)
int status, byte[] transactionId, long mailbox)
{
super(context, id, new Body("", true), recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox, groupAction, groupActionArguments);
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox);
this.contentLocation = contentLocation;
this.messageSize = messageSize;

View File

@ -25,6 +25,8 @@ import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.protocol.Tag;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.util.Util;
/**
* The message record model which represents standard SMS messages.
@ -41,12 +43,10 @@ public class SmsMessageRecord extends MessageRecord {
int recipientDeviceId,
long dateSent, long dateReceived,
long type, long threadId,
int status, int groupAction,
String groupActionArguments)
int status)
{
super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), type,
groupAction, groupActionArguments);
dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), type);
}
public long getType() {

View File

@ -20,19 +20,13 @@ import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.util.Pair;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.push.PushMessageProtos;
import org.whispersystems.textsecure.util.Util;
import java.util.List;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
/**
* The message record model which represents thread heading messages.
*
@ -48,9 +42,9 @@ public class ThreadRecord extends DisplayRecord {
public ThreadRecord(Context context, Body body, Recipients recipients, long date,
long count, boolean read, long threadId, long snippetType,
int distributionType, int groupAction, String groupActionArg)
int distributionType)
{
super(context, body, recipients, date, date, threadId, snippetType, groupAction, groupActionArg);
super(context, body, recipients, date, date, threadId, snippetType);
this.context = context.getApplicationContext();
this.count = count;
this.read = read;
@ -62,13 +56,11 @@ public class ThreadRecord extends DisplayRecord {
// TODO jake is going to fill these in
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
} else if (getGroupAction() == GroupContext.Type.ADD_VALUE ||
getGroupAction() == GroupContext.Type.CREATE_VALUE)
{
return emphasisAdded(Util.join(GroupUtil.getSerializedArgumentMembers(getGroupActionArguments()), ", ") + " have joined the group");
} else if (getGroupAction() == GroupContext.Type.QUIT_VALUE) {
} else if (isGroupAdd()) {
return emphasisAdded(Util.join(GroupUtil.getSerializedArgumentMembers(getBody().getBody()), ", ") + " have joined the group");
} else if (isGroupQuit()) {
return emphasisAdded(getRecipients().toShortString() + " left the group.");
} else if (getGroupAction() == GroupContext.Type.MODIFY_VALUE) {
} else if (isGroupModify()) {
return emphasisAdded(getRecipients().toShortString() + " modified the group.");
} else if (isKeyExchange()) {
return emphasisAdded(context.getString(R.string.ConversationListItem_key_exchange_message));

View File

@ -0,0 +1,53 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.util.Base64;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduPart;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class OutgoingGroupMediaMessage extends OutgoingSecureMediaMessage {
private final GroupContext group;
public OutgoingGroupMediaMessage(Context context, Recipients recipients,
GroupContext group, byte[] avatar)
{
super(context, recipients, new PduBody(), Base64.encodeBytes(group.toByteArray()),
ThreadDatabase.DistributionTypes.CONVERSATION);
this.group = group;
PduPart part = new PduPart();
part.setData(avatar);
part.setContentType(ContentType.IMAGE_PNG.getBytes());
part.setContentId((System.currentTimeMillis()+"").getBytes());
part.setName(("Image" + System.currentTimeMillis()).getBytes());
body.addPart(part);
}
@Override
public boolean isGroup() {
return true;
}
public boolean isGroupAdd() {
return
group.getType().getNumber() == GroupContext.Type.ADD_VALUE ||
group.getType().getNumber() == GroupContext.Type.CREATE_VALUE;
}
public boolean isGroupQuit() {
return group.getType().getNumber() == GroupContext.Type.QUIT_VALUE;
}
public boolean isGroupModify() {
return group.getType().getNumber() == GroupContext.Type.MODIFY_VALUE;
}
}

View File

@ -0,0 +1,60 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.util.Util;
import ws.com.google.android.mms.pdu.PduBody;
public class OutgoingMediaMessage {
private final Recipients recipients;
protected final PduBody body;
private final int distributionType;
public OutgoingMediaMessage(Context context, Recipients recipients, PduBody body,
String message, int distributionType)
{
this.recipients = recipients;
this.body = body;
this.distributionType = distributionType;
if (!Util.isEmpty(message)) {
this.body.addPart(new TextSlide(context, message).getPart());
}
}
public OutgoingMediaMessage(Context context, Recipients recipients, SlideDeck slideDeck,
String message, int distributionType)
{
this(context, recipients, slideDeck.toPduBody(), message, distributionType);
}
public OutgoingMediaMessage(OutgoingMediaMessage that) {
this.recipients = that.getRecipients();
this.body = that.body;
this.distributionType = that.distributionType;
}
public Recipients getRecipients() {
return recipients;
}
public PduBody getPduBody() {
return body;
}
public int getDistributionType() {
return distributionType;
}
public boolean isSecure() {
return false;
}
public boolean isGroup() {
return false;
}
}

View File

@ -0,0 +1,25 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import org.thoughtcrime.securesms.recipients.Recipients;
import ws.com.google.android.mms.pdu.PduBody;
public class OutgoingSecureMediaMessage extends OutgoingMediaMessage {
public OutgoingSecureMediaMessage(Context context, Recipients recipients, PduBody body,
String message, int distributionType)
{
super(context, recipients, body, message, distributionType);
}
public OutgoingSecureMediaMessage(OutgoingMediaMessage base) {
super(base);
}
@Override
public boolean isSecure() {
return true;
}
}

View File

@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingEndSessionMessage;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
@ -37,6 +38,7 @@ import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.RecipientDevice;
import org.whispersystems.textsecure.storage.Session;
import org.whispersystems.textsecure.util.Base64;
import ws.com.google.android.mms.MmsException;
@ -156,7 +158,7 @@ public class PushReceiver {
if (secure && (messageContent.getFlags() & PushMessageContent.Flags.END_SESSION_VALUE) != 0) {
Log.w("PushReceiver", "Received end session message...");
handleEndSessionMessage(masterSecret, message, messageContent);
} else if (messageContent.hasGroup()) {
} else if (messageContent.hasGroup() && messageContent.getGroup().getType().getNumber() != Type.DELIVER_VALUE) {
Log.w("PushReceiver", "Received push group message...");
handleReceivedGroupMessage(masterSecret, message, messageContent, secure);
} else if (messageContent.getAttachmentsCount() > 0) {
@ -177,14 +179,13 @@ public class PushReceiver {
PushMessageContent messageContent,
boolean secure)
{
if (messageContent.getGroup().getType().equals(Type.UNKNOWN)) {
Log.w("PushReceiver", "Received group message of unknown type: " +
messageContent.getGroup().getType().getNumber());
if (!messageContent.getGroup().hasId()) {
Log.w("PushReceiver", "Received group message with no id!");
return;
}
if (!messageContent.getGroup().hasId()) {
Log.w("PushReceiver", "Received group message with no id!");
if (!secure) {
Log.w("PushReceiver", "Received insecure group push action!");
return;
}
@ -193,25 +194,17 @@ public class PushReceiver {
byte[] id = group.getId().toByteArray();
int type = group.getType().getNumber();
switch (type) {
case Type.CREATE_VALUE:
database.create(id, message.getSource(), group.getName(), group.getMembersList(), group.getAvatar(), message.getRelay());
break;
case Type.ADD_VALUE:
database.add(id, message.getSource(), group.getMembersList());
break;
case Type.QUIT_VALUE:
database.remove(id, message.getSource());
break;
case Type.MODIFY_VALUE:
database.update(id, message.getSource(), group.getName(), group.getAvatar());
break;
case Type.DELIVER_VALUE:
break;
case Type.UNKNOWN_VALUE:
default:
Log.w("PushReceiver", "Received group message of unknown type: " + type);
return;
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()) {
@ -221,11 +214,15 @@ public class PushReceiver {
context.startService(intent);
}
if (messageContent.getAttachmentsCount() > 0) {
handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
} else {
handleReceivedTextMessage(masterSecret, message, messageContent, secure);
}
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,

View File

@ -0,0 +1,55 @@
package org.thoughtcrime.securesms.sms;
import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.push.PushMessageProtos;
import java.io.IOException;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class IncomingGroupMessage extends IncomingTextMessage {
private final GroupContext groupContext;
public IncomingGroupMessage(IncomingTextMessage base, GroupContext groupContext, String body) {
super(base, body);
this.groupContext = groupContext;
}
@Override
public IncomingGroupMessage withMessageBody(String body) {
return new IncomingGroupMessage(this, groupContext, body);
}
@Override
public boolean isGroup() {
return true;
}
public boolean isAdd() {
return
groupContext.getType().getNumber() == GroupContext.Type.ADD_VALUE ||
groupContext.getType().getNumber() == GroupContext.Type.CREATE_VALUE;
}
public boolean isQuit() {
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 {
IncomingTextMessage base = new IncomingTextMessage(user, groupId);
GroupContext context = GroupContext.newBuilder()
.setType(GroupContext.Type.QUIT)
.setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupId)))
.build();
return new IncomingGroupMessage(base, context, "");
}
}

View File

@ -24,7 +24,7 @@ public class IncomingIdentityUpdateMessage extends IncomingKeyExchangeMessage {
}
public static IncomingIdentityUpdateMessage createFor(String sender, IdentityKey identityKey, String groupId) {
IncomingTextMessage base = new IncomingTextMessage(sender, groupId, -1, null);
IncomingTextMessage base = new IncomingTextMessage(sender, groupId);
return new IncomingIdentityUpdateMessage(base, Base64.encodeBytesWithoutPadding(identityKey.serialize()));
}
}

View File

@ -38,8 +38,6 @@ public class IncomingTextMessage implements Parcelable {
private final String pseudoSubject;
private final long sentTimestampMillis;
private final String groupId;
private final int groupAction;
private final String groupActionArgument;
public IncomingTextMessage(SmsMessage message) {
this.message = message.getDisplayMessageBody();
@ -51,8 +49,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = message.getPseudoSubject();
this.sentTimestampMillis = message.getTimestampMillis();
this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
}
public IncomingTextMessage(IncomingPushMessage message, String encodedBody, GroupContext group) {
@ -65,14 +61,10 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = "";
this.sentTimestampMillis = message.getTimestampMillis();
if (group != null) {
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
this.groupAction = group.getType().getNumber();
this.groupActionArgument = GroupUtil.serializeArguments(group);
if (group.hasId()) {
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
} else {
this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
this.groupId = null;
}
}
@ -86,8 +78,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = in.readString();
this.sentTimestampMillis = in.readLong();
this.groupId = in.readString();
this.groupAction = in.readInt();
this.groupActionArgument = in.readString();
}
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
@ -100,8 +90,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = base.getPseudoSubject();
this.sentTimestampMillis = base.getSentTimestampMillis();
this.groupId = base.getGroupId();
this.groupAction = base.getGroupAction();
this.groupActionArgument = base.getGroupActionArgument();
}
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
@ -120,8 +108,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = fragments.get(0).getPseudoSubject();
this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis();
this.groupId = fragments.get(0).getGroupId();
this.groupAction = fragments.get(0).getGroupAction();
this.groupActionArgument = fragments.get(0).getGroupActionArgument();
}
public IncomingTextMessage(SendReq record) {
@ -134,8 +120,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = "";
this.sentTimestampMillis = System.currentTimeMillis();
this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
}
public IncomingTextMessage(SmsMessageRecord record) {
@ -148,12 +132,9 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = "";
this.sentTimestampMillis = System.currentTimeMillis();
this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
}
protected IncomingTextMessage(String sender, String groupId,
int groupAction, String groupActionArgument)
protected IncomingTextMessage(String sender, String groupId)
{
this.message = "";
this.sender = sender;
@ -164,8 +145,6 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = "";
this.sentTimestampMillis = System.currentTimeMillis();
this.groupId = groupId;
this.groupAction = groupAction;
this.groupActionArgument = groupActionArgument;
}
public long getSentTimestampMillis() {
@ -228,12 +207,8 @@ public class IncomingTextMessage implements Parcelable {
return groupId;
}
public int getGroupAction() {
return groupAction;
}
public String getGroupActionArgument() {
return groupActionArgument;
public boolean isGroup() {
return false;
}
@Override
@ -252,11 +227,5 @@ public class IncomingTextMessage implements Parcelable {
out.writeString(pseudoSubject);
out.writeLong(sentTimestampMillis);
out.writeString(groupId);
out.writeInt(groupAction);
out.writeString(groupActionArgument);
}
public static IncomingTextMessage createForLeavingGroup(String groupId, String user) {
return new IncomingTextMessage(user, groupId, GroupContext.Type.QUIT_VALUE, null);
}
}

View File

@ -20,93 +20,17 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
import org.thoughtcrime.securesms.mms.ImageSlide;
import org.thoughtcrime.securesms.mms.OutgoingMediaMessage;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.mms.TextSlide;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService;
import java.util.List;
import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.EncodedStringValue;
import ws.com.google.android.mms.pdu.PduBody;
import ws.com.google.android.mms.pdu.PduPart;
import ws.com.google.android.mms.pdu.SendReq;
public class MessageSender {
public static long sendGroupAction(Context context, MasterSecret masterSecret, Recipients recipients,
long threadId, int groupAction, String groupActionArguments, byte[] avatar)
throws MmsException
{
if (threadId == -1) {
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
}
PduBody body = new PduBody();
if (avatar != null) {
PduPart part = new PduPart();
part.setData(avatar);
part.setContentType(ContentType.IMAGE_PNG.getBytes());
part.setContentId((System.currentTimeMillis()+"").getBytes());
part.setName(("Image" + System.currentTimeMillis()).getBytes());
body.addPart(part);
}
SendReq sendRequest = new SendReq();
sendRequest.setDate(System.currentTimeMillis() / 1000L);
sendRequest.setBody(body);
sendRequest.setContentType(ContentType.MULTIPART_MIXED.getBytes());
sendRequest.setGroupAction(groupAction);
sendRequest.setGroupActionArguments(groupActionArguments);
sendMms(context, recipients, masterSecret, sendRequest, threadId,
ThreadDatabase.DistributionTypes.CONVERSATION, true);
return threadId;
}
public static long sendMms(Context context, MasterSecret masterSecret, Recipients recipients,
long threadId, SlideDeck slideDeck, String message, int distributionType,
boolean secure)
throws MmsException
{
if (threadId == -1)
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients, distributionType);
if (message.trim().length() > 0)
slideDeck.addSlide(new TextSlide(context, message));
SendReq sendRequest = new SendReq();
PduBody body = slideDeck.toPduBody();
sendRequest.setDate(System.currentTimeMillis() / 1000L);
sendRequest.setBody(body);
sendRequest.setContentType(ContentType.MULTIPART_MIXED.getBytes());
// Recipients secureRecipients = recipients.getSecureSessionRecipients(context);
// Recipients insecureRecipients = recipients.getInsecureSessionRecipients(context);
// for (Recipient secureRecipient : secureRecipients.getRecipientsList()) {
// sendMms(context, new Recipients(secureRecipient), masterSecret,
// sendRequest, threadId, !forcePlaintext);
// }
//
// if (!insecureRecipients.isEmpty()) {
// sendMms(context, insecureRecipients, masterSecret, sendRequest, threadId, false);
// }
sendMms(context, recipients, masterSecret, sendRequest, threadId, distributionType, secure);
return threadId;
}
public static long send(Context context, MasterSecret masterSecret,
OutgoingTextMessage message, long threadId)
{
@ -129,6 +53,25 @@ public class MessageSender {
return threadId;
}
public static long send(Context context, MasterSecret masterSecret, OutgoingMediaMessage message, long threadId)
throws MmsException
{
if (threadId == -1)
threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(message.getRecipients(), message.getDistributionType());
long messageId = DatabaseFactory.getMmsDatabase(context)
.insertMessageOutbox(masterSecret, message, threadId);
Intent intent = new Intent(SendReceiveService.SEND_MMS_ACTION, null,
context, SendReceiveService.class);
intent.putExtra("message_id", messageId);
intent.putExtra("thread_id", threadId);
context.startService(intent);
return threadId;
}
public static void resend(Context context, long messageId, boolean isMms)
{
@ -146,34 +89,4 @@ public class MessageSender {
context.startService(intent);
}
private static void sendMms(Context context, Recipients recipients, MasterSecret masterSecret,
SendReq sendRequest, long threadId, int distributionType, boolean secure)
throws MmsException
{
Log.w("MessageSender", "Distribution type: " + distributionType);
String[] recipientsArray = recipients.toNumberStringArray(true);
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
if (recipients.isSingleRecipient()) {
Log.w("MessageSender", "Single recipient!?");
sendRequest.setTo(encodedNumbers);
} else if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
Log.w("MessageSender", "Broadcast...");
sendRequest.setBcc(encodedNumbers);
} else if (distributionType == ThreadDatabase.DistributionTypes.CONVERSATION || distributionType == 0) {
Log.w("MessageSender", "Conversation...");
sendRequest.setTo(encodedNumbers);
}
long messageId = DatabaseFactory.getMmsDatabase(context)
.insertMessageOutbox(masterSecret, sendRequest, threadId, secure);
Intent intent = new Intent(SendReceiveService.SEND_MMS_ACTION, null,
context, SendReceiveService.class);
intent.putExtra("message_id", messageId);
intent.putExtra("thread_id", threadId);
context.startService(intent);
}
}

View File

@ -25,6 +25,7 @@ import com.google.protobuf.ByteString;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsSmsColumns;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.mms.PartParser;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
@ -243,41 +244,32 @@ public class PushTransport extends BaseTransport {
PushMessageContent.Builder builder = PushMessageContent.newBuilder();
if (GroupUtil.isEncodedGroup(message.getTo()[0].getString())) {
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
GroupContext.Builder groupBuilder = GroupContext.newBuilder();
byte[] groupId = GroupUtil.getDecodedId(message.getTo()[0].getString());
groupBuilder.setId(ByteString.copyFrom(groupId));
groupBuilder.setType(GroupContext.Type.DELIVER);
switch (message.getGroupAction()) {
case GroupContext.Type.ADD_VALUE: groupBuilder.setType(GroupContext.Type.ADD); break;
case GroupContext.Type.CREATE_VALUE: groupBuilder.setType(GroupContext.Type.CREATE); break;
case GroupContext.Type.QUIT_VALUE: groupBuilder.setType(GroupContext.Type.QUIT); break;
default: groupBuilder.setType(GroupContext.Type.DELIVER); break;
}
if (message.getGroupAction() == GroupContext.Type.ADD_VALUE ||
message.getGroupAction() == GroupContext.Type.CREATE_VALUE)
if (MmsSmsColumns.Types.isGroupAdd(message.getDatabaseMessageBox()) ||
MmsSmsColumns.Types.isGroupModify(message.getDatabaseMessageBox()) ||
MmsSmsColumns.Types.isGroupQuit(message.getDatabaseMessageBox()))
{
GroupContext serialized = GroupContext.parseFrom(Base64.decode(message.getGroupActionArguments()));
groupBuilder.addAllMembers(serialized.getMembersList());
if (messageBody != null && messageBody.trim().length() > 0) {
groupBuilder = GroupContext.parseFrom(Base64.decode(messageBody)).toBuilder();
messageBody = null;
if (serialized.hasName()) {
groupBuilder.setName(serialized.getName());
if (attachments != null && !attachments.isEmpty()) {
groupBuilder.setAvatar(AttachmentPointer.newBuilder()
.setId(attachments.get(0).getId())
.setContentType(attachments.get(0).getContentType())
.setKey(ByteString.copyFrom(attachments.get(0).getKey()))
.build());
attachments.remove(0);
}
}
}
if (message.getGroupAction() == GroupContext.Type.CREATE_VALUE && !attachments.isEmpty()) {
Log.w("PushTransport", "Adding avatar...");
groupBuilder.setAvatar(AttachmentPointer.newBuilder()
.setId(attachments.get(0).getId())
.setContentType(attachments.get(0).getContentType())
.setKey(ByteString.copyFrom(attachments.get(0).getKey()))
.build());
attachments.remove(0);
} else {
Log.w("PushTransport", "Not adding avatar: " + message.getGroupAction() + " , " + attachments.isEmpty());
}
builder.setGroup(groupBuilder.build());
}

View File

@ -25,8 +25,8 @@ import org.thoughtcrime.securesms.mms.MmsSendResult;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingIdentityUpdateMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@ -173,7 +173,7 @@ public class UniversalTransport {
Log.w("UniversalTransport", ee);
try {
for (UnregisteredUserException unregistered : ee.getUnregisteredUserExceptions()) {
IncomingTextMessage quitMessage = IncomingTextMessage.createForLeavingGroup(mediaMessage.getTo()[0].getString(), unregistered.getE164Number());
IncomingGroupMessage quitMessage = IncomingGroupMessage.createForQuit(mediaMessage.getTo()[0].getString(), unregistered.getE164Number());
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, quitMessage);
DatabaseFactory.getGroupDatabase(context).remove(GroupUtil.getDecodedId(mediaMessage.getTo()[0].getString()), unregistered.getE164Number());
}

View File

@ -25,9 +25,7 @@ public class SendReq extends MultimediaMessagePdu {
private static final String TAG = "SendReq";
private long databaseMessageId;
private long messageBox;
private int groupAction;
private String groupActionArguments;
public SendReq() {
super();
@ -92,14 +90,11 @@ public class SendReq extends MultimediaMessagePdu {
super(headers, body);
}
public SendReq(PduHeaders headers, PduBody body, long messageId, long messageBox,
int groupAction, String groupActionArguments)
public SendReq(PduHeaders headers, PduBody body, long messageId, long messageBox)
{
super(headers, body);
this.databaseMessageId = messageId;
this.messageBox = messageBox;
this.groupAction = groupAction;
this.groupActionArguments = groupActionArguments;
}
public long getDatabaseMessageBox() {
@ -110,22 +105,6 @@ public class SendReq extends MultimediaMessagePdu {
return databaseMessageId;
}
public int getGroupAction() {
return this.groupAction;
}
public String getGroupActionArguments() {
return this.groupActionArguments;
}
public void setGroupAction(int groupAction) {
this.groupAction = groupAction;
}
public void setGroupActionArguments(String groupActionArguments) {
this.groupActionArguments = groupActionArguments;
}
/**
* Get Bcc value.
*