diff --git a/res/layout/conversation_item_activity.xml b/res/layout/conversation_item_activity.xml
deleted file mode 100644
index 79adc8789b..0000000000
--- a/res/layout/conversation_item_activity.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
diff --git a/res/layout/conversation_item_update.xml b/res/layout/conversation_item_update.xml
new file mode 100644
index 0000000000..a3c927e65b
--- /dev/null
+++ b/res/layout/conversation_item_update.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/org/thoughtcrime/securesms/ConversationAdapter.java b/src/org/thoughtcrime/securesms/ConversationAdapter.java
index e89ed27af3..c6cc4cb44b 100644
--- a/src/org/thoughtcrime/securesms/ConversationAdapter.java
+++ b/src/org/thoughtcrime/securesms/ConversationAdapter.java
@@ -57,7 +57,7 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
public static final int MESSAGE_TYPE_OUTGOING = 0;
public static final int MESSAGE_TYPE_INCOMING = 1;
- public static final int MESSAGE_TYPE_GROUP_ACTION = 2;
+ public static final int MESSAGE_TYPE_UPDATE = 2;
private final Set batchSelected = Collections.synchronizedSet(new HashSet());
@@ -85,13 +85,22 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
@Override
public void bindView(View view, Context context, Cursor cursor) {
- ConversationItem item = (ConversationItem)view;
long id = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
- item.set(masterSecret, messageRecord, locale, batchSelected, selectionClickListener,
- groupThread, pushDestination);
+ switch (getItemViewType(cursor)) {
+ case MESSAGE_TYPE_INCOMING:
+ case MESSAGE_TYPE_OUTGOING:
+ ((ConversationItem) view).set(masterSecret, messageRecord, locale, batchSelected,
+ selectionClickListener, groupThread, pushDestination);
+ break;
+ case MESSAGE_TYPE_UPDATE:
+ ((ConversationUpdateItem)view).set(messageRecord);
+ break;
+ default:
+ throw new AssertionError("Unknown type!");
+ }
}
@Override
@@ -113,8 +122,8 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
case ConversationAdapter.MESSAGE_TYPE_INCOMING:
view = inflater.inflate(R.layout.conversation_item_received, parent, false);
break;
- case ConversationAdapter.MESSAGE_TYPE_GROUP_ACTION:
- view = inflater.inflate(R.layout.conversation_item_activity, parent, false);
+ case ConversationAdapter.MESSAGE_TYPE_UPDATE:
+ view = inflater.inflate(R.layout.conversation_item_update, parent, false);
break;
default: throw new IllegalArgumentException("unsupported item view type given to ConversationAdapter");
}
@@ -138,7 +147,7 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
- if (messageRecord.isGroupAction()) return MESSAGE_TYPE_GROUP_ACTION;
+ if (messageRecord.isGroupAction()) return MESSAGE_TYPE_UPDATE;
else if (messageRecord.isOutgoing()) return MESSAGE_TYPE_OUTGOING;
else return MESSAGE_TYPE_INCOMING;
}
@@ -181,6 +190,6 @@ public class ConversationAdapter extends CursorAdapter implements AbsListView.Re
@Override
public void onMovedToScrapHeap(View view) {
- ((ConversationItem)view).unbind();
+ ((Unbindable) view).unbind();
}
}
diff --git a/src/org/thoughtcrime/securesms/ConversationItem.java b/src/org/thoughtcrime/securesms/ConversationItem.java
index 408fbcd1df..44201532b1 100644
--- a/src/org/thoughtcrime/securesms/ConversationItem.java
+++ b/src/org/thoughtcrime/securesms/ConversationItem.java
@@ -60,6 +60,7 @@ import org.thoughtcrime.securesms.mms.PartAuthority;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils;
+import org.thoughtcrime.securesms.util.Util;
import java.util.List;
import java.util.Locale;
@@ -73,7 +74,7 @@ import java.util.Set;
*
*/
-public class ConversationItem extends LinearLayout implements Recipient.RecipientModifiedListener {
+public class ConversationItem extends LinearLayout implements Recipient.RecipientModifiedListener, Unbindable {
private final static String TAG = ConversationItem.class.getSimpleName();
private MessageRecord messageRecord;
@@ -181,16 +182,13 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
setSelectionBackgroundDrawables(messageRecord);
setBodyText(messageRecord);
-
- if (hasConversationBubble(messageRecord)) {
- setBubbleState(messageRecord, recipient);
- setStatusIcons(messageRecord);
- setContactPhoto(recipient);
- setGroupMessageStatus(messageRecord, recipient);
- setEvents(messageRecord);
- setMinimumWidth();
- setMediaAttributes(messageRecord);
- }
+ setBubbleState(messageRecord, recipient);
+ setStatusIcons(messageRecord);
+ setContactPhoto(recipient);
+ setGroupMessageStatus(messageRecord, recipient);
+ setEvents(messageRecord);
+ setMinimumWidth();
+ setMediaAttributes(messageRecord);
}
private void initializeAttributes() {
@@ -205,6 +203,7 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
attrs.recycle();
}
+ @Override
public void unbind() {
if (recipient != null) {
recipient.removeListener(this);
@@ -236,10 +235,6 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
}
}
- private boolean hasConversationBubble(MessageRecord messageRecord) {
- return !messageRecord.isGroupAction();
- }
-
private boolean isCaptionlessMms(MessageRecord messageRecord) {
return TextUtils.isEmpty(messageRecord.getDisplayBody()) && messageRecord.isMms();
}
@@ -403,12 +398,15 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
}
@Override
- public void onModified(Recipient recipient) {
- if (hasConversationBubble(messageRecord)) {
- setBubbleState(messageRecord, recipient);
- setContactPhoto(recipient);
- setGroupMessageStatus(messageRecord, recipient);
- }
+ public void onModified(final Recipient recipient) {
+ Util.runOnMain(new Runnable() {
+ @Override
+ public void run() {
+ setBubbleState(messageRecord, recipient);
+ setContactPhoto(recipient);
+ setGroupMessageStatus(messageRecord, recipient);
+ }
+ });
}
private class ThumbnailDownloadClickListener implements ThumbnailView.ThumbnailClickListener {
@@ -416,6 +414,7 @@ public class ConversationItem extends LinearLayout implements Recipient.Recipien
DatabaseFactory.getPartDatabase(context).setTransferState(messageRecord.getId(), slide.getPart().getPartId(), PartDatabase.TRANSFER_PROGRESS_STARTED);
}
}
+
private class ThumbnailClickListener implements ThumbnailView.ThumbnailClickListener {
private void fireIntent(Slide slide) {
Log.w(TAG, "Clicked: " + slide.getUri() + " , " + slide.getContentType());
diff --git a/src/org/thoughtcrime/securesms/ConversationUpdateItem.java b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java
new file mode 100644
index 0000000000..f3939909ed
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/ConversationUpdateItem.java
@@ -0,0 +1,98 @@
+package org.thoughtcrime.securesms;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.thoughtcrime.securesms.database.model.MessageRecord;
+import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.recipients.Recipients;
+import org.thoughtcrime.securesms.util.GroupUtil;
+import org.thoughtcrime.securesms.util.Util;
+
+public class ConversationUpdateItem extends LinearLayout
+ implements Recipients.RecipientsModifiedListener, Recipient.RecipientModifiedListener, Unbindable, View.OnClickListener
+{
+ private static final String TAG = ConversationUpdateItem.class.getSimpleName();
+
+ private ImageView icon;
+ private TextView body;
+ private Recipient sender;
+ private MessageRecord messageRecord;
+
+ public ConversationUpdateItem(Context context) {
+ super(context);
+ }
+
+ public ConversationUpdateItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+
+ this.icon = (ImageView)findViewById(R.id.conversation_update_icon);
+ this.body = (TextView)findViewById(R.id.conversation_update_body);
+
+ setOnClickListener(this);
+ }
+
+ public void set(MessageRecord messageRecord) {
+ this.messageRecord = messageRecord;
+ this.sender = messageRecord.getIndividualRecipient();
+
+ this.sender.addListener(this);
+
+ if (messageRecord.isGroupAction()) {
+ icon.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_group_grey600_24dp));
+
+ if (messageRecord.isGroupQuit() && messageRecord.isOutgoing()) {
+ body.setText(R.string.MessageRecord_left_group);
+ } else if (messageRecord.isGroupQuit()) {
+ body.setText(getContext().getString(R.string.ConversationItem_group_action_left, sender.toShortString()));
+ } else {
+ GroupUtil.GroupDescription description = GroupUtil.getDescription(getContext(), messageRecord.getBody().getBody());
+ description.addListener(this);
+ body.setText(description.toString());
+ }
+ }
+ }
+
+ @Override
+ public void onModified(Recipients recipients) {
+ onModified(recipients.getPrimaryRecipient());
+ }
+
+ @Override
+ public void onModified(Recipient recipient) {
+ Util.runOnMain(new Runnable() {
+ @Override
+ public void run() {
+ set(messageRecord);
+ }
+ });
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (messageRecord.isIdentityUpdate()) {
+ Intent intent = new Intent(getContext(), RecipientPreferenceActivity.class);
+ intent.putExtra(RecipientPreferenceActivity.RECIPIENTS_EXTRA,
+ new long[] {messageRecord.getIndividualRecipient().getRecipientId()});
+
+ getContext().startActivity(intent);
+ }
+ }
+
+ @Override
+ public void unbind() {
+ if (sender != null) {
+ sender.removeListener(this);
+ }
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
index 764365f722..dad593a094 100644
--- a/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
+++ b/src/org/thoughtcrime/securesms/MessageDetailsActivity.java
@@ -184,7 +184,7 @@ public class MessageDetailsActivity extends PassphraseRequiredActionBarActivity
private void inflateMessageViewIfAbsent(MessageRecord messageRecord) {
if (conversationItem == null) {
if (messageRecord.isGroupAction()) {
- conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_activity, itemParent, false);
+ conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_update, itemParent, false);
} else if (messageRecord.isOutgoing()) {
conversationItem = (ConversationItem) inflater.inflate(R.layout.conversation_item_sent, itemParent, false);
} else {
diff --git a/src/org/thoughtcrime/securesms/Unbindable.java b/src/org/thoughtcrime/securesms/Unbindable.java
new file mode 100644
index 0000000000..3dd5cd8cc0
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/Unbindable.java
@@ -0,0 +1,5 @@
+package org.thoughtcrime.securesms;
+
+public interface Unbindable {
+ public void unbind();
+}
diff --git a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
index 161a4ad883..c58a7ab870 100644
--- a/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/MessageRecord.java
@@ -110,7 +110,7 @@ public abstract class MessageRecord extends DisplayRecord {
if (isGroupUpdate() && isOutgoing()) {
return emphasisAdded(context.getString(R.string.MessageRecord_updated_group));
} else if (isGroupUpdate()) {
- return emphasisAdded(GroupUtil.getDescription(context, getBody().getBody()));
+ return emphasisAdded(GroupUtil.getDescription(context, getBody().getBody()).toString());
} else if (isGroupQuit() && isOutgoing()) {
return emphasisAdded(context.getString(R.string.MessageRecord_left_group));
} else if (isGroupQuit()) {
diff --git a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
index e2fe05f43d..def8f3ece8 100644
--- a/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
+++ b/src/org/thoughtcrime/securesms/database/model/ThreadRecord.java
@@ -57,7 +57,7 @@ public class ThreadRecord extends DisplayRecord {
if (SmsDatabase.Types.isDecryptInProgressType(type)) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_decrypting_please_wait));
} else if (isGroupUpdate()) {
- return emphasisAdded(GroupUtil.getDescription(context, getBody().getBody()));
+ return emphasisAdded(GroupUtil.getDescription(context, getBody().getBody()).toString());
} else if (isGroupQuit()) {
return emphasisAdded(context.getString(R.string.ThreadRecord_left_the_group));
} else if (isKeyExchange()) {
diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java
index 09628ebabd..7c8a06a208 100644
--- a/src/org/thoughtcrime/securesms/recipients/Recipient.java
+++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java
@@ -179,7 +179,7 @@ public class Recipient {
}
for (RecipientModifiedListener listener : localListeners)
- listener.onModified(Recipient.this);
+ listener.onModified(this);
}
public interface RecipientModifiedListener {
diff --git a/src/org/thoughtcrime/securesms/recipients/Recipients.java b/src/org/thoughtcrime/securesms/recipients/Recipients.java
index 15a23fcc49..4408cecb7f 100644
--- a/src/org/thoughtcrime/securesms/recipients/Recipients.java
+++ b/src/org/thoughtcrime/securesms/recipients/Recipients.java
@@ -185,9 +185,7 @@ public class Recipients implements Iterable, RecipientModifiedListene
}
}
- synchronized (this) {
- listeners.add(listener);
- }
+ listeners.add(listener);
}
public synchronized void removeListener(RecipientsModifiedListener listener) {
diff --git a/src/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java b/src/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java
index 5fdcf9dcca..29e940adb2 100644
--- a/src/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java
+++ b/src/org/thoughtcrime/securesms/sms/IncomingGroupMessage.java
@@ -29,14 +29,4 @@ public class IncomingGroupMessage extends IncomingTextMessage {
return groupContext.getType().getNumber() == GroupContext.Type.QUIT_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, "");
-// }
-
}
diff --git a/src/org/thoughtcrime/securesms/util/GroupUtil.java b/src/org/thoughtcrime/securesms/util/GroupUtil.java
index 465e887f92..cc27c7d4c3 100644
--- a/src/org/thoughtcrime/securesms/util/GroupUtil.java
+++ b/src/org/thoughtcrime/securesms/util/GroupUtil.java
@@ -1,19 +1,22 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
-import com.google.protobuf.InvalidProtocolBufferException;
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.recipients.RecipientFactory;
+import org.thoughtcrime.securesms.recipients.Recipients;
import java.io.IOException;
-import java.util.List;
-import org.thoughtcrime.securesms.R;
import static org.whispersystems.textsecure.internal.push.TextSecureProtos.GroupContext;
public class GroupUtil {
private static final String ENCODED_GROUP_PREFIX = "__textsecure_group__!";
+ private static final String TAG = GroupUtil.class.getSimpleName();
public static String getEncodedId(byte[] groupId) {
return ENCODED_GROUP_PREFIX + Hex.toStringCondensed(groupId);
@@ -31,19 +34,47 @@ public class GroupUtil {
return groupId.startsWith(ENCODED_GROUP_PREFIX);
}
- public static String getDescription(Context context, String encodedGroup) {
+ public static @NonNull GroupDescription getDescription(@NonNull Context context, @Nullable String encodedGroup) {
if (encodedGroup == null) {
- return context.getString(R.string.GroupUtil_group_updated);
+ return new GroupDescription(context, null);
}
try {
- StringBuilder description = new StringBuilder();
GroupContext groupContext = GroupContext.parseFrom(Base64.decode(encodedGroup));
- List members = groupContext.getMembersList();
- String title = groupContext.getName();
+ return new GroupDescription(context, groupContext);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return new GroupDescription(context, null);
+ }
+ }
- if (!members.isEmpty()) {
- description.append(context.getString(R.string.GroupUtil_joined_the_group, Util.join(members, ", ")));
+ public static class GroupDescription {
+
+ @NonNull private final Context context;
+ @Nullable private final GroupContext groupContext;
+ @Nullable private final Recipients members;
+
+ public GroupDescription(@NonNull Context context, @Nullable GroupContext groupContext) {
+ this.context = context.getApplicationContext();
+ this.groupContext = groupContext;
+
+ if (groupContext == null || groupContext.getMembersList().isEmpty()) {
+ this.members = null;
+ } else {
+ this.members = RecipientFactory.getRecipientsFromString(context, Util.join(groupContext.getMembersList(), ", "), true);
+ }
+ }
+
+ public String toString() {
+ if (groupContext == null) {
+ return context.getString(R.string.GroupUtil_group_updated);
+ }
+
+ StringBuilder description = new StringBuilder();
+ String title = groupContext.getName();
+
+ if (members != null) {
+ description.append(context.getString(R.string.GroupUtil_joined_the_group, members.toShortString()));
}
if (title != null && !title.trim().isEmpty()) {
@@ -56,12 +87,12 @@ public class GroupUtil {
} else {
return context.getString(R.string.GroupUtil_group_updated);
}
- } catch (InvalidProtocolBufferException e) {
- Log.w("GroupUtil", e);
- return context.getString(R.string.GroupUtil_group_updated);
- } catch (IOException e) {
- Log.w("GroupUtil", e);
- return context.getString(R.string.GroupUtil_group_updated);
+ }
+
+ public void addListener(Recipients.RecipientsModifiedListener listener) {
+ if (this.members != null) {
+ this.members.addListener(listener);
+ }
}
}
}