Display group actions and correctly handle group delivery.

This commit is contained in:
Moxie Marlinspike 2014-02-14 15:59:57 -08:00
parent 7c46f3cbf8
commit 067799be06
26 changed files with 304 additions and 160 deletions

View File

@ -833,11 +833,12 @@ public class ConversationActivity extends PassphraseRequiredSherlockFragmentActi
} }
private boolean isSingleConversation() { private boolean isSingleConversation() {
return getRecipients() != null && getRecipients().isSingleRecipient(); return getRecipients() != null && getRecipients().isSingleRecipient() && !getRecipients().isGroupRecipient();
} }
private boolean isGroupConversation() { private boolean isGroupConversation() {
return getRecipients() != null && !getRecipients().isSingleRecipient(); return getRecipients() != null &&
(!getRecipients().isSingleRecipient() || getRecipients().isGroupRecipient());
} }
private Recipients getRecipients() { private Recipients getRecipients() {

View File

@ -31,6 +31,7 @@ import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
@ -60,6 +61,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SendReceiveService; import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.Emoji; import org.thoughtcrime.securesms.util.Emoji;
import org.whispersystems.textsecure.push.PushMessageProtos;
import org.whispersystems.textsecure.util.FutureTaskListener; import org.whispersystems.textsecure.util.FutureTaskListener;
import org.whispersystems.textsecure.util.ListenableFutureTask; import org.whispersystems.textsecure.util.ListenableFutureTask;
@ -69,6 +71,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
/** /**
* A view that displays an individual conversation item within a conversation * A view that displays an individual conversation item within a conversation
* thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter. * thread. Used by ComposeMessageActivity's ListActivity via a ConversationAdapter.
@ -175,8 +179,25 @@ public class ConversationItem extends LinearLayout {
/// MessageRecord Attribute Parsers /// MessageRecord Attribute Parsers
private void setBodyText(MessageRecord messageRecord) { private void setBodyText(MessageRecord messageRecord) {
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(), Emoji.EMOJI_LARGE), switch (messageRecord.getGroupAction()) {
TextView.BufferType.SPANNABLE); case GroupContext.Type.QUIT_VALUE:
bodyText.setText(messageRecord.getIndividualRecipient().toShortString() + " has left the group.");
return;
case GroupContext.Type.ADD_VALUE:
case GroupContext.Type.CREATE_VALUE:
bodyText.setText(messageRecord.getGroupActionArguments() + " have joined the group.");
return;
case GroupContext.Type.MODIFY_VALUE:
bodyText.setText(messageRecord.getIndividualRecipient() + " has updated the group.");
return;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
bodyText.setText(Emoji.getInstance(context).emojify(messageRecord.getDisplayBody(), Emoji.EMOJI_LARGE),
TextView.BufferType.SPANNABLE);
} else {
bodyText.setText(messageRecord.getDisplayBody());
}
} }
private void setContactPhoto(MessageRecord messageRecord) { private void setContactPhoto(MessageRecord messageRecord) {

View File

@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.provider.Contacts.Intents; import android.provider.Contacts.Intents;
import android.provider.ContactsContract.QuickContact; import android.provider.ContactsContract.QuickContact;
@ -102,9 +103,14 @@ public class ConversationListItem extends RelativeLayout
this.recipients.addListener(this); this.recipients.addListener(this);
this.fromView.setText(formatFrom(recipients, count, read)); this.fromView.setText(formatFrom(recipients, count, read));
this.subjectView.setText(Emoji.getInstance(context).emojify(thread.getDisplayBody(),
Emoji.EMOJI_SMALL), if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
TextView.BufferType.SPANNABLE); this.subjectView.setText(Emoji.getInstance(context).emojify(thread.getDisplayBody(),
Emoji.EMOJI_SMALL),
TextView.BufferType.SPANNABLE);
} else {
this.subjectView.setText(thread.getDisplayBody());
}
if (thread.getDate() > 0) if (thread.getDate() > 0)
this.dateView.setText(DateUtils.getBetterRelativeTimeSpanString(getContext(), thread.getDate())); this.dateView.setText(DateUtils.getBetterRelativeTimeSpanString(getContext(), thread.getDate()));

View File

@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
import org.thoughtcrime.securesms.transport.PushTransport; import org.thoughtcrime.securesms.transport.PushTransport;
import org.thoughtcrime.securesms.util.ActionBarUtil; import org.thoughtcrime.securesms.util.ActionBarUtil;
import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicLanguage;
@ -366,6 +367,8 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
byte[] groupId = groupDatabase.allocateGroupId(); byte[] groupId = groupDatabase.allocateGroupId();
AttachmentPointer avatarPointer = null; AttachmentPointer avatarPointer = null;
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(this));
GroupContext.Builder builder = GroupContext.newBuilder() GroupContext.Builder builder = GroupContext.newBuilder()
.setId(ByteString.copyFrom(groupId)) .setId(ByteString.copyFrom(groupId))
.setType(GroupContext.Type.CREATE) .setType(GroupContext.Type.CREATE)
@ -389,9 +392,23 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
groupDatabase.updateAvatar(groupId, avatar); groupDatabase.updateAvatar(groupId, avatar);
} }
long threadId = threadDatabase.getThreadIdForGroup(GroupUtil.getEncodedId(groupId)); try {
String groupRecipientId = GroupUtil.getEncodedId(groupId);
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false).getPrimaryRecipient();
OutgoingTextMessage outgoing = new OutgoingTextMessage(groupRecipient, GroupContext.Type.ADD_VALUE, org.whispersystems.textsecure.util.Util.join(memberE164Numbers, ","));
long threadId = threadDatabase.getThreadIdFor(new Recipients(groupRecipient));
List<Long> messageIds = DatabaseFactory.getEncryptingSmsDatabase(this)
.insertMessageOutbox(masterSecret, threadId, outgoing);
return new Pair<Long, List<Recipient>>(threadId, failures); for (long messageId : messageIds) {
DatabaseFactory.getEncryptingSmsDatabase(this).markAsSent(messageId);
}
return new Pair<Long, List<Recipient>>(threadId, failures);
} catch (RecipientFormattingException e) {
throw new AssertionError(e);
}
} }
private long handleCreateMmsGroup(Set<Recipient> members) { private long handleCreateMmsGroup(Set<Recipient> members) {

View File

@ -647,6 +647,10 @@ public class DatabaseFactory {
db.execSQL("ALTER TABLE push ADD COLUMN device_id INTEGER DEFAULT 1;"); 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 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 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 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(); db.setTransactionSuccessful();

View File

@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.util.Hex; import org.whispersystems.textsecure.util.Hex;
import org.whispersystems.textsecure.util.Util; import org.whispersystems.textsecure.util.Util;
@ -90,11 +91,20 @@ public class GroupDatabase extends Database {
List<String> members, AttachmentPointer avatar, List<String> members, AttachmentPointer avatar,
String relay) String relay)
{ {
List<String> filteredMembers = new LinkedList<String>();
String localNumber = TextSecurePreferences.getLocalNumber(context);
for (String member : members) {
if (!member.equals(localNumber)) {
filteredMembers.add(member);
}
}
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);
contentValues.put(TITLE, title); contentValues.put(TITLE, title);
contentValues.put(MEMBERS, Util.join(members, ",")); contentValues.put(MEMBERS, Util.join(filteredMembers, ","));
if (avatar != null) { if (avatar != null) {
contentValues.put(AVATAR_ID, avatar.getId()); contentValues.put(AVATAR_ID, avatar.getId());
@ -147,7 +157,7 @@ public class GroupDatabase extends Database {
contents.put(MEMBERS, Util.join(concatenatedMembers, ",")); contents.put(MEMBERS, Util.join(concatenatedMembers, ","));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[] {Hex.toString(id)}); new String[] {GroupUtil.getEncodedId(id)});
} }
} }
} }
@ -160,7 +170,7 @@ public class GroupDatabase extends Database {
contents.put(MEMBERS, Util.join(currentMembers, ",")); contents.put(MEMBERS, Util.join(currentMembers, ","));
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?", databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
new String[]{Hex.toString(id)}); new String[]{GroupUtil.getEncodedId(id)});
} }
private List<String> getCurrentMembers(byte[] id) { private List<String> getCurrentMembers(byte[] id) {
@ -168,7 +178,8 @@ public class GroupDatabase extends Database {
try { try {
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS}, cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
GROUP_ID + " = ?", new String[] {Hex.toString(id)}, GROUP_ID + " = ?",
new String[] {GroupUtil.getEncodedId(id)},
null, null, null); null, null, null);
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {

View File

@ -116,7 +116,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + RETRIEVE_STATUS + " INTEGER, " + STATUS + " INTEGER, " + TRANSACTION_ID + " TEXT, " + RETRIEVE_STATUS + " INTEGER, " +
RETRIEVE_TEXT + " TEXT, " + RETRIEVE_TEXT_CS + " INTEGER, " + READ_STATUS + " INTEGER, " + RETRIEVE_TEXT + " TEXT, " + RETRIEVE_TEXT_CS + " INTEGER, " + READ_STATUS + " INTEGER, " +
CONTENT_CLASS + " INTEGER, " + RESPONSE_TEXT + " TEXT, " + DELIVERY_TIME + " INTEGER, " + CONTENT_CLASS + " INTEGER, " + RESPONSE_TEXT + " TEXT, " + DELIVERY_TIME + " INTEGER, " +
DELIVERY_REPORT + " INTEGER);"; DELIVERY_REPORT + " INTEGER, " + GROUP_ACTION + " INTEGER DEFAULT -1, " + GROUP_ACTION_ARGUMENTS + " TEXT);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", "CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@ -181,7 +181,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException { private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException {
if (retrieved.getGroupId() != null) { if (retrieved.getGroupId() != null) {
return DatabaseFactory.getThreadDatabase(context).getThreadIdForGroup(retrieved.getGroupId()); Recipients groupRecipients = RecipientFactory.getRecipientsFromString(context, retrieved.getGroupId(), true);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipients);
} }
try { try {
@ -399,6 +400,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED); contentValues.put(STATUS, Status.DOWNLOAD_INITIALIZED);
contentValues.put(DATE_RECEIVED, System.currentTimeMillis() / 1000); contentValues.put(DATE_RECEIVED, System.currentTimeMillis() / 1000);
contentValues.put(READ, unread ? 0 : 1); contentValues.put(READ, unread ? 0 : 1);
contentValues.put(GROUP_ACTION, retrieved.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, retrieved.getGroupActionArguments());
if (!contentValues.containsKey(DATE_SENT)) { if (!contentValues.containsKey(DATE_SENT)) {
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED)); contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
@ -797,6 +800,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE)); long messageSize = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_SIZE));
long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY)); long expiry = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRY));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.STATUS)); 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[]contentLocationBytes = null;
byte[]transactionIdBytes = null; byte[]transactionIdBytes = null;
@ -811,7 +816,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), return new NotificationMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
addressDeviceId, dateSent, dateReceived, threadId, addressDeviceId, dateSent, dateReceived, threadId,
contentLocationBytes, messageSize, expiry, status, contentLocationBytes, messageSize, expiry, status,
transactionIdBytes, mailbox); transactionIdBytes, mailbox, groupAction, groupActionArgs);
} }
private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) { private MediaMmsMessageRecord getMediaMmsMessageRecord(Cursor cursor) {
@ -822,6 +827,8 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.THREAD_ID));
String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS)); String address = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS));
int addressDeviceId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.ADDRESS_DEVICE_ID)); 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); DisplayRecord.Body body = getBody(cursor);
int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT)); int partCount = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.PART_COUNT));
Recipients recipients = getRecipientsFor(address); Recipients recipients = getRecipientsFor(address);
@ -830,7 +837,7 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(), return new MediaMmsMessageRecord(context, id, recipients, recipients.getPrimaryRecipient(),
addressDeviceId, dateSent, dateReceived, threadId, body, addressDeviceId, dateSent, dateReceived, threadId, body,
slideDeck, partCount, box); slideDeck, partCount, box, groupAction, groupActionArgs);
} }
private Recipients getRecipientsFor(String address) { private Recipients getRecipientsFor(String address) {

View File

@ -10,7 +10,8 @@ public interface MmsSmsColumns {
public static final String BODY = "body"; public static final String BODY = "body";
public static final String ADDRESS = "address"; public static final String ADDRESS = "address";
public static final String ADDRESS_DEVICE_ID = "address_device_id"; 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 { public static class Types {
protected static final long TOTAL_MASK = 0xFFFFFFFF; protected static final long TOTAL_MASK = 0xFFFFFFFF;

View File

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

View File

@ -26,7 +26,6 @@ import android.telephony.PhoneNumberUtils;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.model.DisplayRecord; import org.thoughtcrime.securesms.database.model.DisplayRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
@ -66,7 +65,8 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + ADDRESS_DEVICE_ID + " INTEGER DEFAULT 1, " + PERSON + " INTEGER, " + 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, " + DATE_RECEIVED + " INTEGER, " + DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " + STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " +
SUBJECT + " TEXT, " + BODY + " TEXT, " + SERVICE_CENTER + " TEXT);"; SUBJECT + " TEXT, " + BODY + " TEXT, " + SERVICE_CENTER + " TEXT, " +
GROUP_ACTION + " INTEGER DEFAULT -1, " + GROUP_ACTION_ARGUMENTS + " TEXT);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");", "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_RECEIVED + " AS " + NORMALIZED_DATE_RECEIVED,
DATE_SENT + " AS " + NORMALIZED_DATE_SENT, DATE_SENT + " AS " + NORMALIZED_DATE_SENT,
PROTOCOL, READ, STATUS, TYPE, PROTOCOL, READ, STATUS, TYPE,
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, GROUP_ACTION, GROUP_ACTION_ARGUMENTS
}; };
public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) { public SmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
@ -303,6 +303,8 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
values.put(BODY, message.getMessageBody()); values.put(BODY, message.getMessageBody());
values.put(TYPE, type); values.put(TYPE, type);
values.put(THREAD_ID, threadId); values.put(THREAD_ID, threadId);
values.put(GROUP_ACTION, message.getGroupAction());
values.put(GROUP_ACTION_ARGUMENTS, message.getGroupActionArgument());
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, null, values); long messageId = db.insert(TABLE_NAME, null, values);
@ -338,6 +340,8 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
contentValues.put(DATE_SENT, date); contentValues.put(DATE_SENT, date);
contentValues.put(READ, 1); contentValues.put(READ, 1);
contentValues.put(TYPE, type); contentValues.put(TYPE, type);
contentValues.put(GROUP_ACTION, message.getGroupAction());
contentValues.put(GROUP_ACTION_ARGUMENTS, message.getGroupActionArguments());
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
messageIds.add(db.insert(TABLE_NAME, ADDRESS, contentValues)); messageIds.add(db.insert(TABLE_NAME, ADDRESS, contentValues));
@ -494,6 +498,8 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT)); long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.NORMALIZED_DATE_SENT));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID)); long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS)); 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); Recipients recipients = getRecipientsFor(address);
DisplayRecord.Body body = getBody(cursor); DisplayRecord.Body body = getBody(cursor);
@ -501,7 +507,7 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
recipients.getPrimaryRecipient(), recipients.getPrimaryRecipient(),
addressDeviceId, addressDeviceId,
dateSent, dateReceived, type, dateSent, dateReceived, type,
threadId, status); threadId, status, groupAction, groupActionArgs);
} }
private Recipients getRecipientsFor(String address) { private Recipients getRecipientsFor(String address) {

View File

@ -53,13 +53,15 @@ public class ThreadDatabase extends Database {
private static final String ERROR = "error"; private static final String ERROR = "error";
private static final String HAS_ATTACHMENT = "has_attachment"; private static final String HAS_ATTACHMENT = "has_attachment";
public static final String SNIPPET_TYPE = "snippet_type"; 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, " + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " +
RECIPIENT_IDS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " 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, " + READ + " INTEGER DEFAULT 1, " + TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " +
SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_TYPE + " INTEGER DEFAULT 0, " + GROUP_ACTION + " INTEGER DEFAULT -1, " +
HAS_ATTACHMENT + " INTEGER DEFAULT 0);"; GROUP_ACTION_ARG + " TEXT, " + HAS_ATTACHMENT + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = { public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");", "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + RECIPIENT_IDS + ");",
@ -99,17 +101,6 @@ public class ThreadDatabase extends Database {
return sb.toString(); return sb.toString();
} }
private long createThreadForGroup(String group) {
long date = System.currentTimeMillis();
ContentValues values = new ContentValues();
values.put(DATE, date - date % 1000);
values.put(RECIPIENT_IDS, group);
values.put(MESSAGE_COUNT, 0);
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
}
private long createThreadForRecipients(String recipients, int recipientCount, int distributionType) { private long createThreadForRecipients(String recipients, int recipientCount, int distributionType) {
ContentValues contentValues = new ContentValues(4); ContentValues contentValues = new ContentValues(4);
long date = System.currentTimeMillis(); long date = System.currentTimeMillis();
@ -126,12 +117,16 @@ public class ThreadDatabase extends Database {
return db.insert(TABLE_NAME, null, contentValues); return db.insert(TABLE_NAME, null, contentValues);
} }
private void updateThread(long threadId, long count, String body, long date, long type) { private void updateThread(long threadId, long count, String body, long date, long type,
int groupAction, String groupActionArguments)
{
ContentValues contentValues = new ContentValues(3); ContentValues contentValues = new ContentValues(3);
contentValues.put(DATE, date - date % 1000); contentValues.put(DATE, date - date % 1000);
contentValues.put(MESSAGE_COUNT, count); contentValues.put(MESSAGE_COUNT, count);
contentValues.put(SNIPPET, body); contentValues.put(SNIPPET, body);
contentValues.put(SNIPPET_TYPE, type); contentValues.put(SNIPPET_TYPE, type);
contentValues.put(GROUP_ACTION, groupAction);
contentValues.put(GROUP_ACTION_ARG, groupActionArguments);
SQLiteDatabase db = databaseHelper.getWritableDatabase(); SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""});
@ -358,26 +353,6 @@ public class ThreadDatabase extends Database {
} }
} }
public long getThreadIdForGroup(String groupId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = RECIPIENT_IDS + " = ?";
String[] recipientsArg = new String[] {groupId};
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
} else {
return createThreadForGroup(groupId);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public Recipients getRecipientsForThreadId(long threadId) { public Recipients getRecipientsForThreadId(long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase(); SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null; Cursor cursor = null;
@ -414,7 +389,8 @@ public class ThreadDatabase extends Database {
MessageRecord record = null; MessageRecord record = null;
if (reader != null && (record = reader.getNext()) != null) { if (reader != null && (record = reader.getNext()) != null) {
updateThread(threadId, count, record.getBody().getBody(), record.getDateReceived(), record.getType()); updateThread(threadId, count, record.getBody().getBody(), record.getDateReceived(),
record.getType(), record.getGroupAction(), record.getGroupActionArguments());
} else { } else {
deleteThread(threadId); deleteThread(threadId);
} }
@ -470,9 +446,12 @@ public class ThreadDatabase extends Database {
long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ)); long read = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.READ));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE)); long type = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET_TYPE));
int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.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, return new ThreadRecord(context, body, recipients, date, count,
read == 1, threadId, type, distributionType); read == 1, threadId, type, distributionType,
groupAction, groupActionArg);
} }
private DisplayRecord.Body getPlaintextBody(Cursor cursor) { private DisplayRecord.Body getPlaintextBody(Cursor cursor) {

View File

@ -36,22 +36,26 @@ public abstract class DisplayRecord {
protected final long type; protected final long type;
private final Recipients recipients; private final Recipients recipients;
private final long dateSent; private final long dateSent;
private final long dateReceived; private final long dateReceived;
private final long threadId; private final long threadId;
private final Body body; private final Body body;
// private final String body; private final int groupAction;
private final String groupActionArguments;
public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent, public DisplayRecord(Context context, Body body, Recipients recipients, long dateSent,
long dateReceived, long threadId, long type) long dateReceived, long threadId, long type, int groupAction,
String groupActionArguments)
{ {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.threadId = threadId; this.threadId = threadId;
this.recipients = recipients; this.recipients = recipients;
this.dateSent = dateSent; this.dateSent = dateSent;
this.dateReceived = dateReceived; this.dateReceived = dateReceived;
this.type = type; this.type = type;
this.body = body; this.body = body;
this.groupAction = groupAction;
this.groupActionArguments = groupActionArguments;
} }
public Body getBody() { public Body getBody() {
@ -80,6 +84,14 @@ public abstract class DisplayRecord {
return SmsDatabase.Types.isKeyExchangeType(type); return SmsDatabase.Types.isKeyExchangeType(type);
} }
public int getGroupAction() {
return groupAction;
}
public String getGroupActionArguments() {
return groupActionArguments;
}
public static class Body { public static class Body {
private final String body; private final String body;
private final boolean plaintext; private final boolean plaintext;

View File

@ -44,10 +44,12 @@ public class MediaMmsMessageRecord extends MessageRecord {
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long threadId, Body body, long dateSent, long dateReceived, long threadId, Body body,
ListenableFutureTask<SlideDeck> slideDeck, ListenableFutureTask<SlideDeck> slideDeck,
int partCount, long mailbox) int partCount, long mailbox, int groupAction,
String groupActionArguments)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox); dateSent, dateReceived, threadId, DELIVERY_STATUS_NONE, mailbox,
groupAction, groupActionArguments);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.partCount = partCount; this.partCount = partCount;

View File

@ -53,9 +53,9 @@ public abstract class MessageRecord extends DisplayRecord {
Recipient individualRecipient, int recipientDeviceId, Recipient individualRecipient, int recipientDeviceId,
long dateSent, long dateReceived, long dateSent, long dateReceived,
long threadId, int deliveryStatus, long threadId, int deliveryStatus,
long type) long type, int groupAction, String groupActionArguments)
{ {
super(context, body, recipients, dateSent, dateReceived, threadId, type); super(context, body, recipients, dateSent, dateReceived, threadId, type, groupAction, groupActionArguments);
this.id = id; this.id = id;
this.individualRecipient = individualRecipient; this.individualRecipient = individualRecipient;
this.recipientDeviceId = recipientDeviceId; this.recipientDeviceId = recipientDeviceId;

View File

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

View File

@ -41,10 +41,12 @@ public class SmsMessageRecord extends MessageRecord {
int recipientDeviceId, int recipientDeviceId,
long dateSent, long dateReceived, long dateSent, long dateReceived,
long type, long threadId, long type, long threadId,
int status) int status, int groupAction,
String groupActionArguments)
{ {
super(context, id, body, recipients, individualRecipient, recipientDeviceId, super(context, id, body, recipients, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), type); dateSent, dateReceived, threadId, getGenericDeliveryStatus(status), type,
groupAction, groupActionArguments);
} }
public long getType() { public long getType() {

View File

@ -24,8 +24,11 @@ import android.text.style.StyleSpan;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipients; import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.push.PushMessageProtos;
import org.whispersystems.textsecure.util.Util; import org.whispersystems.textsecure.util.Util;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
/** /**
* The message record model which represents thread heading messages. * The message record model which represents thread heading messages.
* *
@ -41,9 +44,9 @@ public class ThreadRecord extends DisplayRecord {
public ThreadRecord(Context context, Body body, Recipients recipients, long date, public ThreadRecord(Context context, Body body, Recipients recipients, long date,
long count, boolean read, long threadId, long snippetType, long count, boolean read, long threadId, long snippetType,
int distributionType) int distributionType, int groupAction, String groupActionArg)
{ {
super(context, body, recipients, date, date, threadId, snippetType); super(context, body, recipients, date, date, threadId, snippetType, groupAction, groupActionArg);
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.count = count; this.count = count;
this.read = read; this.read = read;
@ -54,6 +57,14 @@ public class ThreadRecord extends DisplayRecord {
public SpannableString getDisplayBody() { public SpannableString getDisplayBody() {
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 (getGroupAction() == GroupContext.Type.ADD_VALUE ||
getGroupAction() == GroupContext.Type.CREATE_VALUE)
{
return emphasisAdded("Added " + getGroupActionArguments());
} else if (getGroupAction() == GroupContext.Type.QUIT_VALUE) {
return emphasisAdded(getRecipients().toShortString() + " left the group.");
} else if (getGroupAction() == GroupContext.Type.MODIFY_VALUE) {
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)) {

View File

@ -20,11 +20,15 @@ public class IncomingMediaMessage {
private final PduHeaders headers; private final PduHeaders headers;
private final PduBody body; private final PduBody body;
private final String groupId; private final String groupId;
private final int groupAction;
private final String groupActionArguments;
public IncomingMediaMessage(RetrieveConf retreived) { public IncomingMediaMessage(RetrieveConf retreived) {
this.headers = retreived.getPduHeaders(); this.headers = retreived.getPduHeaders();
this.body = retreived.getBody(); this.body = retreived.getBody();
this.groupId = null; this.groupId = null;
this.groupAction = -1;
this.groupActionArguments = null;
} }
public IncomingMediaMessage(MasterSecret masterSecret, String localNumber, public IncomingMediaMessage(MasterSecret masterSecret, String localNumber,
@ -35,9 +39,13 @@ public class IncomingMediaMessage {
this.body = new PduBody(); this.body = new PduBody();
if (messageContent.hasGroup()) { if (messageContent.hasGroup()) {
this.groupId = GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray()); this.groupId = GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray());
this.groupAction = messageContent.getGroup().getType().getNumber();
this.groupActionArguments = GroupUtil.getActionArgument(messageContent.getGroup());
} else { } else {
this.groupId = null; this.groupId = null;
this.groupAction = -1;
this.groupActionArguments = null;
} }
this.headers.setEncodedStringValue(new EncodedStringValue(message.getSource()), PduHeaders.FROM); this.headers.setEncodedStringValue(new EncodedStringValue(message.getSource()), PduHeaders.FROM);
@ -90,4 +98,11 @@ public class IncomingMediaMessage {
headers.getEncodedStringValues(PduHeaders.TO).length > 1); headers.getEncodedStringValues(PduHeaders.TO).length > 1);
} }
public int getGroupAction() {
return groupAction;
}
public String getGroupActionArguments() {
return groupActionArguments;
}
} }

View File

@ -1,57 +0,0 @@
package org.thoughtcrime.securesms.push;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.Set;
public class GroupActionRecord {
private static final int CREATE_GROUP_TYPE = 1;
private static final int ADD_USERS_TYPE = 2;
private static final int LEAVE_GROUP_TYPE = 3;
private final int type;
private final Set<Recipient> recipients;
private final byte[] groupId;
private final String groupName;
private final byte[] avatar;
public GroupActionRecord(int type, byte[] groupId, String groupName,
byte[] avatar, Set<Recipient> recipients)
{
this.type = type;
this.groupId = groupId;
this.groupName = groupName;
this.avatar = avatar;
this.recipients = recipients;
}
public boolean isCreateAction() {
return type== CREATE_GROUP_TYPE;
}
public boolean isAddUsersAction() {
return type == ADD_USERS_TYPE;
}
public boolean isLeaveAction() {
return type == LEAVE_GROUP_TYPE;
}
public Set<Recipient> getRecipients() {
return recipients;
}
public byte[] getGroupId() {
return groupId;
}
public String getGroupName() {
return groupName;
}
public byte[] getAvatar() {
return avatar;
}
}

View File

@ -108,6 +108,10 @@ public class Recipient implements Parcelable, CanonicalRecipient {
return this.contactUri; return this.contactUri;
} }
public synchronized void setContactPhoto(Bitmap bitmap) {
this.contactPhoto = bitmap;
}
public synchronized String getName() { public synchronized String getName() {
return this.name; return this.name;
} }
@ -149,6 +153,18 @@ public class Recipient implements Parcelable, CanonicalRecipient {
listeners.remove(listener); listeners.remove(listener);
} }
public void notifyListeners() {
HashSet<RecipientModifiedListener> localListeners;
synchronized (this) {
localListeners = (HashSet<RecipientModifiedListener>)listeners.clone();
}
for (RecipientModifiedListener listener : localListeners) {
listener.onModified(this);
}
}
public synchronized void writeToParcel(Parcel dest, int flags) { public synchronized void writeToParcel(Parcel dest, int flags) {
dest.writeString(number); dest.writeString(number);
dest.writeString(name); dest.writeString(name);

View File

@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.recipients;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.util.Log;
import android.util.Patterns; import android.util.Patterns;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener; import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
@ -145,7 +146,10 @@ public class Recipients implements Parcelable {
while (iterator.hasNext()) { while (iterator.hasNext()) {
String number = iterator.next().getNumber(); String number = iterator.next().getNumber();
if (scrub && number != null && !Patterns.EMAIL_ADDRESS.matcher(number).matches()) { if (scrub && number != null &&
!Patterns.EMAIL_ADDRESS.matcher(number).matches() &&
!GroupUtil.isEncodedGroup(number))
{
number = number.replaceAll("[^0-9+]", ""); number = number.replaceAll("[^0-9+]", "");
} }

View File

@ -9,8 +9,12 @@ import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.GroupDatabase; import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.PartDatabase; import org.thoughtcrime.securesms.database.PartDatabase;
import org.thoughtcrime.securesms.push.PushServiceSocketFactory; import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.util.BitmapDecodingException; import org.thoughtcrime.securesms.util.BitmapDecodingException;
import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream; import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream;
import org.whispersystems.textsecure.crypto.InvalidMessageException; import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
@ -55,7 +59,16 @@ public class AvatarDownloader {
database.updateAvatar(groupId, avatar); database.updateAvatar(groupId, avatar);
avatar.recycle(); try {
Recipient groupRecipient = RecipientFactory.getRecipientsFromString(context, GroupUtil.getEncodedId(groupId), true)
.getPrimaryRecipient();
groupRecipient.setContactPhoto(avatar);
groupRecipient.notifyListeners();
} catch (RecipientFormattingException e) {
Log.w("AvatarDownloader", e);
}
// avatar.recycle();
attachment.delete(); attachment.delete();
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -24,7 +24,6 @@ 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;
import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.InvalidKeyException; import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.InvalidMessageException; import org.whispersystems.textsecure.crypto.InvalidMessageException;
@ -35,7 +34,6 @@ import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent; 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.util.Hex;
import ws.com.google.android.mms.MmsException; import ws.com.google.android.mms.MmsException;
@ -220,7 +218,7 @@ public class PushReceiver {
if (messageContent.getAttachmentsCount() > 0) { if (messageContent.getAttachmentsCount() > 0) {
handleReceivedMediaMessage(masterSecret, message, messageContent, secure); handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
} else if (messageContent.hasBody()) { } else {
handleReceivedTextMessage(masterSecret, message, messageContent, secure); handleReceivedTextMessage(masterSecret, message, messageContent, secure);
} }
} }
@ -263,8 +261,7 @@ public class PushReceiver {
boolean secure) boolean secure)
{ {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context); EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String groupId = messageContent.hasGroup() ? GroupUtil.getEncodedId(messageContent.getGroup().getId().toByteArray()) : null; IncomingTextMessage textMessage = new IncomingTextMessage(message, "", messageContent.getGroup());
IncomingTextMessage textMessage = new IncomingTextMessage(message, "", groupId);
if (secure) { if (secure) {
textMessage = new IncomingEncryptedMessage(textMessage, ""); textMessage = new IncomingEncryptedMessage(textMessage, "");

View File

@ -4,11 +4,14 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.whispersystems.textsecure.push.IncomingPushMessage; import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.storage.RecipientDevice; import org.whispersystems.textsecure.storage.RecipientDevice;
import java.util.List; import java.util.List;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class IncomingTextMessage implements Parcelable { public class IncomingTextMessage implements Parcelable {
public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() { public static final Parcelable.Creator<IncomingTextMessage> CREATOR = new Parcelable.Creator<IncomingTextMessage>() {
@ -32,6 +35,8 @@ public class IncomingTextMessage implements Parcelable {
private final String pseudoSubject; private final String pseudoSubject;
private final long sentTimestampMillis; private final long sentTimestampMillis;
private final String groupId; private final String groupId;
private final int groupAction;
private final String groupActionArgument;
public IncomingTextMessage(SmsMessage message) { public IncomingTextMessage(SmsMessage message) {
this.message = message.getDisplayMessageBody(); this.message = message.getDisplayMessageBody();
@ -43,9 +48,11 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = message.getPseudoSubject(); this.pseudoSubject = message.getPseudoSubject();
this.sentTimestampMillis = message.getTimestampMillis(); this.sentTimestampMillis = message.getTimestampMillis();
this.groupId = null; this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
} }
public IncomingTextMessage(IncomingPushMessage message, String encodedBody, String groupId) { public IncomingTextMessage(IncomingPushMessage message, String encodedBody, GroupContext group) {
this.message = encodedBody; this.message = encodedBody;
this.sender = message.getSource(); this.sender = message.getSource();
this.senderDeviceId = message.getSourceDevice(); this.senderDeviceId = message.getSourceDevice();
@ -54,7 +61,16 @@ public class IncomingTextMessage implements Parcelable {
this.replyPathPresent = true; this.replyPathPresent = true;
this.pseudoSubject = ""; this.pseudoSubject = "";
this.sentTimestampMillis = message.getTimestampMillis(); this.sentTimestampMillis = message.getTimestampMillis();
this.groupId = groupId;
if (group != null) {
this.groupId = GroupUtil.getEncodedId(group.getId().toByteArray());
this.groupAction = group.getType().getNumber();
this.groupActionArgument = GroupUtil.getActionArgument(group);
} else {
this.groupId = null;
this.groupAction = -1;
this.groupActionArgument = null;
}
} }
public IncomingTextMessage(Parcel in) { public IncomingTextMessage(Parcel in) {
@ -67,6 +83,8 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = in.readString(); this.pseudoSubject = in.readString();
this.sentTimestampMillis = in.readLong(); this.sentTimestampMillis = in.readLong();
this.groupId = in.readString(); this.groupId = in.readString();
this.groupAction = in.readInt();
this.groupActionArgument = in.readString();
} }
public IncomingTextMessage(IncomingTextMessage base, String newBody) { public IncomingTextMessage(IncomingTextMessage base, String newBody) {
@ -79,6 +97,8 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = base.getPseudoSubject(); this.pseudoSubject = base.getPseudoSubject();
this.sentTimestampMillis = base.getSentTimestampMillis(); this.sentTimestampMillis = base.getSentTimestampMillis();
this.groupId = base.getGroupId(); this.groupId = base.getGroupId();
this.groupAction = base.getGroupAction();
this.groupActionArgument = base.getGroupActionArgument();
} }
public IncomingTextMessage(List<IncomingTextMessage> fragments) { public IncomingTextMessage(List<IncomingTextMessage> fragments) {
@ -97,6 +117,8 @@ public class IncomingTextMessage implements Parcelable {
this.pseudoSubject = fragments.get(0).getPseudoSubject(); this.pseudoSubject = fragments.get(0).getPseudoSubject();
this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis(); this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis();
this.groupId = fragments.get(0).getGroupId(); this.groupId = fragments.get(0).getGroupId();
this.groupAction = fragments.get(0).getGroupAction();
this.groupActionArgument = fragments.get(0).getGroupActionArgument();
} }
public long getSentTimestampMillis() { public long getSentTimestampMillis() {
@ -151,6 +173,14 @@ public class IncomingTextMessage implements Parcelable {
return groupId; return groupId;
} }
public int getGroupAction() {
return groupAction;
}
public String getGroupActionArgument() {
return groupActionArgument;
}
@Override @Override
public int describeContents() { public int describeContents() {
return 0; return 0;
@ -167,5 +197,7 @@ public class IncomingTextMessage implements Parcelable {
out.writeString(pseudoSubject); out.writeString(pseudoSubject);
out.writeLong(sentTimestampMillis); out.writeLong(sentTimestampMillis);
out.writeString(groupId); out.writeString(groupId);
out.writeInt(groupAction);
out.writeString(groupActionArgument);
} }
} }

View File

@ -7,20 +7,33 @@ import org.thoughtcrime.securesms.recipients.Recipients;
public class OutgoingTextMessage { public class OutgoingTextMessage {
private final Recipients recipients; private final Recipients recipients;
private String message; private final String message;
private final int groupAction;
private final String groupActionArguments;
public OutgoingTextMessage(Recipient recipient, String message) { public OutgoingTextMessage(Recipient recipient, String message) {
this(new Recipients(recipient), message); this(new Recipients(recipient), message);
} }
public OutgoingTextMessage(Recipients recipients, String message) { public OutgoingTextMessage(Recipients recipients, String message) {
this.recipients = recipients; this.recipients = recipients;
this.message = message; this.message = message;
this.groupAction = -1;
this.groupActionArguments = null;
}
public OutgoingTextMessage(Recipient recipient, int groupAction, String groupActionArguments) {
this.recipients = new Recipients(recipient);
this.groupAction = groupAction;
this.groupActionArguments = groupActionArguments;
this.message = "";
} }
protected OutgoingTextMessage(OutgoingTextMessage base, String body) { protected OutgoingTextMessage(OutgoingTextMessage base, String body) {
this.recipients = base.getRecipients(); this.recipients = base.getRecipients();
this.message = body; this.groupAction = base.getGroupAction();
this.groupActionArguments = base.getGroupActionArguments();
this.message = body;
} }
public String getMessageBody() { public String getMessageBody() {
@ -56,4 +69,12 @@ public class OutgoingTextMessage {
public OutgoingTextMessage withBody(String body) { public OutgoingTextMessage withBody(String body) {
return new OutgoingTextMessage(this, body); return new OutgoingTextMessage(this, body);
} }
public int getGroupAction() {
return groupAction;
}
public String getGroupActionArguments() {
return groupActionArguments;
}
} }

View File

@ -4,6 +4,8 @@ import org.whispersystems.textsecure.util.Hex;
import java.io.IOException; import java.io.IOException;
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
public class GroupUtil { public class GroupUtil {
private static final String ENCODED_GROUP_PREFIX = "__textsecure_group__!"; private static final String ENCODED_GROUP_PREFIX = "__textsecure_group__!";
@ -24,4 +26,15 @@ public class GroupUtil {
return groupId.startsWith(ENCODED_GROUP_PREFIX); return groupId.startsWith(ENCODED_GROUP_PREFIX);
} }
public static String getActionArgument(GroupContext group) {
if (group.getType().equals(GroupContext.Type.CREATE) ||
group.getType().equals(GroupContext.Type.ADD))
{
return org.whispersystems.textsecure.util.Util.join(group.getMembersList(), ",");
} else if (group.getType().equals(GroupContext.Type.MODIFY)) {
return group.getName();
}
return null;
}
} }