Show when private messages have been delivered.

This commit is contained in:
akwizgran
2014-04-03 17:22:48 +01:00
parent a4954408a8
commit 1c282a8835
11 changed files with 142 additions and 35 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

View File

@@ -24,8 +24,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -52,6 +54,7 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageAddedEvent;
import org.briarproject.api.event.MessageExpiredEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.messaging.Group;
import org.briarproject.api.messaging.GroupId;
import org.briarproject.api.messaging.Message;
@@ -381,9 +384,33 @@ implements EventListener, OnClickListener, OnItemClickListener {
} else if(e instanceof MessageExpiredEvent) {
LOG.info("Message expired, reloading");
loadHeaders();
} else if(e instanceof MessagesAckedEvent) {
MessagesAckedEvent m = (MessagesAckedEvent) e;
if(m.getContactId().equals(contactId)) {
LOG.info("Messages acked");
markMessagesDelivered(m.getMessageIds());
}
}
}
private void markMessagesDelivered(final Collection<MessageId> acked) {
runOnUiThread(new Runnable() {
public void run() {
Set<MessageId> ackedSet = new HashSet<MessageId>(acked);
boolean changed = false;
int count = adapter.getCount();
for(int i = 0; i < count; i++) {
ConversationItem item = adapter.getItem(i);
if(ackedSet.contains(item.getHeader().getId())) {
item.setDelivered(true);
changed = true;
}
}
if(changed) adapter.notifyDataSetChanged();
}
});
}
public void onClick(View view) {
String message = content.getText().toString();
if(message.equals("")) return;

View File

@@ -1,13 +1,16 @@
package org.briarproject.android.contact;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.View.INVISIBLE;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP;
import java.util.ArrayList;
import org.briarproject.R;
import org.briarproject.android.util.ElasticHorizontalSpace;
import org.briarproject.android.util.LayoutUtils;
import org.briarproject.api.db.MessageHeader;
import org.briarproject.util.StringUtils;
@@ -19,6 +22,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -60,23 +64,45 @@ class ConversationAdapter extends ArrayAdapter<ConversationItem> {
attachment.setImageResource(R.drawable.content_attachment);
content = attachment;
}
content.setId(2);
content.setLayoutParams(MATCH_WRAP);
content.setBackgroundColor(background);
content.setPadding(pad, pad, pad, 0);
layout.addView(content);
TextView date = new TextView(ctx);
date.setId(1);
date.setLayoutParams(MATCH_WRAP);
if(header.isLocal()) date.setGravity(RIGHT);
else date.setGravity(LEFT);
date.setTextColor(res.getColor(R.color.private_message_date));
date.setBackgroundColor(background);
date.setPadding(pad, 0, pad, pad);
long timestamp = header.getTimestamp();
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
layout.addView(date);
if(header.isLocal()) {
LinearLayout footer = new LinearLayout(ctx);
footer.setLayoutParams(MATCH_WRAP);
footer.setOrientation(HORIZONTAL);
footer.setGravity(BOTTOM);
footer.setPadding(pad, 0, pad, pad);
footer.setBackgroundColor(background);
footer.addView(new ElasticHorizontalSpace(ctx));
ImageView delivered = new ImageView(ctx);
delivered.setPadding(0, 0, pad, 0);
delivered.setImageResource(R.drawable.message_delivered);
if(!item.isDelivered()) delivered.setVisibility(INVISIBLE);
footer.addView(delivered);
TextView date = new TextView(ctx);
date.setTextColor(res.getColor(R.color.private_message_date));
long timestamp = header.getTimestamp();
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
footer.addView(date);
layout.addView(footer);
} else {
TextView date = new TextView(ctx);
date.setLayoutParams(MATCH_WRAP);
date.setGravity(LEFT);
date.setTextColor(res.getColor(R.color.private_message_date));
date.setBackgroundColor(background);
date.setPadding(pad, 0, pad, pad);
long timestamp = header.getTimestamp();
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
layout.addView(date);
}
return layout;
}

View File

