mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Wrap messages, introductions and forum invites in a ConversationManager
This commit is contained in:
@@ -9,6 +9,7 @@ import org.briarproject.android.forum.ForumPersistentData;
|
||||
import org.briarproject.android.report.BriarReportSender;
|
||||
import org.briarproject.api.contact.ContactExchangeTask;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.conversation.ConversationManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.api.crypto.PasswordStrengthEstimator;
|
||||
@@ -84,6 +85,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
|
||||
|
||||
ContactManager contactManager();
|
||||
|
||||
ConversationManager conversationManager();
|
||||
|
||||
MessagingManager messagingManager();
|
||||
|
||||
PrivateMessageFactory privateMessageFactory();
|
||||
|
||||
@@ -23,6 +23,11 @@ import org.briarproject.android.util.BriarRecyclerView;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.conversation.ConversationForumInvitationItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionRequestItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionResponseItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.conversation.ConversationMessageItem;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.event.ContactAddedEvent;
|
||||
@@ -41,6 +46,8 @@ import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.plugins.ConnectionRegistry;
|
||||
@@ -257,7 +264,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
LOG.info("Message received, update contact");
|
||||
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
updateItem(p.getGroupId(), ConversationItem.from(h));
|
||||
updateItem(p.getGroupId(), ConversationMessageItem.from(h));
|
||||
} else if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent m = (MessageStateChangedEvent) e;
|
||||
ClientId c = m.getClientId();
|
||||
@@ -354,7 +361,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
messagingManager.getMessageHeaders(id);
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
messages.add(ConversationItem.from(h));
|
||||
messages.add(ConversationMessageItem.from(h));
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
@@ -365,7 +372,12 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
introductionManager
|
||||
.getIntroductionMessages(id);
|
||||
for (IntroductionMessage m : introductions) {
|
||||
messages.add(ConversationItem.from(m));
|
||||
if (m instanceof IntroductionRequest)
|
||||
messages.add(ConversationIntroductionRequestItem
|
||||
.from((IntroductionRequest) m));
|
||||
else
|
||||
messages.add(ConversationIntroductionResponseItem
|
||||
.from((IntroductionResponse) m));
|
||||
}
|
||||
duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
@@ -375,7 +387,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
Collection<ForumInvitationMessage> invitations =
|
||||
forumSharingManager.getInvitationMessages(id);
|
||||
for (ForumInvitationMessage i : invitations) {
|
||||
messages.add(ConversationItem.from(i));
|
||||
messages.add(ConversationForumInvitationItem.from(i));
|
||||
}
|
||||
duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ContactListItem {
|
||||
|
||||
@@ -44,8 +43,8 @@ public class ContactListItem {
|
||||
empty = empty && message == null;
|
||||
if (message != null) {
|
||||
if (message.getTime() > timestamp) timestamp = message.getTime();
|
||||
if (message instanceof IncomingItem &&
|
||||
!((IncomingItem) message).isRead())
|
||||
if (message instanceof ConversationItem.IncomingItem &&
|
||||
!((ConversationItem.IncomingItem) message).isRead())
|
||||
unread++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +30,18 @@ import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.introduction.IntroductionActivity;
|
||||
import org.briarproject.android.util.BriarRecyclerView;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionRequestItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionResponseItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.conversation.ConversationItem.IncomingItem;
|
||||
import org.briarproject.api.conversation.ConversationManager;
|
||||
import org.briarproject.api.conversation.ConversationMessageItem;
|
||||
import org.briarproject.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.event.ContactConnectedEvent;
|
||||
import org.briarproject.api.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.api.event.ContactRemovedEvent;
|
||||
@@ -50,13 +54,8 @@ import org.briarproject.api.event.IntroductionResponseReceivedEvent;
|
||||
import org.briarproject.api.event.MessagesAckedEvent;
|
||||
import org.briarproject.api.event.MessagesSentEvent;
|
||||
import org.briarproject.api.event.PrivateMessageReceivedEvent;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
@@ -68,10 +67,8 @@ import org.briarproject.util.StringUtils;
|
||||
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;
|
||||
@@ -85,12 +82,11 @@ import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
|
||||
|
||||
public class ConversationActivity extends BriarActivity
|
||||
implements EventListener, OnClickListener,
|
||||
ConversationAdapter.IntroductionHandler {
|
||||
ConversationAdapter.ConversationHandler,
|
||||
ConversationAdapter.MessageUpdatedHandler {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ConversationActivity.class.getName());
|
||||
@@ -116,22 +112,17 @@ public class ConversationActivity extends BriarActivity
|
||||
@Inject
|
||||
protected volatile ContactManager contactManager;
|
||||
@Inject
|
||||
protected volatile MessagingManager messagingManager;
|
||||
protected volatile ConversationManager conversationManager;
|
||||
@Inject
|
||||
protected volatile EventBus eventBus;
|
||||
@Inject
|
||||
volatile PrivateMessageFactory privateMessageFactory;
|
||||
@Inject
|
||||
protected volatile IntroductionManager introductionManager;
|
||||
@Inject
|
||||
protected volatile ForumSharingManager forumSharingManager;
|
||||
|
||||
private volatile GroupId groupId = null;
|
||||
private volatile ContactId contactId = null;
|
||||
private volatile String contactName = null;
|
||||
private volatile byte[] contactIdenticonKey = null;
|
||||
private volatile boolean connected = false;
|
||||
private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -165,7 +156,7 @@ public class ConversationActivity extends BriarActivity
|
||||
ViewCompat.setTransitionName(toolbarAvatar, "avatar" + hexGroupId);
|
||||
ViewCompat.setTransitionName(toolbarStatus, "bulb" + hexGroupId);
|
||||
|
||||
adapter = new ConversationAdapter(this, this);
|
||||
adapter = new ConversationAdapter(this, this, this);
|
||||
list = (BriarRecyclerView) findViewById(R.id.conversationView);
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
list.setAdapter(adapter);
|
||||
@@ -267,7 +258,7 @@ public class ConversationActivity extends BriarActivity
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
if (contactId == null)
|
||||
contactId = messagingManager.getContactId(groupId);
|
||||
contactId = conversationManager.getContactId(groupId);
|
||||
if (contactName == null || contactIdenticonKey == null) {
|
||||
Contact contact = contactManager.getContact(contactId);
|
||||
contactName = contact.getAuthor().getName();
|
||||
@@ -324,19 +315,13 @@ public class ConversationActivity extends BriarActivity
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
if (contactId == null)
|
||||
contactId = messagingManager.getContactId(groupId);
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
messagingManager.getMessageHeaders(contactId);
|
||||
Collection<IntroductionMessage> introductions =
|
||||
introductionManager
|
||||
.getIntroductionMessages(contactId);
|
||||
Collection<ForumInvitationMessage> invitations =
|
||||
forumSharingManager
|
||||
.getInvitationMessages(contactId);
|
||||
contactId = conversationManager.getContactId(groupId);
|
||||
List<ConversationItem> items =
|
||||
conversationManager.getMessages(contactId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading headers took " + duration + " ms");
|
||||
displayMessages(headers, introductions, invitations);
|
||||
displayMessages(items);
|
||||
} catch (NoSuchContactException e) {
|
||||
finishOnUiThread();
|
||||
} catch (DbException e) {
|
||||
@@ -347,46 +332,16 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMessages(final Collection<PrivateMessageHeader> headers,
|
||||
final Collection<IntroductionMessage> introductions,
|
||||
final Collection<ForumInvitationMessage> invitations) {
|
||||
private void displayMessages(final List<ConversationItem> items) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendButton.setEnabled(true);
|
||||
if (headers.isEmpty() && introductions.isEmpty() &&
|
||||
invitations.isEmpty()) {
|
||||
if (items.isEmpty()) {
|
||||
// we have no messages,
|
||||
// so let the list know to hide progress bar
|
||||
list.showData();
|
||||
} else {
|
||||
List<ConversationItem> items = new ArrayList<>();
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
ConversationMessageItem item =
|
||||
(ConversationMessageItem) ConversationItem
|
||||
.from(h);
|
||||
byte[] body = bodyCache.get(h.getId());
|
||||
if (body == null) loadMessageBody(h);
|
||||
else item.setBody(body);
|
||||
items.add(item);
|
||||
}
|
||||
for (IntroductionMessage m : introductions) {
|
||||
ConversationItem item;
|
||||
if (m instanceof IntroductionRequest) {
|
||||
item = ConversationItem
|
||||
.from((IntroductionRequest) m);
|
||||
} else {
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this,
|
||||
contactName,
|
||||
(IntroductionResponse) m);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
for (ForumInvitationMessage i : invitations) {
|
||||
ConversationItem item = ConversationItem.from(i);
|
||||
items.add(item);
|
||||
}
|
||||
adapter.addAll(items);
|
||||
// Scroll to the bottom
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
@@ -395,47 +350,6 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void loadMessageBody(final PrivateMessageHeader h) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
byte[] body = messagingManager.getMessageBody(h.getId());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading message took " + duration + " ms");
|
||||
displayMessageBody(h.getId(), body);
|
||||
} catch (NoSuchMessageException e) {
|
||||
// The item will be removed when we get the event
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMessageBody(final MessageId m, final byte[] body) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
bodyCache.put(m, body);
|
||||
SparseArray<ConversationMessageItem> messages =
|
||||
adapter.getPrivateMessages();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ConversationMessageItem item = messages.valueAt(i);
|
||||
if (item.getId().equals(m)) {
|
||||
item.setBody(body);
|
||||
adapter.notifyItemChanged(messages.keyAt(i));
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addConversationItem(final ConversationItem item) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -448,11 +362,11 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
private void markMessagesRead() {
|
||||
List<MessageId> unread = new ArrayList<>();
|
||||
List<ConversationItem> unread = new ArrayList<>();
|
||||
SparseArray<IncomingItem> list = adapter.getIncomingMessages();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
IncomingItem item = list.valueAt(i);
|
||||
if (!item.isRead()) unread.add(item.getId());
|
||||
if (!item.isRead()) unread.add((ConversationItem) item);
|
||||
}
|
||||
if (unread.isEmpty()) return;
|
||||
if (LOG.isLoggable(INFO))
|
||||
@@ -460,16 +374,14 @@ public class ConversationActivity extends BriarActivity
|
||||
markMessagesRead(Collections.unmodifiableList(unread));
|
||||
}
|
||||
|
||||
private void markMessagesRead(final Collection<MessageId> unread) {
|
||||
private void markMessagesRead(final Collection<ConversationItem> unread) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
for (MessageId m : unread)
|
||||
// not really clean, but the messaging manager can
|
||||
// handle introduction messages as well
|
||||
messagingManager.setReadFlag(m, true);
|
||||
for (ConversationItem item : unread)
|
||||
conversationManager.setReadFlag(item, true);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Marking read took " + duration + " ms");
|
||||
@@ -494,9 +406,10 @@ public class ConversationActivity extends BriarActivity
|
||||
if (p.getGroupId().equals(groupId)) {
|
||||
LOG.info("Message received, adding");
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
addConversationItem(ConversationItem.from(h));
|
||||
loadMessageBody(h);
|
||||
markMessageReadIfNew(h);
|
||||
ConversationMessageItem m = ConversationMessageItem.from(h);
|
||||
conversationManager.loadMessageContent(m);
|
||||
addConversationItem(m);
|
||||
markMessageReadIfNew(m);
|
||||
}
|
||||
} else if (e instanceof MessagesSentEvent) {
|
||||
MessagesSentEvent m = (MessagesSentEvent) e;
|
||||
@@ -529,7 +442,8 @@ public class ConversationActivity extends BriarActivity
|
||||
(IntroductionRequestReceivedEvent) e;
|
||||
if (event.getContactId().equals(contactId)) {
|
||||
IntroductionRequest ir = event.getIntroductionRequest();
|
||||
ConversationItem item = new ConversationIntroductionInItem(ir);
|
||||
ConversationItem item =
|
||||
ConversationIntroductionRequestItem.from(ir);
|
||||
addConversationItem(item);
|
||||
}
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
@@ -538,7 +452,7 @@ public class ConversationActivity extends BriarActivity
|
||||
if (event.getContactId().equals(contactId)) {
|
||||
IntroductionResponse ir = event.getIntroductionResponse();
|
||||
ConversationItem item =
|
||||
ConversationItem.from(this, contactName, ir);
|
||||
ConversationIntroductionResponseItem.from(ir);
|
||||
addConversationItem(item);
|
||||
}
|
||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||
@@ -550,31 +464,31 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
}
|
||||
|
||||
private void markMessageReadIfNew(final PrivateMessageHeader h) {
|
||||
private void markMessageReadIfNew(final ConversationItem item) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ConversationItem item = adapter.getLastItem();
|
||||
if (item != null) {
|
||||
ConversationItem last = adapter.getLastItem();
|
||||
if (last != null) {
|
||||
// Mark the message read if it's the newest message
|
||||
long lastMsgTime = item.getTime();
|
||||
long newMsgTime = h.getTimestamp();
|
||||
if (newMsgTime > lastMsgTime) markNewMessageRead(h.getId());
|
||||
long lastMsgTime = last.getTime();
|
||||
long newMsgTime = item.getTime();
|
||||
if (newMsgTime > lastMsgTime) markNewMessageRead(item);
|
||||
else loadMessages();
|
||||
} else {
|
||||
// mark the message as read as well if it is the first one
|
||||
markNewMessageRead(h.getId());
|
||||
markNewMessageRead(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void markNewMessageRead(final MessageId m) {
|
||||
private void markNewMessageRead(final ConversationItem item) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
messagingManager.setReadFlag(m, true);
|
||||
conversationManager.setReadFlag(item, true);
|
||||
loadMessages();
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -590,9 +504,10 @@ public class ConversationActivity extends BriarActivity
|
||||
@Override
|
||||
public void run() {
|
||||
Set<MessageId> messages = new HashSet<>(messageIds);
|
||||
SparseArray<OutgoingItem> list = adapter.getOutgoingMessages();
|
||||
SparseArray<ConversationItem.OutgoingItem> list =
|
||||
adapter.getOutgoingMessages();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
OutgoingItem item = list.valueAt(i);
|
||||
ConversationItem.OutgoingItem item = list.valueAt(i);
|
||||
if (messages.contains(item.getId())) {
|
||||
item.setSent(sent);
|
||||
item.setSeen(seen);
|
||||
@@ -641,19 +556,10 @@ public class ConversationActivity extends BriarActivity
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
messagingManager.addLocalMessage(m);
|
||||
ConversationItem item = conversationManager.addLocalMessage(m, body);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Storing message took " + duration + " ms");
|
||||
|
||||
PrivateMessageHeader h = new PrivateMessageHeader(
|
||||
m.getMessage().getId(),
|
||||
m.getMessage().getTimestamp(), m.getContentType(),
|
||||
true, false, false, false);
|
||||
ConversationMessageItem item =
|
||||
(ConversationMessageItem) ConversationItem.from(h);
|
||||
item.setBody(body);
|
||||
bodyCache.put(m.getMessage().getId(), body);
|
||||
addConversationItem(item);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -688,7 +594,7 @@ public class ConversationActivity extends BriarActivity
|
||||
try {
|
||||
// make sure contactId is initialised
|
||||
if (contactId == null)
|
||||
contactId = messagingManager.getContactId(groupId);
|
||||
contactId = conversationManager.getContactId(groupId);
|
||||
// remove contact with that ID
|
||||
contactManager.removeContact(contactId);
|
||||
} catch (DbException e) {
|
||||
@@ -739,7 +645,7 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToIntroduction(final SessionId sessionId,
|
||||
public void respondToItem(final ConversationItem item,
|
||||
final boolean accept) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
@@ -747,22 +653,15 @@ public class ConversationActivity extends BriarActivity
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
try {
|
||||
if (accept) {
|
||||
introductionManager
|
||||
.acceptIntroduction(contactId, sessionId,
|
||||
timestamp);
|
||||
} else {
|
||||
introductionManager
|
||||
.declineIntroduction(contactId, sessionId,
|
||||
timestamp);
|
||||
}
|
||||
conversationManager
|
||||
.respondToItem(contactId, item, accept, timestamp);
|
||||
loadMessages();
|
||||
} catch (DbException | FormatException e) {
|
||||
// TODO decide how to make this type-agnostic
|
||||
introductionResponseError();
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -778,4 +677,15 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageUpdated(final int position) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
adapter.notifyItemChanged(position);
|
||||
// Scroll to the bottom
|
||||
list.scrollToPosition(adapter.getItemCount() - 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,26 @@ import android.widget.TextView;
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.forum.ForumInvitationsActivity;
|
||||
import org.briarproject.android.util.AndroidUtils;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.api.conversation.ConversationForumInvitationItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionRequestItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionResponseItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.conversation.ConversationItem.IncomingItem;
|
||||
import org.briarproject.api.conversation.ConversationItem.OutgoingItem;
|
||||
import org.briarproject.api.conversation.ConversationMessageItem;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||
import static android.support.v7.widget.RecyclerView.NO_ID;
|
||||
import static android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import static org.briarproject.android.contact.ConversationItem.MSG_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.MSG_IN_UNREAD;
|
||||
import static org.briarproject.android.contact.ConversationItem.MSG_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.NOTICE_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.NOTICE_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
|
||||
|
||||
class ConversationAdapter extends RecyclerView.Adapter {
|
||||
|
||||
@@ -43,13 +42,17 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
new SortedList<>(ConversationItem.class, new ListCallbacks());
|
||||
|
||||
private Context ctx;
|
||||
private IntroductionHandler intro;
|
||||
private ConversationHandler handler;
|
||||
private MessageUpdatedHandler msgUpdated;
|
||||
private String contactName;
|
||||
|
||||
public ConversationAdapter(Context context,
|
||||
IntroductionHandler introductionHandler) {
|
||||
ConversationHandler conversationHandler,
|
||||
MessageUpdatedHandler messageUpdatedHandler) {
|
||||
ctx = context;
|
||||
intro = introductionHandler;
|
||||
handler = conversationHandler;
|
||||
msgUpdated = messageUpdatedHandler;
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
public void setContactName(String contactName) {
|
||||
@@ -59,82 +62,99 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return getItem(position).getType();
|
||||
ConversationItem m = getItem(position);
|
||||
if (m instanceof IncomingItem) {
|
||||
if (m instanceof ConversationMessageItem) {
|
||||
return R.layout.list_item_msg_in;
|
||||
} else if (m instanceof ConversationIntroductionRequestItem) {
|
||||
return R.layout.list_item_introduction_in;
|
||||
} else if (m instanceof ConversationIntroductionResponseItem) {
|
||||
return R.layout.list_item_notice_in;
|
||||
} else if (m instanceof ConversationForumInvitationItem) {
|
||||
return R.layout.list_item_forum_invitation_in;
|
||||
}
|
||||
} else if (m instanceof OutgoingItem) {
|
||||
if (m instanceof ConversationMessageItem) {
|
||||
return R.layout.list_item_msg_out;
|
||||
} else if (m instanceof ConversationIntroductionRequestItem) {
|
||||
return R.layout.list_item_introduction_out;
|
||||
} else if (m instanceof ConversationIntroductionResponseItem) {
|
||||
return R.layout.list_item_notice_out;
|
||||
} else if (m instanceof ConversationForumInvitationItem) {
|
||||
return R.layout.list_item_forum_invitation_out;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unhandled Conversation Message");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
||||
View v;
|
||||
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
type, viewGroup, false);
|
||||
|
||||
// outgoing message (local)
|
||||
if (type == MSG_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_out, viewGroup, false);
|
||||
return new MessageHolder(v, type);
|
||||
} else if (type == INTRODUCTION_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_introduction_in, viewGroup, false);
|
||||
return new IntroductionHolder(v, type);
|
||||
} else if (type == INTRODUCTION_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_introduction_out, viewGroup, false);
|
||||
return new IntroductionHolder(v, type);
|
||||
} else if (type == NOTICE_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_notice_in, viewGroup, false);
|
||||
return new NoticeHolder(v, type);
|
||||
} else if (type == NOTICE_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_notice_out, viewGroup, false);
|
||||
return new NoticeHolder(v, type);
|
||||
} else if (type == FORUM_INVITATION_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_forum_invitation_in, viewGroup, false);
|
||||
return new InvitationHolder(v, type);
|
||||
} else if (type == FORUM_INVITATION_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_forum_invitation_out, viewGroup, false);
|
||||
return new InvitationHolder(v, type);
|
||||
}
|
||||
// incoming message (non-local)
|
||||
else {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_in, viewGroup, false);
|
||||
return new MessageHolder(v, type);
|
||||
switch (type) {
|
||||
case R.layout.list_item_msg_in:
|
||||
return new MessageHolder(v, false);
|
||||
case R.layout.list_item_msg_out:
|
||||
return new MessageHolder(v, true);
|
||||
|
||||
case R.layout.list_item_introduction_in:
|
||||
return new IntroductionHolder(v, false);
|
||||
case R.layout.list_item_introduction_out:
|
||||
return new IntroductionHolder(v, true);
|
||||
|
||||
case R.layout.list_item_notice_in:
|
||||
return new NoticeHolder(v, false);
|
||||
case R.layout.list_item_notice_out:
|
||||
return new NoticeHolder(v, true);
|
||||
|
||||
case R.layout.list_item_forum_invitation_in:
|
||||
return new InvitationHolder(v, false);
|
||||
case R.layout.list_item_forum_invitation_out:
|
||||
return new InvitationHolder(v, true);
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Unhandled Conversation Message");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder ui, int position) {
|
||||
ConversationItem item = getItem(position);
|
||||
if (item instanceof ConversationMessageItem) {
|
||||
bindMessage((MessageHolder) ui, (ConversationMessageItem) item);
|
||||
} else if (item instanceof ConversationIntroductionOutItem) {
|
||||
bindIntroduction((IntroductionHolder) ui,
|
||||
(ConversationIntroductionOutItem) item, position);
|
||||
} else if (item instanceof ConversationIntroductionInItem) {
|
||||
bindIntroduction((IntroductionHolder) ui,
|
||||
(ConversationIntroductionInItem) item, position);
|
||||
} else if (item instanceof ConversationNoticeOutItem) {
|
||||
bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item);
|
||||
} else if (item instanceof ConversationNoticeInItem) {
|
||||
bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item);
|
||||
} else if (item instanceof ConversationForumInvitationOutItem) {
|
||||
bindInvitation((InvitationHolder) ui,
|
||||
(ConversationForumInvitationOutItem) item);
|
||||
} else if (item instanceof ConversationForumInvitationInItem) {
|
||||
bindInvitation((InvitationHolder) ui,
|
||||
(ConversationForumInvitationInItem) item);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled Conversation Item");
|
||||
switch (ui.getItemViewType()) {
|
||||
case R.layout.list_item_msg_in:
|
||||
case R.layout.list_item_msg_out:
|
||||
bindMessage((MessageHolder) ui, position);
|
||||
break;
|
||||
|
||||
case R.layout.list_item_introduction_in:
|
||||
case R.layout.list_item_introduction_out:
|
||||
bindIntroduction((IntroductionHolder) ui, position);
|
||||
break;
|
||||
|
||||
case R.layout.list_item_notice_in:
|
||||
case R.layout.list_item_notice_out:
|
||||
bindNotice((NoticeHolder) ui, position);
|
||||
break;
|
||||
|
||||
case R.layout.list_item_forum_invitation_in:
|
||||
case R.layout.list_item_forum_invitation_out:
|
||||
bindInvitation((InvitationHolder) ui, position);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Unhandled Conversation Message");
|
||||
}
|
||||
}
|
||||
|
||||
private void bindMessage(MessageHolder ui, ConversationMessageItem item) {
|
||||
private void bindMessage(final MessageHolder ui, int position) {
|
||||
|
||||
ConversationMessageItem item =
|
||||
(ConversationMessageItem) getItem(position);
|
||||
PrivateMessageHeader header = item.getHeader();
|
||||
|
||||
if (item instanceof ConversationItem.OutgoingItem) {
|
||||
if (item instanceof OutgoingItem) {
|
||||
if (((OutgoingItem) item).isSeen()) {
|
||||
ui.status.setImageResource(R.drawable.message_delivered_white);
|
||||
} else if (((OutgoingItem) item).isSent()) {
|
||||
@@ -143,7 +163,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
ui.status.setImageResource(R.drawable.message_stored_white);
|
||||
}
|
||||
} else {
|
||||
if (item.getType() == MSG_IN_UNREAD) {
|
||||
if (!((IncomingItem) item).isRead()) {
|
||||
// TODO implement new unread message highlight according to #232
|
||||
/* int left = ui.layout.getPaddingLeft();
|
||||
int top = ui.layout.getPaddingTop();
|
||||
@@ -162,6 +182,12 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
|
||||
if (item.getBody() == null) {
|
||||
ui.body.setText("\u2026");
|
||||
item.setContentListener(new ConversationItem.ContentListener() {
|
||||
@Override
|
||||
public void contentReady() {
|
||||
msgUpdated.messageUpdated(ui.getAdapterPosition());
|
||||
}
|
||||
});
|
||||
} else if (header.getContentType().equals("text/plain")) {
|
||||
ui.body.setText(
|
||||
StringUtils.trim(StringUtils.fromUtf8(item.getBody())));
|
||||
@@ -173,9 +199,10 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, timestamp));
|
||||
}
|
||||
|
||||
private void bindIntroduction(IntroductionHolder ui,
|
||||
final ConversationIntroductionItem item, final int position) {
|
||||
private void bindIntroduction(IntroductionHolder ui, final int position) {
|
||||
|
||||
final ConversationIntroductionRequestItem item =
|
||||
(ConversationIntroductionRequestItem) getItem(position);
|
||||
final IntroductionRequest ir = item.getIntroductionRequest();
|
||||
|
||||
String message = ir.getMessage();
|
||||
@@ -189,11 +216,10 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
// Outgoing Introduction Request
|
||||
if (item instanceof ConversationIntroductionOutItem) {
|
||||
if (item instanceof OutgoingItem) {
|
||||
ui.text.setText(ctx.getString(R.string.introduction_request_sent,
|
||||
contactName, ir.getName()));
|
||||
ConversationIntroductionOutItem i =
|
||||
(ConversationIntroductionOutItem) item;
|
||||
OutgoingItem i = (OutgoingItem) item;
|
||||
if (i.isSeen()) {
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
ui.message.status.setImageResource(
|
||||
@@ -239,7 +265,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
ui.acceptButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
intro.respondToIntroduction(ir.getSessionId(), true);
|
||||
handler.respondToItem(item, true);
|
||||
item.setAnswered(true);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
@@ -249,7 +275,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
ui.declineButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
intro.respondToIntroduction(ir.getSessionId(), false);
|
||||
handler.respondToItem(item, false);
|
||||
item.setAnswered(true);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
@@ -258,13 +284,14 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
|
||||
}
|
||||
|
||||
private void bindNotice(NoticeHolder ui, ConversationNoticeItem item) {
|
||||
private void bindNotice(NoticeHolder ui, int position) {
|
||||
ConversationItem item = getItem(position);
|
||||
|
||||
ui.text.setText(item.getText());
|
||||
ui.text.setText(getNoticeText(item));
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
|
||||
|
||||
if (item instanceof ConversationNoticeOutItem) {
|
||||
ConversationNoticeOutItem n = (ConversationNoticeOutItem) item;
|
||||
if (item instanceof OutgoingItem) {
|
||||
OutgoingItem n = (OutgoingItem) item;
|
||||
if (n.isSeen()) {
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
} else if (n.isSent()) {
|
||||
@@ -275,9 +302,49 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void bindInvitation(InvitationHolder ui,
|
||||
final ConversationForumInvitationItem item) {
|
||||
private String getNoticeText(ConversationItem m) {
|
||||
if (m instanceof ConversationIntroductionResponseItem) {
|
||||
IntroductionResponse ir =
|
||||
((ConversationIntroductionResponseItem) m)
|
||||
.getIntroductionResponse();
|
||||
|
||||
if (ir.isLocal()) {
|
||||
if (ir.wasAccepted()) {
|
||||
return ctx.getString(
|
||||
R.string.introduction_response_accepted_sent,
|
||||
ir.getName());
|
||||
} else {
|
||||
return ctx.getString(
|
||||
R.string.introduction_response_declined_sent,
|
||||
ir.getName());
|
||||
}
|
||||
} else {
|
||||
if (ir.wasAccepted()) {
|
||||
return ctx.getString(
|
||||
R.string.introduction_response_accepted_received,
|
||||
contactName, ir.getName());
|
||||
} else {
|
||||
if (ir.isIntroducer()) {
|
||||
return ctx.getString(
|
||||
R.string.introduction_response_declined_received,
|
||||
contactName, ir.getName());
|
||||
} else {
|
||||
return ctx.getString(
|
||||
R.string.introduction_response_declined_received_by_introducee,
|
||||
contactName, ir.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unhandled Conversation Message");
|
||||
}
|
||||
}
|
||||
|
||||
private void bindInvitation(InvitationHolder ui, int position) {
|
||||
|
||||
ConversationForumInvitationItem item =
|
||||
(ConversationForumInvitationItem) getItem(position);
|
||||
ForumInvitationMessage fim = item.getForumInvitationMessage();
|
||||
|
||||
String message = fim.getMessage();
|
||||
@@ -291,11 +358,10 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
// Outgoing Invitation
|
||||
if (item instanceof ConversationForumInvitationOutItem) {
|
||||
if (item instanceof OutgoingItem) {
|
||||
ui.text.setText(ctx.getString(R.string.forum_invitation_sent,
|
||||
fim.getForumName(), contactName));
|
||||
ConversationForumInvitationOutItem i =
|
||||
(ConversationForumInvitationOutItem) item;
|
||||
OutgoingItem i = (OutgoingItem) item;
|
||||
if (i.isSeen()) {
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
ui.message.status.setImageResource(
|
||||
@@ -338,6 +404,23 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
ConversationItem m = getItem(position);
|
||||
if (m == null) {
|
||||
return NO_ID;
|
||||
}
|
||||
byte[] b = m.getId().getBytes();
|
||||
// Technically this could result in collisions because hashCode is not
|
||||
// guaranteed to be collision-resistant. We could instead use BLAKE2s
|
||||
// with hash output set to 8 bytes, and then convert that to a long.
|
||||
long id = Arrays.hashCode(Arrays.copyOf(b, UniqueId.LENGTH / 2));
|
||||
id <<= 32;
|
||||
id |= Arrays.hashCode(
|
||||
Arrays.copyOfRange(b, UniqueId.LENGTH / 2, UniqueId.LENGTH));
|
||||
return id;
|
||||
}
|
||||
|
||||
public ConversationItem getItem(int position) {
|
||||
if (position == INVALID_POSITION || items.size() <= position) {
|
||||
return null; // Not found
|
||||
@@ -354,7 +437,8 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
public SparseArray<IncomingItem> getIncomingMessages() {
|
||||
SparseArray<IncomingItem> messages = new SparseArray<>();
|
||||
SparseArray<IncomingItem> messages =
|
||||
new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
@@ -366,7 +450,8 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
|
||||
public SparseArray<OutgoingItem> getOutgoingMessages() {
|
||||
SparseArray<OutgoingItem> messages = new SparseArray<>();
|
||||
SparseArray<OutgoingItem> messages =
|
||||
new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
@@ -377,18 +462,6 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public SparseArray<ConversationMessageItem> getPrivateMessages() {
|
||||
SparseArray<ConversationMessageItem> messages = new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
if (item instanceof ConversationMessageItem) {
|
||||
messages.put(i, (ConversationMessageItem) item);
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void add(final ConversationItem message) {
|
||||
this.items.add(message);
|
||||
}
|
||||
@@ -408,7 +481,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
public TextView date;
|
||||
public ImageView status;
|
||||
|
||||
public MessageHolder(View v, int type) {
|
||||
public MessageHolder(View v, boolean outgoing) {
|
||||
super(v);
|
||||
|
||||
layout = (ViewGroup) v.findViewById(R.id.msgLayout);
|
||||
@@ -416,7 +489,7 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
date = (TextView) v.findViewById(R.id.msgTime);
|
||||
|
||||
// outgoing message (local)
|
||||
if (type == MSG_OUT) {
|
||||
if (outgoing) {
|
||||
status = (ImageView) v.findViewById(R.id.msgStatus);
|
||||
}
|
||||
}
|
||||
@@ -432,18 +505,17 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
public IntroductionHolder(View v, int type) {
|
||||
public IntroductionHolder(View v, boolean outgoing) {
|
||||
super(v);
|
||||
|
||||
messageLayout = v.findViewById(R.id.messageLayout);
|
||||
message = new MessageHolder(messageLayout,
|
||||
type == INTRODUCTION_IN ? MSG_IN : MSG_OUT);
|
||||
message = new MessageHolder(messageLayout, outgoing);
|
||||
text = (TextView) v.findViewById(R.id.introductionText);
|
||||
acceptButton = (Button) v.findViewById(R.id.acceptButton);
|
||||
declineButton = (Button) v.findViewById(R.id.declineButton);
|
||||
date = (TextView) v.findViewById(R.id.introductionTime);
|
||||
|
||||
if (type == INTRODUCTION_OUT) {
|
||||
if (outgoing) {
|
||||
status = (ImageView) v.findViewById(R.id.introductionStatus);
|
||||
} else {
|
||||
status = null;
|
||||
@@ -457,13 +529,13 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
public NoticeHolder(View v, int type) {
|
||||
public NoticeHolder(View v, boolean outgoing) {
|
||||
super(v);
|
||||
|
||||
text = (TextView) v.findViewById(R.id.noticeText);
|
||||
date = (TextView) v.findViewById(R.id.noticeTime);
|
||||
|
||||
if (type == NOTICE_OUT) {
|
||||
if (outgoing) {
|
||||
status = (ImageView) v.findViewById(R.id.noticeStatus);
|
||||
} else {
|
||||
status = null;
|
||||
@@ -480,17 +552,16 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
public InvitationHolder(View v, int type) {
|
||||
public InvitationHolder(View v, boolean outgoing) {
|
||||
super(v);
|
||||
|
||||
messageLayout = v.findViewById(R.id.messageLayout);
|
||||
message = new MessageHolder(messageLayout,
|
||||
type == FORUM_INVITATION_IN ? MSG_IN : MSG_OUT);
|
||||
message = new MessageHolder(messageLayout, outgoing);
|
||||
text = (TextView) v.findViewById(R.id.introductionText);
|
||||
showForumsButton = (Button) v.findViewById(R.id.showForumsButton);
|
||||
date = (TextView) v.findViewById(R.id.introductionTime);
|
||||
|
||||
if (type == FORUM_INVITATION_OUT) {
|
||||
if (outgoing) {
|
||||
status = (ImageView) v.findViewById(R.id.introductionStatus);
|
||||
} else {
|
||||
status = null;
|
||||
@@ -498,7 +569,8 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
private class ListCallbacks extends SortedList.Callback<ConversationItem> {
|
||||
private class ListCallbacks
|
||||
extends SortedList.Callback<ConversationItem> {
|
||||
|
||||
@Override
|
||||
public void onInserted(int position, int count) {
|
||||
@@ -543,7 +615,11 @@ class ConversationAdapter extends RecyclerView.Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
public interface IntroductionHandler {
|
||||
void respondToIntroduction(SessionId sessionId, boolean accept);
|
||||
public interface ConversationHandler {
|
||||
void respondToItem(ConversationItem item, boolean accept);
|
||||
}
|
||||
|
||||
public interface MessageUpdatedHandler {
|
||||
void messageUpdated(int position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationForumInvitationInItem
|
||||
extends ConversationForumInvitationItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
public ConversationForumInvitationInItem(ForumInvitationMessage fim) {
|
||||
super(fim);
|
||||
|
||||
this.read = fim.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return FORUM_INVITATION_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
|
||||
abstract class ConversationForumInvitationItem extends ConversationItem {
|
||||
|
||||
private final ForumInvitationMessage fim;
|
||||
|
||||
public ConversationForumInvitationItem(ForumInvitationMessage fim) {
|
||||
super(fim.getId(), fim.getTimestamp());
|
||||
|
||||
this.fim = fim;
|
||||
}
|
||||
|
||||
public ForumInvitationMessage getForumInvitationMessage() {
|
||||
return fim;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
|
||||
/**
|
||||
* This class is needed and can not be replaced by an ConversationNoticeOutItem,
|
||||
* because it carries the optional invitation message
|
||||
* to be displayed as a regular private message.
|
||||
* <p/>
|
||||
* This class is not thread-safe
|
||||
*/
|
||||
public class ConversationForumInvitationOutItem
|
||||
extends ConversationForumInvitationItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
public ConversationForumInvitationOutItem(ForumInvitationMessage fim) {
|
||||
super(fim);
|
||||
this.sent = fim.isSent();
|
||||
this.seen = fim.isSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return FORUM_INVITATION_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationIntroductionInItem extends ConversationIntroductionItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
public ConversationIntroductionInItem(IntroductionRequest ir) {
|
||||
super(ir);
|
||||
|
||||
this.read = ir.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return INTRODUCTION_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
// This class is not thread-safe
|
||||
abstract class ConversationIntroductionItem extends ConversationItem {
|
||||
|
||||
private final IntroductionRequest ir;
|
||||
private boolean answered;
|
||||
|
||||
public ConversationIntroductionItem(IntroductionRequest ir) {
|
||||
super(ir.getMessageId(), ir.getTimestamp());
|
||||
|
||||
this.ir = ir;
|
||||
this.answered = ir.wasAnswered();
|
||||
}
|
||||
|
||||
public IntroductionRequest getIntroductionRequest() {
|
||||
return ir;
|
||||
}
|
||||
|
||||
public boolean wasAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
public void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
/**
|
||||
* This class is needed and can not be replaced by an ConversationNoticeOutItem,
|
||||
* because it carries the optional introduction message
|
||||
* to be displayed as a regular private message.
|
||||
*
|
||||
* This class is not thread-safe
|
||||
*/
|
||||
public class ConversationIntroductionOutItem
|
||||
extends ConversationIntroductionItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
public ConversationIntroductionOutItem(IntroductionRequest ir) {
|
||||
super(ir);
|
||||
this.sent = ir.isSent();
|
||||
this.seen = ir.isSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return INTRODUCTION_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
// This class is not thread-safe
|
||||
public abstract class ConversationItem {
|
||||
|
||||
// this is needed for RecyclerView adapter which requires an int type
|
||||
final static int MSG_IN = 0;
|
||||
final static int MSG_IN_UNREAD = 1;
|
||||
final static int MSG_OUT = 2;
|
||||
final static int INTRODUCTION_IN = 3;
|
||||
final static int INTRODUCTION_OUT = 4;
|
||||
final static int NOTICE_IN = 5;
|
||||
final static int NOTICE_OUT = 6;
|
||||
final static int FORUM_INVITATION_IN = 7;
|
||||
final static int FORUM_INVITATION_OUT = 8;
|
||||
|
||||
private MessageId id;
|
||||
private long time;
|
||||
|
||||
public ConversationItem(MessageId id, long time) {
|
||||
this.id = id;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
abstract int getType();
|
||||
|
||||
public MessageId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public static ConversationItem from(PrivateMessageHeader h) {
|
||||
if (h.isLocal())
|
||||
return new ConversationMessageOutItem(h);
|
||||
else
|
||||
return new ConversationMessageInItem(h);
|
||||
}
|
||||
|
||||
public static ConversationItem from(IntroductionRequest ir) {
|
||||
if (ir.isLocal()) {
|
||||
return new ConversationIntroductionOutItem(ir);
|
||||
} else {
|
||||
return new ConversationIntroductionInItem(ir);
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationItem from(Context ctx, String contactName,
|
||||
IntroductionResponse ir) {
|
||||
|
||||
if (ir.isLocal()) {
|
||||
String text;
|
||||
if (ir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_accepted_sent,
|
||||
ir.getName());
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_declined_sent,
|
||||
ir.getName());
|
||||
}
|
||||
return new ConversationNoticeOutItem(ir.getMessageId(), text,
|
||||
ir.getTimestamp(), ir.isSent(), ir.isSeen());
|
||||
} else {
|
||||
String text;
|
||||
if (ir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_accepted_received,
|
||||
contactName, ir.getName());
|
||||
} else {
|
||||
if (ir.isIntroducer()) {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_declined_received,
|
||||
contactName, ir.getName());
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_declined_received_by_introducee,
|
||||
contactName, ir.getName());
|
||||
}
|
||||
}
|
||||
return new ConversationNoticeInItem(ir.getMessageId(), text,
|
||||
ir.getTimestamp(), ir.isRead());
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationItem from(ForumInvitationMessage fim) {
|
||||
if (fim.isLocal()) {
|
||||
return new ConversationForumInvitationOutItem(fim);
|
||||
} else {
|
||||
return new ConversationForumInvitationInItem(fim);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should not be used to get user-facing objects,
|
||||
* Its purpose is to provider data for the contact list.
|
||||
*/
|
||||
public static ConversationItem from(IntroductionMessage im) {
|
||||
if (im.isLocal())
|
||||
return new ConversationNoticeOutItem(im.getMessageId(), "",
|
||||
im.getTimestamp(), false, false);
|
||||
return new ConversationNoticeInItem(im.getMessageId(), "",
|
||||
im.getTimestamp(), im.isRead());
|
||||
}
|
||||
|
||||
protected interface OutgoingItem {
|
||||
|
||||
MessageId getId();
|
||||
|
||||
boolean isSent();
|
||||
|
||||
void setSent(boolean sent);
|
||||
|
||||
boolean isSeen();
|
||||
|
||||
void setSeen(boolean seen);
|
||||
}
|
||||
|
||||
protected interface IncomingItem {
|
||||
|
||||
MessageId getId();
|
||||
|
||||
boolean isRead();
|
||||
|
||||
void setRead(boolean read);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationMessageInItem extends ConversationMessageItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
public ConversationMessageInItem(PrivateMessageHeader header) {
|
||||
super(header);
|
||||
|
||||
read = header.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return MSG_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
abstract class ConversationMessageItem extends ConversationItem {
|
||||
|
||||
private final PrivateMessageHeader header;
|
||||
private byte[] body;
|
||||
|
||||
public ConversationMessageItem(PrivateMessageHeader header) {
|
||||
super(header.getId(), header.getTimestamp());
|
||||
|
||||
this.header = header;
|
||||
body = null;
|
||||
}
|
||||
|
||||
PrivateMessageHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
void setBody(byte[] body) {
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationMessageOutItem extends ConversationMessageItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
public ConversationMessageOutItem(PrivateMessageHeader header) {
|
||||
super(header);
|
||||
|
||||
sent = header.isSent();
|
||||
seen = header.isSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return MSG_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationNoticeInItem extends ConversationNoticeItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
public ConversationNoticeInItem(MessageId id, String text, long time,
|
||||
boolean read) {
|
||||
super(id, text, time);
|
||||
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return NOTICE_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
abstract class ConversationNoticeItem extends ConversationItem {
|
||||
|
||||
private final String text;
|
||||
|
||||
public ConversationNoticeItem(MessageId id, String text, long time) {
|
||||
super(id, time);
|
||||
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package org.briarproject.android.forum;
|
||||
|
||||
import org.briarproject.android.contact.ContactListItem;
|
||||
import org.briarproject.android.contact.ConversationItem;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
|
||||
@@ -15,20 +15,17 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.contact.ContactListAdapter;
|
||||
import org.briarproject.android.contact.ContactListItem;
|
||||
import org.briarproject.android.contact.ConversationItem;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.util.BriarRecyclerView;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.conversation.ConversationManager;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.plugins.ConnectionRegistry;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
@@ -61,9 +58,7 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
@Inject
|
||||
protected volatile IdentityManager identityManager;
|
||||
@Inject
|
||||
protected volatile MessagingManager messagingManager;
|
||||
@Inject
|
||||
protected volatile IntroductionManager introductionManager;
|
||||
protected volatile ConversationManager conversationManager;
|
||||
@Inject
|
||||
protected volatile ConnectionRegistry connectionRegistry;
|
||||
|
||||
@@ -166,7 +161,7 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
} else {
|
||||
ContactId id = c.getId();
|
||||
GroupId groupId =
|
||||
messagingManager.getConversationId(id);
|
||||
conversationManager.getConversationId(id);
|
||||
Collection<ConversationItem> messages =
|
||||
getMessages(id);
|
||||
boolean connected =
|
||||
@@ -227,28 +222,12 @@ public class ContactChooserFragment extends BaseFragment {
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
Collection<ConversationItem> messages = new ArrayList<>();
|
||||
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
messagingManager.getMessageHeaders(id);
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
messages.add(ConversationItem.from(h));
|
||||
}
|
||||
Collection<ConversationItem> messages =
|
||||
conversationManager.getMessages(id, false);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading message headers took " + duration + " ms");
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
Collection<IntroductionMessage> introductions =
|
||||
introductionManager
|
||||
.getIntroductionMessages(id);
|
||||
for (IntroductionMessage m : introductions) {
|
||||
messages.add(ConversationItem.from(m));
|
||||
}
|
||||
duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading introduction messages took " + duration + " ms");
|
||||
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
|
||||
public interface ConversationForumInvitationItem extends ConversationItem {
|
||||
|
||||
ForumInvitationMessage getForumInvitationMessage();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
public interface ConversationIntroductionRequestItem extends ConversationItem {
|
||||
|
||||
IntroductionRequest getIntroductionRequest();
|
||||
|
||||
boolean wasAnswered();
|
||||
|
||||
void setAnswered(boolean answered);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
|
||||
public interface ConversationIntroductionResponseItem extends ConversationItem {
|
||||
|
||||
IntroductionResponse getIntroductionResponse();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public interface ConversationItem {
|
||||
|
||||
MessageId getId();
|
||||
|
||||
long getTime();
|
||||
|
||||
interface ContentListener {
|
||||
|
||||
void contentReady();
|
||||
}
|
||||
|
||||
interface Partial extends ConversationItem {
|
||||
|
||||
void setContent(byte[] content);
|
||||
|
||||
void setContentListener(ContentListener listener);
|
||||
}
|
||||
|
||||
interface IncomingItem extends ConversationItem {
|
||||
|
||||
boolean isRead();
|
||||
|
||||
void setRead(boolean read);
|
||||
}
|
||||
|
||||
interface OutgoingItem extends ConversationItem {
|
||||
|
||||
boolean isSent();
|
||||
|
||||
void setSent(boolean sent);
|
||||
|
||||
boolean isSeen();
|
||||
|
||||
void setSeen(boolean seen);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ConversationManager {
|
||||
|
||||
/**
|
||||
* Returns the unique ID of the conversation client.
|
||||
*/
|
||||
ClientId getClientId();
|
||||
|
||||
/**
|
||||
* Stores a local private message, and returns the corresponding item.
|
||||
*/
|
||||
ConversationItem addLocalMessage(PrivateMessage m, byte[] body) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the ID of the contact with the given private conversation.
|
||||
*/
|
||||
ContactId getContactId(GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the ID of the private conversation with the given contact.
|
||||
*/
|
||||
GroupId getConversationId(ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all messages in the given private conversation.
|
||||
*/
|
||||
List<ConversationItem> getMessages(ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all messages in the given private conversation.
|
||||
*/
|
||||
List<ConversationItem> getMessages(ContactId c, boolean content)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Starts a background task to load the content of the given message.
|
||||
*/
|
||||
void loadMessageContent(ConversationItem.Partial m);
|
||||
|
||||
/**
|
||||
* Respond to a conversation item with accept/decline.
|
||||
*/
|
||||
void respondToItem(ContactId c, ConversationItem item, boolean accept,
|
||||
long timestamp) throws DbException, FormatException;
|
||||
|
||||
/**
|
||||
* Marks a conversation item as read or unread.
|
||||
*/
|
||||
void setReadFlag(ConversationItem item, boolean read) throws DbException;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.briarproject.api.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationItem.Partial;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
public interface ConversationMessageItem extends Partial {
|
||||
|
||||
PrivateMessageHeader getHeader();
|
||||
|
||||
byte[] getBody();
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sharing.SharingManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -46,4 +47,7 @@ public interface ForumSharingManager extends SharingManager<Forum, ForumInvitati
|
||||
/** Returns true if the forum not already shared and no invitation is open */
|
||||
boolean canBeShared(GroupId g, Contact c) throws DbException;
|
||||
|
||||
/** Marks a forum sharing message as read or unread. */
|
||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -41,4 +42,7 @@ public interface IntroductionManager {
|
||||
Collection<IntroductionMessage> getIntroductionMessages(ContactId contactId)
|
||||
throws DbException;
|
||||
|
||||
/** Marks an introduction message as read or unread. */
|
||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -45,4 +46,7 @@ public interface SharingManager<S extends Shareable, IM extends InvitationMessag
|
||||
/** Returns true if the group not already shared and no invitation is open */
|
||||
boolean canBeShared(GroupId g, Contact c) throws DbException;
|
||||
|
||||
/** Marks a sharing message as read or unread. */
|
||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject;
|
||||
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.conversation.ConversationModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.db.DatabaseExecutorModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
@@ -22,6 +23,8 @@ public interface CoreEagerSingletons {
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(ConversationModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoModule.EagerSingletons init);
|
||||
|
||||
void inject(DatabaseExecutorModule.EagerSingletons init);
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.conversation.ConversationModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseExecutorModule;
|
||||
@@ -31,6 +32,7 @@ import dagger.Module;
|
||||
BlogsModule.class,
|
||||
ClientsModule.class,
|
||||
ContactModule.class,
|
||||
ConversationModule.class,
|
||||
CryptoModule.class,
|
||||
DataModule.class,
|
||||
DatabaseModule.class,
|
||||
@@ -58,6 +60,7 @@ public class CoreModule {
|
||||
public static void initEagerSingletons(CoreEagerSingletons c) {
|
||||
c.inject(new BlogsModule.EagerSingletons());
|
||||
c.inject(new ContactModule.EagerSingletons());
|
||||
c.inject(new ConversationModule.EagerSingletons());
|
||||
c.inject(new CryptoModule.EagerSingletons());
|
||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||
c.inject(new ForumModule.EagerSingletons());
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationForumInvitationItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
|
||||
class ConversationForumInvitationItemImpl {
|
||||
|
||||
static ConversationItem from(ForumInvitationMessage i) {
|
||||
return i.isLocal() ? new Outgoing(i) : new Incoming(i);
|
||||
}
|
||||
|
||||
static class Outgoing extends OutgoingConversationItem
|
||||
implements ConversationForumInvitationItem {
|
||||
|
||||
private final ForumInvitationMessage fim;
|
||||
|
||||
public Outgoing(ForumInvitationMessage fim) {
|
||||
super(fim.getId(), fim.getTimestamp(), fim.isSent(), fim.isSeen());
|
||||
|
||||
this.fim = fim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitationMessage getForumInvitationMessage() {
|
||||
return fim;
|
||||
}
|
||||
}
|
||||
|
||||
static class Incoming extends IncomingConversationItem
|
||||
implements ConversationForumInvitationItem {
|
||||
|
||||
private final ForumInvitationMessage fim;
|
||||
|
||||
public Incoming(ForumInvitationMessage fim) {
|
||||
super(fim.getId(), fim.getTimestamp(), fim.isRead());
|
||||
|
||||
this.fim = fim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitationMessage getForumInvitationMessage() {
|
||||
return fim;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationIntroductionRequestItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
class ConversationIntroductionRequestItemImpl {
|
||||
|
||||
static ConversationItem from(IntroductionRequest i) {
|
||||
return i.isLocal() ? new Outgoing(i) : new Incoming(i);
|
||||
}
|
||||
|
||||
static class Outgoing extends OutgoingConversationItem
|
||||
implements ConversationIntroductionRequestItem {
|
||||
|
||||
private final IntroductionRequest ir;
|
||||
private boolean answered;
|
||||
|
||||
public Outgoing(IntroductionRequest ir) {
|
||||
super(ir.getMessageId(), ir.getTimestamp(), ir.isSent(), ir.isSeen());
|
||||
|
||||
this.ir = ir;
|
||||
this.answered = ir.wasAnswered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntroductionRequest getIntroductionRequest() {
|
||||
return ir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
}
|
||||
|
||||
// This class is not thread-safe
|
||||
static class Incoming extends IncomingConversationItem
|
||||
implements ConversationIntroductionRequestItem {
|
||||
|
||||
private final IntroductionRequest ir;
|
||||
private boolean answered;
|
||||
|
||||
public Incoming(IntroductionRequest ir) {
|
||||
super(ir.getMessageId(), ir.getTimestamp(), ir.isRead());
|
||||
|
||||
this.ir = ir;
|
||||
this.answered = ir.wasAnswered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntroductionRequest getIntroductionRequest() {
|
||||
return ir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationIntroductionResponseItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
|
||||
class ConversationIntroductionResponseItemImpl {
|
||||
|
||||
static ConversationItem from(IntroductionResponse i) {
|
||||
return i.isLocal() ? new Outgoing(i) : new Incoming(i);
|
||||
}
|
||||
|
||||
static class Outgoing extends OutgoingConversationItem
|
||||
implements ConversationIntroductionResponseItem {
|
||||
|
||||
private final IntroductionResponse ir;
|
||||
|
||||
public Outgoing(IntroductionResponse ir) {
|
||||
super(ir.getMessageId(), ir.getTimestamp(), ir.isSent(), ir.isSeen());
|
||||
|
||||
this.ir = ir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntroductionResponse getIntroductionResponse() {
|
||||
return ir;
|
||||
}
|
||||
}
|
||||
|
||||
static class Incoming extends IncomingConversationItem
|
||||
implements ConversationIntroductionResponseItem {
|
||||
|
||||
private final IntroductionResponse ir;
|
||||
|
||||
public Incoming(IntroductionResponse ir) {
|
||||
super(ir.getMessageId(), ir.getTimestamp(), ir.isRead());
|
||||
|
||||
this.ir = ir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntroductionResponse getIntroductionResponse() {
|
||||
return ir;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public abstract class ConversationItemImpl implements ConversationItem {
|
||||
|
||||
private final MessageId id;
|
||||
private final long time;
|
||||
|
||||
public ConversationItemImpl(MessageId id, long time) {
|
||||
this.id = id;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public MessageId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.conversation.ConversationForumInvitationItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionRequestItem;
|
||||
import org.briarproject.api.conversation.ConversationIntroductionResponseItem;
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.conversation.ConversationItem.Partial;
|
||||
import org.briarproject.api.conversation.ConversationManager;
|
||||
import org.briarproject.api.conversation.ConversationMessageItem;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class ConversationManagerImpl implements ConversationManager {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"05313ec596871e5305181220488fc9c0"
|
||||
+ "3d44985a48a6d0fb8767da3a6cae1cb4"));
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ConversationManagerImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor;
|
||||
private final ForumSharingManager forumSharingManager;
|
||||
private final IntroductionManager introductionManager;
|
||||
private final MessagingManager messagingManager;
|
||||
|
||||
private Map<MessageId, byte[]> bodyCache =
|
||||
new ConcurrentHashMap<MessageId, byte[]>();
|
||||
|
||||
@Inject
|
||||
ConversationManagerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
ForumSharingManager forumSharingManager,
|
||||
IntroductionManager introductionManager,
|
||||
MessagingManager messagingManager) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
this.introductionManager = introductionManager;
|
||||
this.messagingManager = messagingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConversationItem addLocalMessage(PrivateMessage m, byte[] body)
|
||||
throws DbException {
|
||||
messagingManager.addLocalMessage(m);
|
||||
bodyCache.put(m.getMessage().getId(), body);
|
||||
|
||||
PrivateMessageHeader h = new PrivateMessageHeader(
|
||||
m.getMessage().getId(),
|
||||
m.getMessage().getTimestamp(), m.getContentType(),
|
||||
true, false, false, false);
|
||||
ConversationItem item = ConversationMessageItemImpl.from(h);
|
||||
((Partial) item).setContent(body);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContactId getContactId(GroupId g) throws DbException {
|
||||
return messagingManager.getContactId(g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupId getConversationId(ContactId c) throws DbException {
|
||||
return messagingManager.getConversationId(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConversationItem> getMessages(ContactId c)
|
||||
throws DbException {
|
||||
return getMessages(c, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConversationItem> getMessages(ContactId c, boolean content)
|
||||
throws DbException {
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
messagingManager.getMessageHeaders(c);
|
||||
Collection<IntroductionMessage> introductions =
|
||||
introductionManager.getIntroductionMessages(c);
|
||||
Collection<ForumInvitationMessage> invitations =
|
||||
forumSharingManager.getInvitationMessages(c);
|
||||
List<ConversationItem> items = new ArrayList<ConversationItem>();
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
ConversationItem item = ConversationMessageItemImpl.from(h);
|
||||
if (content) {
|
||||
byte[] body = bodyCache.get(h.getId());
|
||||
if (body == null) loadMessageContent((Partial) item);
|
||||
else ((Partial) item).setContent(body);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
for (IntroductionMessage m : introductions) {
|
||||
ConversationItem item;
|
||||
if (m instanceof IntroductionRequest) {
|
||||
item = ConversationIntroductionRequestItemImpl.from(
|
||||
(IntroductionRequest) m);
|
||||
} else {
|
||||
item = ConversationIntroductionResponseItemImpl.from(
|
||||
(IntroductionResponse) m);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
for (ForumInvitationMessage i : invitations) {
|
||||
ConversationItem item = ConversationForumInvitationItemImpl.from(i);
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessageContent(final Partial m) {
|
||||
dbExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
MessageId id = m.getId();
|
||||
long now = System.currentTimeMillis();
|
||||
byte[] body = messagingManager.getMessageBody(id);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading message took " + duration + " ms");
|
||||
bodyCache.put(id, body);
|
||||
m.setContent(body);
|
||||
} catch (NoSuchMessageException e) {
|
||||
// The item will be removed when we get the event
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToItem(ContactId c, ConversationItem item,
|
||||
boolean accept, long timestamp)
|
||||
throws DbException, FormatException {
|
||||
if (item instanceof ConversationIntroductionRequestItem) {
|
||||
SessionId sessionId = ((ConversationIntroductionRequestItem) item)
|
||||
.getIntroductionRequest().getSessionId();
|
||||
if (accept) {
|
||||
introductionManager
|
||||
.acceptIntroduction(c, sessionId,
|
||||
timestamp);
|
||||
} else {
|
||||
introductionManager
|
||||
.declineIntroduction(c, sessionId,
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadFlag(ConversationItem item, boolean read)
|
||||
throws DbException {
|
||||
MessageId id = item.getId();
|
||||
if (item instanceof ConversationMessageItem) {
|
||||
messagingManager.setReadFlag(id, read);
|
||||
} else if (item instanceof ConversationIntroductionRequestItem ||
|
||||
item instanceof ConversationIntroductionResponseItem) {
|
||||
introductionManager.setReadFlag(id, read);
|
||||
} else if (item instanceof ConversationForumInvitationItem) {
|
||||
forumSharingManager.setReadFlag(id, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationItem;
|
||||
import org.briarproject.api.conversation.ConversationMessageItem;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
class ConversationMessageItemImpl {
|
||||
|
||||
static ConversationItem from(PrivateMessageHeader h) {
|
||||
return h.isLocal() ? new Outgoing(h) : new Incoming(h);
|
||||
}
|
||||
|
||||
static class Outgoing extends OutgoingConversationItem
|
||||
implements ConversationMessageItem {
|
||||
|
||||
private final PrivateMessageHeader header;
|
||||
private byte[] body;
|
||||
private ContentListener listener;
|
||||
private final ReentrantReadWriteLock bodyLock =
|
||||
new ReentrantReadWriteLock();
|
||||
private final ReentrantReadWriteLock listenerLock =
|
||||
new ReentrantReadWriteLock();
|
||||
|
||||
public Outgoing(PrivateMessageHeader header) {
|
||||
super(header.getId(), header.getTimestamp(), header.isSent(),
|
||||
header.isSeen());
|
||||
|
||||
this.header = header;
|
||||
body = null;
|
||||
listener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateMessageHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
bodyLock.readLock().lock();
|
||||
byte[] ret = body;
|
||||
bodyLock.readLock().unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(byte[] content) {
|
||||
bodyLock.writeLock().lock();
|
||||
this.body = content;
|
||||
bodyLock.writeLock().unlock();
|
||||
listenerLock.readLock().lock();
|
||||
if (listener != null) {
|
||||
listener.contentReady();
|
||||
}
|
||||
listenerLock.readLock().unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentListener(
|
||||
ContentListener listener) {
|
||||
listenerLock.writeLock().lock();
|
||||
this.listener = listener;
|
||||
listenerLock.writeLock().unlock();
|
||||
bodyLock.readLock().lock();
|
||||
if (body != null) {
|
||||
listener.contentReady();
|
||||
}
|
||||
bodyLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static class Incoming extends IncomingConversationItem
|
||||
implements ConversationMessageItem {
|
||||
|
||||
private final PrivateMessageHeader header;
|
||||
private byte[] body;
|
||||
private ContentListener listener;
|
||||
private final ReentrantReadWriteLock bodyLock =
|
||||
new ReentrantReadWriteLock();
|
||||
private final ReentrantReadWriteLock listenerLock =
|
||||
new ReentrantReadWriteLock();
|
||||
|
||||
public Incoming(PrivateMessageHeader header) {
|
||||
super(header.getId(), header.getTimestamp(), header.isRead());
|
||||
|
||||
this.header = header;
|
||||
body = null;
|
||||
listener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateMessageHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getBody() {
|
||||
bodyLock.readLock().lock();
|
||||
byte[] ret = body;
|
||||
bodyLock.readLock().unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContent(byte[] content) {
|
||||
bodyLock.writeLock().lock();
|
||||
this.body = content;
|
||||
bodyLock.writeLock().unlock();
|
||||
listenerLock.readLock().lock();
|
||||
if (listener != null) {
|
||||
listener.contentReady();
|
||||
}
|
||||
listenerLock.readLock().unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentListener(
|
||||
ContentListener listener) {
|
||||
listenerLock.writeLock().lock();
|
||||
this.listener = listener;
|
||||
listenerLock.writeLock().unlock();
|
||||
bodyLock.readLock().lock();
|
||||
if (body != null) {
|
||||
listener.contentReady();
|
||||
}
|
||||
bodyLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class ConversationModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
ConversationManager conversationManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ConversationManager getConversationManager(
|
||||
ConversationManagerImpl conversationManager) {
|
||||
return conversationManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationItem.IncomingItem;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
// This class is not thread-safe
|
||||
class IncomingConversationItem extends ConversationItemImpl
|
||||
implements IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
public IncomingConversationItem(MessageId id, long time, boolean read) {
|
||||
super(id, time);
|
||||
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
package org.briarproject.android.contact;
|
||||
package org.briarproject.conversation;
|
||||
|
||||
import org.briarproject.api.conversation.ConversationItem.OutgoingItem;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class ConversationNoticeOutItem extends ConversationNoticeItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
class OutgoingConversationItem extends ConversationItemImpl
|
||||
implements OutgoingItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
public ConversationNoticeOutItem(MessageId id, String text, long time,
|
||||
boolean sent, boolean seen) {
|
||||
super(id, text, time);
|
||||
public OutgoingConversationItem(MessageId id, long time, boolean sent,
|
||||
boolean seen) {
|
||||
super(id, time);
|
||||
|
||||
this.sent = sent;
|
||||
this.seen = seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return NOTICE_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@@ -457,6 +457,17 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
||||
try {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(READ, read);
|
||||
clientHelper.mergeMessageMetadata(m, meta);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getNameForIntroducer(ContactId contactId,
|
||||
BdfDictionary state) throws FormatException {
|
||||
|
||||
|
||||
@@ -492,6 +492,17 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
||||
try {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(READ, read);
|
||||
clientHelper.mergeMessageMetadata(m, meta);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
void removingShareable(Transaction txn, S f) throws DbException {
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
|
||||
Reference in New Issue
Block a user