@@ -7,10 +7,12 @@ class ConversationItem {
private final MessageHeader header;
private byte[] body;
private boolean delivered;
ConversationItem(MessageHeader header) {
this.header = header;
body = null;
delivered = header.isDelivered();
}
MessageHeader getHeader() {
@@ -24,4 +26,12 @@ class ConversationItem {
void setBody(byte[] body) {
this.body = body;
}
boolean isDelivered() {
return delivered;
}
void setDelivered(boolean delivered) {
this.delivered = delivered;
}
}

View File

@@ -12,11 +12,11 @@ public class MessageHeader {
private final Author.Status authorStatus;
private final String contentType;
private final long timestamp;
private final boolean local, read;
private final boolean local, read, delivered;
public MessageHeader(MessageId id, MessageId parent, GroupId groupId,
Author author, Author.Status authorStatus, String contentType,
long timestamp, boolean local, boolean read) {
long timestamp, boolean local, boolean read, boolean delivered) {
this.id = id;
this.parent = parent;
this.groupId = groupId;
@@ -26,6 +26,7 @@ public class MessageHeader {
this.timestamp = timestamp;
this.local = local;
this.read = read;
this.delivered = delivered;
}
/** Returns the message's unique identifier. */
@@ -79,4 +80,12 @@ public class MessageHeader {
public boolean isRead() {
return read;
}
/**
* Returns true if the message has been delivered. (This only applies to
* locally generated private messages.)
*/
public boolean isDelivered() {
return delivered;
}
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.api.event;
import java.util.Collection;
import org.briarproject.api.ContactId;
import org.briarproject.api.messaging.MessageId;
/** An event that is broadcast when messages are acked by a contact. */
public class MessagesAckedEvent extends Event {
private final ContactId contactId;
private final Collection<MessageId> acked;
public MessagesAckedEvent(ContactId contactId,
Collection<MessageId> acked ) {
this.contactId = contactId;
this.acked = acked;
}
public ContactId getContactId() {
return contactId;
}
public Collection<MessageId> getMessageIds() {
return acked;
}
}

View File

@@ -53,6 +53,7 @@ import org.briarproject.api.event.MessageExpiredEvent;
import org.briarproject.api.event.MessageRequestedEvent;
import org.briarproject.api.event.MessageToAckEvent;
import org.briarproject.api.event.MessageToRequestEvent;
import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.RemoteRetentionTimeUpdatedEvent;
import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
import org.briarproject.api.event.RemoteTransportsUpdatedEvent;
@@ -1329,6 +1330,7 @@ DatabaseCleaner.Callback {
}
public void receiveAck(ContactId c, Ack a) throws DbException {
Collection<MessageId> acked = new ArrayList<MessageId>();
contactLock.readLock().lock();
try {
messageLock.writeLock().lock();
@@ -1338,8 +1340,10 @@ DatabaseCleaner.Callback {
if(!db.containsContact(txn, c))
throw new NoSuchContactException();
for(MessageId m : a.getMessageIds()) {
if(db.containsVisibleMessage(txn, c, m))
if(db.containsVisibleMessage(txn, c, m)) {
db.raiseSeenFlag(txn, c, m);
acked.add(m);
}
}
db.commitTransaction(txn);
} catch(DbException e) {
@@ -1352,6 +1356,7 @@ DatabaseCleaner.Callback {
} finally {
contactLock.readLock().unlock();
}
callListeners(new MessagesAckedEvent(c, acked));
}
public void receiveMessage(ContactId c, Message m) throws DbException {

View File

@@ -1482,14 +1482,17 @@ abstract class JdbcDatabase implements Database<Connection> {
Author remoteAuthor = new Author(remoteId, remoteName, remoteKey);
if(rs.next()) throw new DbException();
// Get the message headers
sql = "SELECT messageId, parentId, m.groupId, contentType,"
+ " timestamp, local, read"
sql = "SELECT m.messageId, parentId, m.groupId, contentType,"
+ " timestamp, local, read, seen"
+ " FROM messages AS m"
+ " JOIN groups AS g"
+ " ON m.groupId = g.groupId"
+ " JOIN groupVisibilities AS gv"
+ " ON m.groupId = gv.groupId"
+ " WHERE contactId = ?"
+ " JOIN statuses AS s"
+ " ON m.messageId = s.messageId"
+ " AND gv.contactId = s.contactId"
+ " WHERE gv.contactId = ?"
+ " AND inbox = TRUE";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
@@ -1504,15 +1507,10 @@ abstract class JdbcDatabase implements Database<Connection> {
long timestamp = rs.getLong(5);
boolean local = rs.getBoolean(6);
boolean read = rs.getBoolean(7);
if(local) {
headers.add(new MessageHeader(id, parent, groupId,
localAuthor, VERIFIED, contentType, timestamp,
true, read));
} else {
headers.add(new MessageHeader(id, parent, groupId,
remoteAuthor, VERIFIED, contentType, timestamp,
false, read));
}
boolean seen = rs.getBoolean(8);
Author author = local ? localAuthor : remoteAuthor;
headers.add(new MessageHeader(id, parent, groupId, author,
VERIFIED, contentType, timestamp, local, read, seen));
}
rs.close();
ps.close();
@@ -1723,12 +1721,12 @@ abstract class JdbcDatabase implements Database<Connection> {
boolean read = rs.getBoolean(9);
boolean isSelf = rs.getBoolean(10);
boolean isContact = rs.getBoolean(11);
Author.Status authorStatus;
if(author == null) authorStatus = ANONYMOUS;
else if(isSelf || isContact) authorStatus = VERIFIED;
else authorStatus = UNKNOWN;
headers.add(new MessageHeader(id, parent, g, author,
authorStatus, contentType, timestamp, local, read));
Author.Status status;
if(author == null) status = ANONYMOUS;
else if(isSelf || isContact) status = VERIFIED;
else status = UNKNOWN;
headers.add(new MessageHeader(id, parent, g, author, status,
contentType, timestamp, local, read, false));
}
rs.close();
ps.close();

View File

@@ -1531,7 +1531,9 @@ public class H2DatabaseTest extends BriarTestCase {
db.getInboxMessageHeaders(txn, contactId));
// Add a message to the inbox group - the header should be returned
db.addMessage(txn, message, true);
boolean local = true, seen = false;
db.addMessage(txn, message, local);
db.addStatus(txn, contactId, messageId, false, seen);
Collection<MessageHeader> headers =
db.getInboxMessageHeaders(txn, contactId);
assertEquals(1, headers.size());
@@ -1542,6 +1544,9 @@ public class H2DatabaseTest extends BriarTestCase {
assertEquals(localAuthor, header.getAuthor());
assertEquals(contentType, header.getContentType());
assertEquals(timestamp, header.getTimestamp());
assertEquals(local, header.isLocal());
assertEquals(false, header.isRead());
assertEquals(seen, header.isDelivered());
assertFalse(header.isRead());
db.commitTransaction(txn);