Merge branch '373-slow-contact-list' into 'master'

Use new group metadata for showing lists

What was supposed to be a minimal change turned into a rather large MR. I did my best to keep things in separate commits, so I can still split this into smaller MRs if desired.

While making use of the new group metadata in the contact and forum list, I noticed some other things in need of improvement to get rid of needing to load all messages:
* Refactor `SharingManager` so its events provide message headers that can be used to update list items
* Add `GroupId` to conversation items, so the metadata of the respective group can be updated as well when marking the items read
* Create a very basic `ConversationManager` so the GroupCount for the various clients can be queried in one go without needing to know all their groups per contact
* Fix a nasty bug that caused forum and blog invitation to not update their read state
* Fix some bugs related to displaying the forum list with proper unread count

Some casual measurements with just a few contacts and messages showed a reduction of the contact list load time by one third.

See merge request !343
This commit is contained in:
akwizgran
2016-10-10 14:00:36 +00:00
80 changed files with 856 additions and 639 deletions

View File

@@ -17,6 +17,7 @@ import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule; import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule; import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
@@ -46,7 +47,8 @@ import dagger.Component;
SharingModule.class, SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
MessagingModule.class
}) })
interface BlogSharingIntegrationTestComponent { interface BlogSharingIntegrationTestComponent {

View File

@@ -17,6 +17,7 @@ import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule; import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule; import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
@@ -46,7 +47,8 @@ import dagger.Component;
SharingModule.class, SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
MessagingModule.class
}) })
interface ForumManagerTestComponent { interface ForumManagerTestComponent {

View File

@@ -21,6 +21,7 @@ import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule; import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule; import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
@@ -50,7 +51,8 @@ import dagger.Component;
SharingModule.class, SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
MessagingModule.class
}) })
interface ForumSharingIntegrationTestComponent { interface ForumSharingIntegrationTestComponent {

View File

@@ -20,6 +20,7 @@ import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule; import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule; import org.briarproject.system.SystemModule;
@@ -46,7 +47,8 @@ import dagger.Component;
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
DataModule.class, DataModule.class,
PropertiesModule.class PropertiesModule.class,
MessagingModule.class
}) })
public interface IntroductionIntegrationTestComponent { public interface IntroductionIntegrationTestComponent {

View File

@@ -30,6 +30,7 @@ import org.briarproject.api.keyagreement.PayloadEncoder;
import org.briarproject.api.keyagreement.PayloadParser; import org.briarproject.api.keyagreement.PayloadParser;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.ConnectionRegistry;
@@ -86,6 +87,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
ContactManager contactManager(); ContactManager contactManager();
ConversationManager conversationManager();
MessagingManager messagingManager(); MessagingManager messagingManager();
PrivateMessageFactory privateMessageFactory(); PrivateMessageFactory privateMessageFactory();

View File

@@ -28,7 +28,7 @@ import org.briarproject.api.event.ForumPostReceivedEvent;
import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.IntroductionSucceededEvent; import org.briarproject.api.event.IntroductionSucceededEvent;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
@@ -235,8 +235,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} else if (e instanceof IntroductionResponseReceivedEvent) { } else if (e instanceof IntroductionResponseReceivedEvent) {
ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId(); ContactId c = ((IntroductionResponseReceivedEvent) e).getContactId();
showNotificationForPrivateConversation(c); showNotificationForPrivateConversation(c);
} else if (e instanceof InvitationReceivedEvent) { } else if (e instanceof InvitationRequestReceivedEvent) {
ContactId c = ((InvitationReceivedEvent) e).getContactId(); ContactId c = ((InvitationRequestReceivedEvent) e).getContactId();
showNotificationForPrivateConversation(c); showNotificationForPrivateConversation(c);
} else if (e instanceof InvitationResponseReceivedEvent) { } else if (e instanceof InvitationResponseReceivedEvent) {
ContactId c = ((InvitationResponseReceivedEvent) e).getContactId(); ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();

View File

@@ -50,10 +50,8 @@ public abstract class BaseContactListAdapter<VH extends BaseContactListAdapter.B
} }
}); });
if (item.getGroupId() != null) { ViewCompat.setTransitionName(ui.avatar, "avatar" +
ViewCompat.setTransitionName(ui.avatar, "avatar" + StringUtils.toHexString(item.getGroupId().getBytes()));
StringUtils.toHexString(item.getGroupId().getBytes()));
}
} }
@Override @Override
@@ -96,8 +94,9 @@ public abstract class BaseContactListAdapter<VH extends BaseContactListAdapter.B
int count = getItemCount(); int count = getItemCount();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
ContactListItem item = getItemAt(i); ContactListItem item = getItemAt(i);
if (item != null && item.getGroupId().equals(g)) if (item != null && item.getGroupId().equals(g)) {
return i; return i;
}
} }
return INVALID_POSITION; // Not found return INVALID_POSITION; // Not found
} }

View File

@@ -21,13 +21,12 @@ import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.keyagreement.KeyAgreementActivity; import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchContactException; import org.briarproject.api.db.NoSuchContactException;
import org.briarproject.api.event.ContactAddedEvent;
import org.briarproject.api.event.ContactConnectedEvent; import org.briarproject.api.event.ContactConnectedEvent;
import org.briarproject.api.event.ContactDisconnectedEvent; import org.briarproject.api.event.ContactDisconnectedEvent;
import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.ContactRemovedEvent;
@@ -37,24 +36,21 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener; import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; 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.IntroductionRequest;
import org.briarproject.api.introduction.IntroductionResponse; import org.briarproject.api.introduction.IntroductionResponse;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -88,13 +84,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
@Inject @Inject
protected volatile IdentityManager identityManager; protected volatile IdentityManager identityManager;
@Inject @Inject
protected volatile MessagingManager messagingManager; protected volatile ConversationManager conversationManager;
@Inject
protected volatile IntroductionManager introductionManager;
@Inject
protected volatile ForumSharingManager forumSharingManager;
@Inject
protected volatile BlogSharingManager blogSharingManager;
public static ContactListFragment newInstance() { public static ContactListFragment newInstance() {
@@ -130,7 +120,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
new ContactListAdapter.OnItemClickListener() { new ContactListAdapter.OnItemClickListener() {
@Override @Override
public void onItemClick(View view, ContactListItem item) { public void onItemClick(View view, ContactListItem item) {
GroupId groupId = item.getGroupId(); GroupId groupId = item.getGroupId();
Intent i = new Intent(getActivity(), Intent i = new Intent(getActivity(),
ConversationActivity.class); ConversationActivity.class);
@@ -215,15 +204,15 @@ public class ContactListFragment extends BaseFragment implements EventListener {
try { try {
ContactId id = c.getId(); ContactId id = c.getId();
GroupId groupId = GroupId groupId =
messagingManager.getConversationId(id); conversationManager.getConversationId(id);
Collection<ConversationItem> messages = GroupCount count =
getMessages(id); conversationManager.getGroupCount(id);
boolean connected = boolean connected =
connectionRegistry.isConnected(c.getId()); connectionRegistry.isConnected(c.getId());
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
contacts.add(new ContactListItem(c, localAuthor, contacts.add(new ContactListItem(c, localAuthor,
connected, groupId, messages)); connected, groupId, count));
} catch (NoSuchContactException e) { } catch (NoSuchContactException e) {
// Continue // Continue
} }
@@ -252,14 +241,9 @@ public class ContactListFragment extends BaseFragment implements EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) { if (e instanceof ContactStatusChangedEvent) {
if (((ContactAddedEvent) e).isActive()) {
LOG.info("Contact added as active, reloading");
loadContacts();
}
} else if (e instanceof ContactStatusChangedEvent) {
LOG.info("Contact Status changed, reloading"); LOG.info("Contact Status changed, reloading");
// TODO We can update the contact state without needing to reload // is also broadcast when contact was added
loadContacts(); loadContacts();
} else if (e instanceof ContactConnectedEvent) { } else if (e instanceof ContactConnectedEvent) {
setConnected(((ContactConnectedEvent) e).getContactId(), true); setConnected(((ContactConnectedEvent) e).getContactId(), true);
@@ -285,50 +269,20 @@ public class ContactListFragment extends BaseFragment implements EventListener {
(IntroductionResponseReceivedEvent) e; (IntroductionResponseReceivedEvent) e;
IntroductionResponse ir = m.getIntroductionResponse(); IntroductionResponse ir = m.getIntroductionResponse();
updateItem(m.getContactId(), ConversationItem.from(ir)); updateItem(m.getContactId(), ConversationItem.from(ir));
} else if (e instanceof InvitationReceivedEvent) { } else if (e instanceof InvitationRequestReceivedEvent) {
LOG.info("Invitation received, reloading conversation..."); LOG.info("Invitation Request received, update contact");
InvitationReceivedEvent m = (InvitationReceivedEvent) e; InvitationRequestReceivedEvent m = (InvitationRequestReceivedEvent) e;
reloadConversation(m.getContactId()); InvitationRequest ir = m.getRequest();
updateItem(m.getContactId(), ConversationItem.from(ir));
} else if (e instanceof InvitationResponseReceivedEvent) { } else if (e instanceof InvitationResponseReceivedEvent) {
LOG.info("Invitation Response received, reloading ..."); LOG.info("Invitation Response received, update contact");
InvitationResponseReceivedEvent m = InvitationResponseReceivedEvent m =
(InvitationResponseReceivedEvent) e; (InvitationResponseReceivedEvent) e;
reloadConversation(m.getContactId()); InvitationResponse ir = m.getResponse();
updateItem(m.getContactId(), ConversationItem.from(ir));
} }
} }
private void reloadConversation(final ContactId c) {
listener.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Collection<ConversationItem> messages = getMessages(c);
updateItem(c, messages);
} catch (NoSuchContactException e) {
LOG.info("Contact removed");
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void updateItem(final ContactId c,
final Collection<ConversationItem> messages) {
listener.runOnUiThread(new Runnable() {
@Override
public void run() {
int position = adapter.findItemPosition(c);
ContactListItem item = adapter.getItemAt(position);
if (item != null) {
item.setMessages(messages);
adapter.updateItemAt(position, item);
}
}
});
}
private void updateItem(final ContactId c, final ConversationItem m) { private void updateItem(final ContactId c, final ConversationItem m) {
listener.runOnUiThread(new Runnable() { listener.runOnUiThread(new Runnable() {
@Override @Override
@@ -382,50 +336,4 @@ public class ContactListFragment extends BaseFragment implements EventListener {
}); });
} }
// This needs to be called from the DB thread
// Do not call getActivty() here as it might return null
private Collection<ConversationItem> getMessages(ContactId id)
throws DbException {
long now = System.currentTimeMillis();
Collection<ConversationItem> messages = new ArrayList<>();
Collection<PrivateMessageHeader> headers =
messagingManager.getMessageHeaders(id);
for (PrivateMessageHeader h : headers) {
messages.add(ConversationItem.from(h));
}
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");
now = System.currentTimeMillis();
Collection<InvitationMessage> forumInvitations =
forumSharingManager.getInvitationMessages(id);
for (InvitationMessage i : forumInvitations) {
messages.add(ConversationItem.from(i));
}
Collection<InvitationMessage> blogInvitations =
blogSharingManager.getInvitationMessages(id);
for (InvitationMessage i : blogInvitations) {
messages.add(ConversationItem.from(i));
}
duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Loading invitations took " + duration + " ms");
return messages;
}
} }

View File

@@ -1,14 +1,14 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import static org.briarproject.android.contact.ConversationItem.IncomingItem; import static org.briarproject.android.contact.ConversationItem.IncomingItem;
// This class is not thread-safe // This class is NOT thread-safe
public class ContactListItem { public class ContactListItem {
private final Contact contact; private final Contact contact;
@@ -18,26 +18,16 @@ public class ContactListItem {
private long timestamp; private long timestamp;
private int unread; private int unread;
public ContactListItem(Contact contact, LocalAuthor localAuthor, public ContactListItem(@NotNull Contact contact,
boolean connected, @NotNull LocalAuthor localAuthor, boolean connected,
GroupId groupId, @NotNull GroupId groupId, @NotNull GroupCount count) {
Collection<ConversationItem> messages) {
this.contact = contact; this.contact = contact;
this.localAuthor = localAuthor; this.localAuthor = localAuthor;
this.groupId = groupId; this.groupId = groupId;
this.connected = connected; this.connected = connected;
setMessages(messages); this.empty = count.getMsgCount() == 0;
} this.unread = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime();
void setMessages(Collection<ConversationItem> messages) {
empty = messages == null || messages.isEmpty();
timestamp = 0;
unread = 0;
if (!empty) {
for (ConversationItem i : messages) {
addMessage(i);
}
}
} }
void addMessage(ConversationItem message) { void addMessage(ConversationItem message) {

View File

@@ -34,6 +34,7 @@ import org.briarproject.android.view.TextInputView;
import org.briarproject.android.view.TextInputView.TextInputListener; import org.briarproject.android.view.TextInputView.TextInputListener;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.BaseMessageHeader;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -50,7 +51,7 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener; import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.MessagesAckedEvent; import org.briarproject.api.event.MessagesAckedEvent;
import org.briarproject.api.event.MessagesSentEvent; import org.briarproject.api.event.MessagesSentEvent;
@@ -76,7 +77,7 @@ import org.briarproject.util.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -476,28 +477,28 @@ public class ConversationActivity extends BriarActivity
} }
private void markMessagesRead() { private void markMessagesRead() {
List<MessageId> unread = new ArrayList<>(); Map<MessageId, GroupId> unread = new HashMap<>();
SparseArray<IncomingItem> list = adapter.getIncomingMessages(); SparseArray<IncomingItem> list = adapter.getIncomingMessages();
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
IncomingItem item = list.valueAt(i); IncomingItem item = list.valueAt(i);
if (!item.isRead()) unread.add(item.getId()); if (!item.isRead())
unread.put(item.getId(), item.getGroupId());
} }
if (unread.isEmpty()) return; if (unread.isEmpty()) return;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Marking " + unread.size() + " messages read"); LOG.info("Marking " + unread.size() + " messages read");
markMessagesRead(Collections.unmodifiableList(unread)); markMessagesRead(unread);
} }
private void markMessagesRead(final Collection<MessageId> unread) { private void markMessagesRead(final Map<MessageId, GroupId> unread) {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
for (MessageId m : unread) for (Map.Entry<MessageId, GroupId> e : unread.entrySet())
// not really clean, but the messaging manager can messagingManager
// handle introduction messages as well .setReadFlag(e.getValue(), e.getKey(), true);
messagingManager.setReadFlag(groupId, m, true);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Marking read took " + duration + " ms"); LOG.info("Marking read took " + duration + " ms");
@@ -560,6 +561,7 @@ public class ConversationActivity extends BriarActivity
IntroductionRequest ir = event.getIntroductionRequest(); IntroductionRequest ir = event.getIntroductionRequest();
ConversationItem item = new ConversationIntroductionInItem(ir); ConversationItem item = new ConversationIntroductionInItem(ir);
addConversationItem(item); addConversationItem(item);
markMessageReadIfNew(ir);
} }
} else if (e instanceof IntroductionResponseReceivedEvent) { } else if (e instanceof IntroductionResponseReceivedEvent) {
IntroductionResponseReceivedEvent event = IntroductionResponseReceivedEvent event =
@@ -570,25 +572,33 @@ public class ConversationActivity extends BriarActivity
ConversationItem item = ConversationItem item =
ConversationItem.from(this, contactName, ir); ConversationItem.from(this, contactName, ir);
addConversationItem(item); addConversationItem(item);
markMessageReadIfNew(ir);
} }
} else if (e instanceof InvitationReceivedEvent) { } else if (e instanceof InvitationRequestReceivedEvent) {
InvitationReceivedEvent event = InvitationRequestReceivedEvent event =
(InvitationReceivedEvent) e; (InvitationRequestReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Invitation received, reloading..."); LOG.info("Invitation received, adding...");
loadMessages(); InvitationRequest ir = event.getRequest();
ConversationItem item = ConversationItem.from(ir);
addConversationItem(item);
markMessageReadIfNew(ir);
} }
} else if (e instanceof InvitationResponseReceivedEvent) { } else if (e instanceof InvitationResponseReceivedEvent) {
InvitationResponseReceivedEvent event = InvitationResponseReceivedEvent event =
(InvitationResponseReceivedEvent) e; (InvitationResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) { if (event.getContactId().equals(contactId)) {
LOG.info("Invitation response received, reloading..."); LOG.info("Invitation response received, adding...");
loadMessages(); InvitationResponse ir = event.getResponse();
ConversationItem item =
ConversationItem.from(this, contactName, ir);
addConversationItem(item);
markMessageReadIfNew(ir);
} }
} }
} }
private void markMessageReadIfNew(final PrivateMessageHeader h) { private void markMessageReadIfNew(final BaseMessageHeader h) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -597,22 +607,23 @@ public class ConversationActivity extends BriarActivity
// Mark the message read if it's the newest message // Mark the message read if it's the newest message
long lastMsgTime = item.getTime(); long lastMsgTime = item.getTime();
long newMsgTime = h.getTimestamp(); long newMsgTime = h.getTimestamp();
if (newMsgTime > lastMsgTime) markNewMessageRead(h.getId()); if (newMsgTime > lastMsgTime)
markNewMessageRead(h.getGroupId(), h.getId());
else loadMessages(); else loadMessages();
} else { } else {
// mark the message as read as well if it is the first one // mark the message as read as well if it is the first one
markNewMessageRead(h.getId()); markNewMessageRead(h.getGroupId(), h.getId());
} }
} }
}); });
} }
private void markNewMessageRead(final MessageId m) { private void markNewMessageRead(final GroupId g, final MessageId m) {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
messagingManager.setReadFlag(groupId, m, true); messagingManager.setReadFlag(g, m, true);
loadMessages(); loadMessages();
} catch (DbException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
@@ -681,11 +692,10 @@ public class ConversationActivity extends BriarActivity
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Storing message took " + duration + " ms"); LOG.info("Storing message took " + duration + " ms");
MessageId id = m.getMessage().getId(); MessageId id = m.getMessage().getId();
PrivateMessageHeader h = new PrivateMessageHeader(id, PrivateMessageHeader h = new PrivateMessageHeader(id,
m.getMessage().getTimestamp(), m.getContentType(), groupId, m.getMessage().getTimestamp(),
true, false, false, false); m.getContentType(), true, false, false, false);
ConversationMessageItem item = ConversationItem.from(h); ConversationMessageItem item = ConversationItem.from(h);
item.setBody(body); item.setBody(body);
bodyCache.put(id, body); bodyCache.put(id, body);

View File

@@ -1,14 +1,16 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.android.contact.ConversationItem.IncomingItem;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
import org.jetbrains.annotations.NotNull;
// This class is not thread-safe // This class is not thread-safe
class ConversationIntroductionInItem extends ConversationIntroductionItem class ConversationIntroductionInItem extends ConversationIntroductionItem
implements ConversationItem.IncomingItem { implements IncomingItem {
private boolean read; private boolean read;
ConversationIntroductionInItem(IntroductionRequest ir) { ConversationIntroductionInItem(@NotNull IntroductionRequest ir) {
super(ir); super(ir);
this.read = ir.isRead(); this.read = ir.isRead();

View File

@@ -1,6 +1,7 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
import org.jetbrains.annotations.NotNull;
// This class is not thread-safe // This class is not thread-safe
abstract class ConversationIntroductionItem extends ConversationItem { abstract class ConversationIntroductionItem extends ConversationItem {
@@ -8,13 +9,14 @@ abstract class ConversationIntroductionItem extends ConversationItem {
private final IntroductionRequest ir; private final IntroductionRequest ir;
private boolean answered; private boolean answered;
ConversationIntroductionItem(IntroductionRequest ir) { ConversationIntroductionItem(@NotNull IntroductionRequest ir) {
super(ir.getMessageId(), ir.getTimestamp()); super(ir.getMessageId(), ir.getGroupId(), ir.getTimestamp());
this.ir = ir; this.ir = ir;
this.answered = ir.wasAnswered(); this.answered = ir.wasAnswered();
} }
@NotNull
IntroductionRequest getIntroductionRequest() { IntroductionRequest getIntroductionRequest() {
return ir; return ir;
} }

View File

@@ -12,7 +12,9 @@ import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.InvitationRequest; import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sharing.InvitationResponse; import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
// This class is not thread-safe // This class is not thread-safe
public abstract class ConversationItem { public abstract class ConversationItem {
@@ -30,20 +32,29 @@ public abstract class ConversationItem {
final static int BLOG_INVITATION_IN = 9; final static int BLOG_INVITATION_IN = 9;
final static int BLOG_INVITATION_OUT = 10; final static int BLOG_INVITATION_OUT = 10;
private MessageId id; final private MessageId id;
private long time; final private GroupId groupId;
final private long time;
public ConversationItem(MessageId id, long time) { public ConversationItem(@NotNull MessageId id, @NotNull GroupId groupId,
long time) {
this.id = id; this.id = id;
this.groupId = groupId;
this.time = time; this.time = time;
} }
abstract int getType(); abstract int getType();
@NotNull
public MessageId getId() { public MessageId getId() {
return id; return id;
} }
@NotNull
public GroupId getGroupId() {
return groupId;
}
long getTime() { long getTime() {
return time; return time;
} }
@@ -78,8 +89,9 @@ public abstract class ConversationItem {
R.string.introduction_response_declined_sent, R.string.introduction_response_declined_sent,
ir.getName()); ir.getName());
} }
return new ConversationNoticeOutItem(ir.getMessageId(), text, return new ConversationNoticeOutItem(ir.getMessageId(),
ir.getTimestamp(), ir.isSent(), ir.isSeen()); ir.getGroupId(), text, ir.getTimestamp(), ir.isSent(),
ir.isSeen());
} else { } else {
String text; String text;
if (ir.wasAccepted()) { if (ir.wasAccepted()) {
@@ -97,8 +109,8 @@ public abstract class ConversationItem {
contactName, ir.getName()); contactName, ir.getName());
} }
} }
return new ConversationNoticeInItem(ir.getMessageId(), text, return new ConversationNoticeInItem(ir.getMessageId(),
ir.getTimestamp(), ir.isRead()); ir.getGroupId(), text, ir.getTimestamp(), ir.isRead());
} }
} }
@@ -137,8 +149,8 @@ public abstract class ConversationItem {
R.string.forum_invitation_response_declined_sent, R.string.forum_invitation_response_declined_sent,
contactName); contactName);
} }
return new ConversationNoticeOutItem(fir.getId(), text, return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(),
fir.getTimestamp(), fir.isSent(), fir.isSeen()); text, fir.getTimestamp(), fir.isSent(), fir.isSeen());
} else { } else {
String text; String text;
if (fir.wasAccepted()) { if (fir.wasAccepted()) {
@@ -150,8 +162,8 @@ public abstract class ConversationItem {
R.string.forum_invitation_response_declined_received, R.string.forum_invitation_response_declined_received,
contactName); contactName);
} }
return new ConversationNoticeInItem(fir.getId(), text, return new ConversationNoticeInItem(fir.getId(), fir.getGroupId(),
fir.getTimestamp(), fir.isRead()); text, fir.getTimestamp(), fir.isRead());
} }
} }
@@ -169,8 +181,8 @@ public abstract class ConversationItem {
R.string.blogs_sharing_response_declined_sent, R.string.blogs_sharing_response_declined_sent,
contactName); contactName);
} }
return new ConversationNoticeOutItem(fir.getId(), text, return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(),
fir.getTimestamp(), fir.isSent(), fir.isSeen()); text, fir.getTimestamp(), fir.isSent(), fir.isSeen());
} else { } else {
String text; String text;
if (fir.wasAccepted()) { if (fir.wasAccepted()) {
@@ -182,8 +194,8 @@ public abstract class ConversationItem {
R.string.blogs_sharing_response_declined_received, R.string.blogs_sharing_response_declined_received,
contactName); contactName);
} }
return new ConversationNoticeInItem(fir.getId(), text, return new ConversationNoticeInItem(fir.getId(), fir.getGroupId(),
fir.getTimestamp(), fir.isRead()); text, fir.getTimestamp(), fir.isRead());
} }
} }
@@ -193,10 +205,10 @@ public abstract class ConversationItem {
*/ */
public static ConversationItem from(IntroductionMessage im) { public static ConversationItem from(IntroductionMessage im) {
if (im.isLocal()) if (im.isLocal())
return new ConversationNoticeOutItem(im.getMessageId(), "", return new ConversationNoticeOutItem(im.getMessageId(),
im.getTimestamp(), false, false); im.getGroupId(), "", im.getTimestamp(), false, false);
return new ConversationNoticeInItem(im.getMessageId(), "", return new ConversationNoticeInItem(im.getMessageId(), im.getGroupId(),
im.getTimestamp(), im.isRead()); "", im.getTimestamp(), im.isRead());
} }
/** /**
@@ -205,14 +217,15 @@ public abstract class ConversationItem {
*/ */
public static ConversationItem from(InvitationMessage im) { public static ConversationItem from(InvitationMessage im) {
if (im.isLocal()) if (im.isLocal())
return new ConversationNoticeOutItem(im.getId(), "", return new ConversationNoticeOutItem(im.getId(), im.getGroupId(),
im.getTimestamp(), false, false); "", im.getTimestamp(), false, false);
return new ConversationNoticeInItem(im.getId(), "", return new ConversationNoticeInItem(im.getId(), im.getGroupId(), "",
im.getTimestamp(), im.isRead()); im.getTimestamp(), im.isRead());
} }
interface OutgoingItem { interface OutgoingItem {
@NotNull
MessageId getId(); MessageId getId();
boolean isSent(); boolean isSent();
@@ -226,8 +239,12 @@ public abstract class ConversationItem {
interface IncomingItem { interface IncomingItem {
@NotNull
MessageId getId(); MessageId getId();
@NotNull
GroupId getGroupId();
boolean isRead(); boolean isRead();
void setRead(boolean read); void setRead(boolean read);

View File

@@ -9,7 +9,7 @@ abstract class ConversationMessageItem extends ConversationItem {
private byte[] body; private byte[] body;
ConversationMessageItem(PrivateMessageHeader header) { ConversationMessageItem(PrivateMessageHeader header) {
super(header.getId(), header.getTimestamp()); super(header.getId(), header.getGroupId(), header.getTimestamp());
this.header = header; this.header = header;
body = null; body = null;

View File

@@ -1,5 +1,6 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
// This class is not thread-safe // This class is not thread-safe
@@ -8,9 +9,9 @@ class ConversationNoticeInItem extends ConversationNoticeItem
private boolean read; private boolean read;
ConversationNoticeInItem(MessageId id, String text, long time, ConversationNoticeInItem(MessageId id, GroupId groupId, String text,
boolean read) { long time, boolean read) {
super(id, text, time); super(id, groupId, text, time);
this.read = read; this.read = read;
} }

View File

@@ -1,13 +1,15 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
abstract class ConversationNoticeItem extends ConversationItem { abstract class ConversationNoticeItem extends ConversationItem {
private final String text; private final String text;
ConversationNoticeItem(MessageId id, String text, long time) { ConversationNoticeItem(MessageId id, GroupId groupId, String text,
super(id, time); long time) {
super(id, groupId, time);
this.text = text; this.text = text;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.android.contact; package org.briarproject.android.contact;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
// This class is not thread-safe // This class is not thread-safe
@@ -8,9 +9,9 @@ class ConversationNoticeOutItem extends ConversationNoticeItem
private boolean sent, seen; private boolean sent, seen;
ConversationNoticeOutItem(MessageId id, String text, long time, ConversationNoticeOutItem(MessageId id, GroupId groupId, String text,
boolean sent, boolean seen) { long time, boolean sent, boolean seen) {
super(id, text, time); super(id, groupId, text, time);
this.sent = sent; this.sent = sent;
this.seen = seen; this.seen = seen;

View File

@@ -7,7 +7,7 @@ abstract class ConversationShareableInvitationItem extends ConversationItem {
private final InvitationRequest fim; private final InvitationRequest fim;
ConversationShareableInvitationItem(InvitationRequest fim) { ConversationShareableInvitationItem(InvitationRequest fim) {
super(fim.getId(), fim.getTimestamp()); super(fim.getId(), fim.getGroupId(), fim.getTimestamp());
this.fim = fim; this.fim = fim;
} }

View File

@@ -1,27 +0,0 @@
package org.briarproject.android.forum;
import org.briarproject.api.forum.ForumPostHeader;
// This class is not thread-safe
class ForumItem {
private final ForumPostHeader header;
private byte[] body;
ForumItem(ForumPostHeader header) {
this.header = header;
body = null;
}
ForumPostHeader getHeader() {
return header;
}
byte[] getBody() {
return body;
}
void setBody(byte[] body) {
this.body = body;
}
}

View File

@@ -1,17 +0,0 @@
package org.briarproject.android.forum;
import java.util.Comparator;
class ForumItemComparator implements Comparator<ForumItem> {
static final ForumItemComparator INSTANCE = new ForumItemComparator();
public int compare(ForumItem a, ForumItem b) {
// The oldest message comes first
long aTime = a.getHeader().getTimestamp();
long bTime = b.getHeader().getTimestamp();
if (aTime < bTime) return -1;
if (aTime > bTime) return 1;
return 0;
}
}

View File

@@ -17,6 +17,7 @@ import org.briarproject.android.view.TextAvatarView;
import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.Forum;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import static android.support.v7.util.SortedList.INVALID_POSITION;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.BriarActivity.GROUP_ID;
@@ -104,7 +105,7 @@ class ForumListAdapter
@Override @Override
public boolean areContentsTheSame(ForumListItem a, ForumListItem b) { public boolean areContentsTheSame(ForumListItem a, ForumListItem b) {
return a.getForum().equals(b.getForum()) && return a.isEmpty() == b.isEmpty() &&
a.getTimestamp() == b.getTimestamp() && a.getTimestamp() == b.getTimestamp() &&
a.getUnreadCount() == b.getUnreadCount(); a.getUnreadCount() == b.getUnreadCount();
} }
@@ -125,10 +126,14 @@ class ForumListAdapter
return null; return null;
} }
void updateItem(ForumListItem item) { int findItemPosition(GroupId g) {
ForumListItem oldItem = findItem(item.getForum().getGroup().getId()); int count = getItemCount();
int position = items.indexOf(oldItem); for (int i = 0; i < count; i++) {
items.updateItemAt(position, item); ForumListItem item = getItemAt(i);
if (item != null && item.getForum().getGroup().getId().equals(g))
return i;
}
return INVALID_POSITION; // Not found
} }
static class ForumViewHolder extends RecyclerView.ViewHolder { static class ForumViewHolder extends RecyclerView.ViewHolder {

View File

@@ -20,6 +20,7 @@ import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.fragment.BaseEventFragment; import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.sharing.InvitationsForumActivity; import org.briarproject.android.sharing.InvitationsForumActivity;
import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchGroupException; import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.ContactRemovedEvent;
@@ -47,11 +48,8 @@ import static java.util.logging.Level.WARNING;
public class ForumListFragment extends BaseEventFragment implements public class ForumListFragment extends BaseEventFragment implements
OnClickListener { OnClickListener {
public final static String TAG = "ForumListFragment"; public final static String TAG = ForumListFragment.class.getName();
private final static Logger LOG = Logger.getLogger(TAG);
private static final Logger LOG =
Logger.getLogger(ForumListFragment.class.getName());
private BriarRecyclerView list; private BriarRecyclerView list;
private ForumListAdapter adapter; private ForumListAdapter adapter;
@@ -118,7 +116,7 @@ public class ForumListFragment extends BaseEventFragment implements
notificationManager.blockAllForumPostNotifications(); notificationManager.blockAllForumPostNotifications();
notificationManager.clearAllForumPostNotifications(); notificationManager.clearAllForumPostNotifications();
loadForumHeaders(); loadForums();
loadAvailableForums(); loadAvailableForums();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
} }
@@ -153,7 +151,7 @@ public class ForumListFragment extends BaseEventFragment implements
} }
} }
private void loadForumHeaders() { private void loadForums() {
listener.runOnDbThread(new Runnable() { listener.runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -163,14 +161,14 @@ public class ForumListFragment extends BaseEventFragment implements
Collection<ForumListItem> forums = new ArrayList<>(); Collection<ForumListItem> forums = new ArrayList<>();
for (Forum f : forumManager.getForums()) { for (Forum f : forumManager.getForums()) {
try { try {
Collection<ForumPostHeader> headers = GroupCount count =
forumManager.getPostHeaders(f.getId()); forumManager.getGroupCount(f.getId());
forums.add(new ForumListItem(f, headers)); forums.add(new ForumListItem(f, count));
} catch (NoSuchGroupException e) { } catch (NoSuchGroupException e) {
// Continue // Continue
} }
} }
displayForumHeaders(forums); displayForums(forums);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Full load took " + duration + " ms"); LOG.info("Full load took " + duration + " ms");
@@ -182,7 +180,7 @@ public class ForumListFragment extends BaseEventFragment implements
}); });
} }
private void displayForumHeaders(final Collection<ForumListItem> forums) { private void displayForums(final Collection<ForumListItem> forums) {
listener.runOnUiThread(new Runnable() { listener.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -238,7 +236,7 @@ public class ForumListFragment extends BaseEventFragment implements
GroupAddedEvent g = (GroupAddedEvent) e; GroupAddedEvent g = (GroupAddedEvent) e;
if (g.getGroup().getClientId().equals(forumManager.getClientId())) { if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
LOG.info("Forum added, reloading forums"); LOG.info("Forum added, reloading forums");
loadForumHeaders(); loadForums();
} }
} else if (e instanceof GroupRemovedEvent) { } else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e; GroupRemovedEvent g = (GroupRemovedEvent) e;
@@ -248,39 +246,23 @@ public class ForumListFragment extends BaseEventFragment implements
} }
} else if (e instanceof ForumPostReceivedEvent) { } else if (e instanceof ForumPostReceivedEvent) {
ForumPostReceivedEvent m = (ForumPostReceivedEvent) e; ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
LOG.info("Forum post added, reloading"); LOG.info("Forum post added, updating...");
loadForumHeaders(m.getGroupId()); updateItem(m.getGroupId(), m.getForumPostHeader());
} else if (e instanceof ForumInvitationReceivedEvent) { } else if (e instanceof ForumInvitationReceivedEvent) {
loadAvailableForums(); loadAvailableForums();
} }
} }
private void loadForumHeaders(final GroupId g) { private void updateItem(final GroupId g, final ForumPostHeader m) {
listener.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
long now = System.currentTimeMillis();
Forum f = forumManager.getForum(g);
Collection<ForumPostHeader> headers =
forumManager.getPostHeaders(g);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Partial load took " + duration + " ms");
updateForum(new ForumListItem(f, headers));
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void updateForum(final ForumListItem item) {
listener.runOnUiThread(new Runnable() { listener.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
adapter.updateItem(item); int position = adapter.findItemPosition(g);
ForumListItem item = adapter.getItemAt(position);
if (item != null) {
item.addHeader(m);
adapter.updateItemAt(position, item);
}
} }
}); });
} }
@@ -289,7 +271,8 @@ public class ForumListFragment extends BaseEventFragment implements
listener.runOnUiThread(new Runnable() { listener.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
ForumListItem item = adapter.findItem(g); int position = adapter.findItemPosition(g);
ForumListItem item = adapter.getItemAt(position);
if (item != null) adapter.remove(item); if (item != null) adapter.remove(item);
} }
}); });

View File

@@ -1,40 +1,27 @@
package org.briarproject.android.forum; package org.briarproject.android.forum;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumPostHeader; import org.briarproject.api.forum.ForumPostHeader;
import java.util.Collection; // This class is NOT thread-safe
class ForumListItem { class ForumListItem {
private final Forum forum; private final Forum forum;
private final boolean empty; private int postCount, unread;
private final int postCount; private long timestamp;
private final long timestamp;
private final int unread;
ForumListItem(Forum forum, Collection<ForumPostHeader> headers) { ForumListItem(Forum forum, GroupCount count) {
this.forum = forum; this.forum = forum;
empty = headers.isEmpty(); this.postCount = count.getMsgCount();
if (empty) { this.unread = count.getUnreadCount();
postCount = 0; this.timestamp = count.getLatestMsgTime();
timestamp = 0; }
unread = 0;
} else { void addHeader(ForumPostHeader h) {
ForumPostHeader newest = null; postCount++;
long timestamp = -1; if (!h.isRead()) unread++;
int unread = 0; if (h.getTimestamp() > timestamp) timestamp = h.getTimestamp();
for (ForumPostHeader h : headers) {
if (h.getTimestamp() > timestamp) {
timestamp = h.getTimestamp();
newest = h;
}
if (!h.isRead()) unread++;
}
this.postCount = headers.size();
this.timestamp = newest.getTimestamp();
this.unread = unread;
}
} }
Forum getForum() { Forum getForum() {
@@ -42,7 +29,7 @@ class ForumListItem {
} }
boolean isEmpty() { boolean isEmpty() {
return empty; return postCount == 0;
} }
int getPostCount() { int getPostCount() {

View File

@@ -15,9 +15,9 @@ import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contact.ContactListAdapter; import org.briarproject.android.contact.ContactListAdapter;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.ConversationItem;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
@@ -25,21 +25,16 @@ import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.introduction.IntroductionManager; import org.briarproject.api.messaging.ConversationManager;
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.plugins.ConnectionRegistry;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
public class ContactChooserFragment extends BaseFragment { public class ContactChooserFragment extends BaseFragment {
@@ -61,9 +56,7 @@ public class ContactChooserFragment extends BaseFragment {
@Inject @Inject
protected volatile IdentityManager identityManager; protected volatile IdentityManager identityManager;
@Inject @Inject
protected volatile MessagingManager messagingManager; protected volatile ConversationManager conversationManager;
@Inject
protected volatile IntroductionManager introductionManager;
@Inject @Inject
protected volatile ConnectionRegistry connectionRegistry; protected volatile ConnectionRegistry connectionRegistry;
@@ -159,23 +152,23 @@ public class ContactChooserFragment extends BaseFragment {
public void run() { public void run() {
try { try {
List<ContactListItem> contacts = new ArrayList<>(); List<ContactListItem> contacts = new ArrayList<>();
AuthorId localAuthorId = null; AuthorId localAuthorId =
identityManager.getLocalAuthor().getId();
for (Contact c : contactManager.getActiveContacts()) { for (Contact c : contactManager.getActiveContacts()) {
if (c.getId().getInt() == contactId) { if (c.getId().getInt() == contactId) {
c1 = c; c1 = c;
localAuthorId = c1.getLocalAuthorId();
} else { } else {
ContactId id = c.getId(); ContactId id = c.getId();
GroupId groupId = GroupId groupId =
messagingManager.getConversationId(id); conversationManager.getConversationId(id);
Collection<ConversationItem> messages = GroupCount count =
getMessages(id); conversationManager.getGroupCount(id);
boolean connected = boolean connected =
connectionRegistry.isConnected(c.getId()); connectionRegistry.isConnected(c.getId());
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
contacts.add(new ContactListItem(c, localAuthor, contacts.add(new ContactListItem(c, localAuthor,
connected, groupId, messages)); connected, groupId, count));
} }
} }
displayContacts(localAuthorId, contacts); displayContacts(localAuthorId, contacts);
@@ -220,36 +213,4 @@ public class ContactChooserFragment extends BaseFragment {
builder.show(); builder.show();
} }
/**
* This needs to be called from the DbThread
*/
private Collection<ConversationItem> getMessages(ContactId id)
throws DbException {
long now = System.currentTimeMillis();
Collection<ConversationItem> messages = new ArrayList<>();
Collection<PrivateMessageHeader> headers =
messagingManager.getMessageHeaders(id);
for (PrivateMessageHeader h : headers) {
messages.add(ConversationItem.from(h));
}
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;
}
} }

View File

@@ -3,6 +3,7 @@ package org.briarproject.android.sharing;
import android.content.Context; import android.content.Context;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.transition.Fade; import android.transition.Fade;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@@ -39,6 +40,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.android.sharing.ShareActivity.CONTACTS; import static org.briarproject.android.sharing.ShareActivity.CONTACTS;
import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds; import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
import static org.briarproject.android.sharing.ShareActivity.getContactsFromIntegers;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ContactSelectorFragment extends BaseFragment implements public class ContactSelectorFragment extends BaseFragment implements
@@ -122,8 +124,9 @@ public class ContactSelectorFragment extends BaseFragment implements
if (savedInstanceState != null) { if (savedInstanceState != null) {
ArrayList<Integer> intContacts = ArrayList<Integer> intContacts =
savedInstanceState.getIntegerArrayList(CONTACTS); savedInstanceState.getIntegerArrayList(CONTACTS);
selectedContacts = ShareActivity.getContactsFromIntegers( if (intContacts != null) {
intContacts); selectedContacts = getContactsFromIntegers(intContacts);
}
} }
return contentView; return contentView;
@@ -185,7 +188,7 @@ public class ContactSelectorFragment extends BaseFragment implements
updateMenuItem(); updateMenuItem();
} }
private void loadContacts(final Collection<ContactId> selection) { private void loadContacts(@Nullable final Collection<ContactId> selection) {
shareActivity.runOnDbThread(new Runnable() { shareActivity.runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@@ -1,14 +1,14 @@
package org.briarproject.android.sharing; package org.briarproject.android.sharing;
import android.support.annotation.UiThread;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.ConversationItem; import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.Collections; // This class is NOT thread-safe
// This class is not thread-safe
public class SelectableContactListItem extends ContactListItem { public class SelectableContactListItem extends ContactListItem {
private boolean selected, disabled; private boolean selected, disabled;
@@ -16,8 +16,7 @@ public class SelectableContactListItem extends ContactListItem {
public SelectableContactListItem(Contact contact, LocalAuthor localAuthor, public SelectableContactListItem(Contact contact, LocalAuthor localAuthor,
GroupId groupId, boolean selected, boolean disabled) { GroupId groupId, boolean selected, boolean disabled) {
super(contact, localAuthor, false, groupId, super(contact, localAuthor, false, groupId, new GroupCount(0, 0, 0));
Collections.<ConversationItem>emptyList());
this.selected = selected; this.selected = selected;
this.disabled = disabled; this.disabled = disabled;

View File

@@ -9,6 +9,7 @@ import org.briarproject.R;
import org.briarproject.android.BriarActivity; import org.briarproject.android.BriarActivity;
import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
@@ -105,8 +106,8 @@ abstract class SharingStatusActivity extends BriarActivity {
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
ContactListItem item = ContactListItem item =
new ContactListItem(c, localAuthor, false, null, new ContactListItem(c, localAuthor, false,
null); groupId, new GroupCount(0, 0, 0));
contactItems.add(item); contactItems.add(item);
} }
} catch (DbException e) { } catch (DbException e) {
@@ -141,8 +142,8 @@ abstract class SharingStatusActivity extends BriarActivity {
LocalAuthor localAuthor = identityManager LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId()); .getLocalAuthor(c.getLocalAuthorId());
ContactListItem item = ContactListItem item =
new ContactListItem(c, localAuthor, false, null, new ContactListItem(c, localAuthor, false,
null); groupId, new GroupCount(0, 0, 0));
contactItems.add(item); contactItems.add(item);
} }
} catch (DbException e) { } catch (DbException e) {

View File

@@ -48,11 +48,12 @@ public class TextAvatarView extends FrameLayout {
} }
public void setUnreadCount(int count) { public void setUnreadCount(int count) {
unreadCount = count;
if (count > 0) { if (count > 0) {
this.unreadCount = count;
badge.setBackgroundResource(R.drawable.bubble); badge.setBackgroundResource(R.drawable.bubble);
badge.setText(String.valueOf(count)); badge.setText(String.valueOf(count));
badge.setTextColor(ContextCompat.getColor(getContext(), R.color.briar_text_primary_inverse)); badge.setTextColor(ContextCompat.getColor(getContext(),
R.color.briar_text_primary_inverse));
badge.setVisibility(VISIBLE); badge.setVisibility(VISIBLE);
} else { } else {
badge.setVisibility(INVISIBLE); badge.setVisibility(INVISIBLE);
@@ -63,12 +64,11 @@ public class TextAvatarView extends FrameLayout {
if (problem) { if (problem) {
badge.setBackgroundResource(R.drawable.bubble_problem); badge.setBackgroundResource(R.drawable.bubble_problem);
badge.setText("!"); badge.setText("!");
badge.setTextColor(ContextCompat.getColor(getContext(), R.color.briar_primary)); badge.setTextColor(ContextCompat
.getColor(getContext(), R.color.briar_primary));
badge.setVisibility(VISIBLE); badge.setVisibility(VISIBLE);
} else if (unreadCount > 0) {
setUnreadCount(unreadCount);
} else { } else {
badge.setVisibility(INVISIBLE); setUnreadCount(unreadCount);
} }
} }

View File

@@ -9,18 +9,13 @@ import android.support.annotation.CallSuper;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.widget.AppCompatImageButton;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import org.briarproject.R; import org.briarproject.R;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout; import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
@@ -32,7 +27,6 @@ import org.thoughtcrime.securesms.components.emoji.EmojiToggle;
import java.util.logging.Logger; import java.util.logging.Logger;
import static android.content.Context.INPUT_METHOD_SERVICE; import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@UiThread @UiThread
public class TextInputView extends KeyboardAwareLinearLayout public class TextInputView extends KeyboardAwareLinearLayout

View File

@@ -2,8 +2,8 @@ package org.briarproject.api.blogs;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.InvitationRequest; import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
public class BlogInvitationRequest extends InvitationRequest { public class BlogInvitationRequest extends InvitationRequest {
@@ -11,12 +11,12 @@ public class BlogInvitationRequest extends InvitationRequest {
private final String blogAuthorName; private final String blogAuthorName;
public BlogInvitationRequest(MessageId id, SessionId sessionId, public BlogInvitationRequest(MessageId id, SessionId sessionId,
ContactId contactId, String blogAuthorName, String message, GroupId groupId, ContactId contactId, String blogAuthorName,
boolean available, long time, boolean local, boolean sent, String message, boolean available, long time, boolean local,
boolean seen, boolean read) { boolean sent, boolean seen, boolean read) {
super(id, sessionId, contactId, message, available, time, local, sent, super(id, sessionId, groupId, contactId, message, available, time,
seen, read); local, sent, seen, read);
this.blogAuthorName = blogAuthorName; this.blogAuthorName = blogAuthorName;
} }

View File

@@ -3,15 +3,18 @@ package org.briarproject.api.blogs;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationResponse; import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
public class BlogInvitationResponse extends InvitationResponse { public class BlogInvitationResponse extends InvitationResponse {
public BlogInvitationResponse(MessageId id, SessionId sessionId, public BlogInvitationResponse(@NotNull MessageId id, SessionId sessionId,
ContactId contactId, boolean accept, long time, boolean local, GroupId groupId, ContactId contactId, boolean accept, long time,
boolean sent, boolean seen, boolean read) { boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, contactId, accept, time, local, sent, seen, read); super(id, sessionId, groupId, contactId, accept, time, local, sent,
seen, read);
} }
} }

View File

@@ -13,6 +13,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE; import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG; import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.sharing.SharingConstants.TIME;
public interface BlogSharingMessage { public interface BlogSharingMessage {
@@ -25,9 +26,9 @@ public interface BlogSharingMessage {
public BlogInvitation(GroupId groupId, SessionId sessionId, public BlogInvitation(GroupId groupId, SessionId sessionId,
String blogTitle, String blogDesc, String blogAuthorName, String blogTitle, String blogDesc, String blogAuthorName,
byte[] blogPublicKey, String message) { byte[] blogPublicKey, long time, String message) {
super(groupId, sessionId, message); super(groupId, sessionId, time, message);
this.blogTitle = blogTitle; this.blogTitle = blogTitle;
this.blogDesc = blogDesc; this.blogDesc = blogDesc;
@@ -65,9 +66,10 @@ public interface BlogSharingMessage {
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME); String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY); byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
String message = d.getOptionalString(INVITATION_MSG); String message = d.getOptionalString(INVITATION_MSG);
long time = d.getLong(TIME);
return new BlogInvitation(groupId, sessionId, blogTitle, return new BlogInvitation(groupId, sessionId, blogTitle,
blogDesc, blogAuthorName, blogPublicKey, message); blogDesc, blogAuthorName, blogPublicKey, time, message);
} }
public String getBlogTitle() { public String getBlogTitle() {

View File

@@ -1,17 +1,22 @@
package org.briarproject.api.clients; package org.briarproject.api.clients;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
public abstract class BaseMessageHeader { public abstract class BaseMessageHeader {
private final MessageId id; private final MessageId id;
private final GroupId groupId;
private final long timestamp; private final long timestamp;
private final boolean local, read, sent, seen; private final boolean local, read, sent, seen;
public BaseMessageHeader(MessageId id, long timestamp, boolean local, public BaseMessageHeader(@NotNull MessageId id, @NotNull GroupId groupId,
boolean read, boolean sent, boolean seen) { long timestamp, boolean local, boolean read, boolean sent,
boolean seen) {
this.id = id; this.id = id;
this.groupId = groupId;
this.timestamp = timestamp; this.timestamp = timestamp;
this.local = local; this.local = local;
this.read = read; this.read = read;
@@ -19,10 +24,16 @@ public abstract class BaseMessageHeader {
this.seen = seen; this.seen = seen;
} }
@NotNull
public MessageId getId() { public MessageId getId() {
return id; return id;
} }
@NotNull
public GroupId getGroupId() {
return groupId;
}
public long getTimestamp() { public long getTimestamp() {
return timestamp; return timestamp;
} }
@@ -42,4 +53,5 @@ public abstract class BaseMessageHeader {
public boolean isSeen() { public boolean isSeen() {
return seen; return seen;
} }
} }

View File

@@ -1,9 +1,7 @@
package org.briarproject.api.clients; package org.briarproject.api.clients;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
public interface MessageTracker { public interface MessageTracker {
@@ -20,19 +18,20 @@ public interface MessageTracker {
void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException; void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException;
class GroupCount { class GroupCount {
private final long msgCount, unreadCount, latestMsgTime; private final int msgCount, unreadCount;
private final long latestMsgTime;
public GroupCount(long msgCount, long unreadCount, long latestMsgTime) { public GroupCount(int msgCount, int unreadCount, long latestMsgTime) {
this.msgCount = msgCount; this.msgCount = msgCount;
this.unreadCount = unreadCount; this.unreadCount = unreadCount;
this.latestMsgTime = latestMsgTime; this.latestMsgTime = latestMsgTime;
} }
public long getMsgCount() { public int getMsgCount() {
return msgCount; return msgCount;
} }
public long getUnreadCount() { public int getUnreadCount() {
return unreadCount; return unreadCount;
} }

View File

@@ -2,13 +2,16 @@ package org.briarproject.api.event;
import org.briarproject.api.blogs.Blog; import org.briarproject.api.blogs.Blog;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationRequest;
public class BlogInvitationReceivedEvent extends InvitationReceivedEvent { public class BlogInvitationReceivedEvent extends
InvitationRequestReceivedEvent {
private final Blog blog; private final Blog blog;
public BlogInvitationReceivedEvent(Blog blog, ContactId contactId) { public BlogInvitationReceivedEvent(Blog blog, ContactId contactId,
super(contactId); InvitationRequest request) {
super(contactId, request);
this.blog = blog; this.blog = blog;
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.api.event; package org.briarproject.api.event;
import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
public class BlogInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent { public class BlogInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
@@ -7,8 +8,8 @@ public class BlogInvitationResponseReceivedEvent extends InvitationResponseRecei
private final String blogTitle; private final String blogTitle;
public BlogInvitationResponseReceivedEvent(String blogTitle, public BlogInvitationResponseReceivedEvent(String blogTitle,
ContactId contactId) { ContactId contactId, BlogInvitationResponse response) {
super(contactId); super(contactId, response);
this.blogTitle = blogTitle; this.blogTitle = blogTitle;
} }

View File

@@ -2,17 +2,21 @@ package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationRequest;
public class ForumInvitationReceivedEvent extends InvitationReceivedEvent { public class ForumInvitationReceivedEvent extends
InvitationRequestReceivedEvent {
private final Forum forum; private final Forum forum;
public ForumInvitationReceivedEvent(Forum forum, ContactId contactId) { public ForumInvitationReceivedEvent(Forum forum, ContactId contactId,
super(contactId); ForumInvitationRequest request) {
super(contactId, request);
this.forum = forum; this.forum = forum;
} }
public Forum getForum() { public Forum getForum() {
return forum; return forum;
} }
} }

View File

@@ -1,14 +1,15 @@
package org.briarproject.api.event; package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.forum.ForumInvitationResponse;
public class ForumInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent { public class ForumInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
private final String forumName; private final String forumName;
public ForumInvitationResponseReceivedEvent(String forumName, public ForumInvitationResponseReceivedEvent(String forumName,
ContactId contactId) { ContactId contactId, ForumInvitationResponse response) {
super(contactId); super(contactId, response);
this.forumName = forumName; this.forumName = forumName;
} }

View File

@@ -1,16 +0,0 @@
package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId;
public abstract class InvitationReceivedEvent extends Event {
private final ContactId contactId;
InvitationReceivedEvent(ContactId contactId) {
this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
}
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationRequest;
public abstract class InvitationRequestReceivedEvent extends Event {
private final ContactId contactId;
private final InvitationRequest request;
InvitationRequestReceivedEvent(ContactId contactId,
InvitationRequest request) {
this.contactId = contactId;
this.request = request;
}
public ContactId getContactId() {
return contactId;
}
public InvitationRequest getRequest() {
return request;
}
}

View File

@@ -1,16 +1,24 @@
package org.briarproject.api.event; package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationResponse;
public abstract class InvitationResponseReceivedEvent extends Event { public abstract class InvitationResponseReceivedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final InvitationResponse response;
public InvitationResponseReceivedEvent(ContactId contactId) { public InvitationResponseReceivedEvent(ContactId contactId,
InvitationResponse response) {
this.contactId = contactId; this.contactId = contactId;
this.response = response;
} }
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
public InvitationResponse getResponse() {
return response;
}
} }

View File

@@ -3,19 +3,21 @@ package org.briarproject.api.forum;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationRequest; import org.briarproject.api.sharing.InvitationRequest;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.Nullable;
public class ForumInvitationRequest extends InvitationRequest { public class ForumInvitationRequest extends InvitationRequest {
private final String forumName; private final String forumName;
public ForumInvitationRequest(MessageId id, SessionId sessionId, public ForumInvitationRequest(MessageId id, SessionId sessionId,
ContactId contactId, String forumName, String message, GroupId groupId, ContactId contactId, String forumName, String message,
boolean available, long time, boolean local, boolean sent, boolean available, long time, boolean local, boolean sent,
boolean seen, boolean read) { boolean seen, boolean read) {
super(id, sessionId, contactId, message, available, time, local, sent, super(id, sessionId, groupId, contactId, message, available, time,
seen, read); local, sent, seen, read);
this.forumName = forumName; this.forumName = forumName;
} }

View File

@@ -3,15 +3,19 @@ package org.briarproject.api.forum;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sharing.InvitationResponse; import org.briarproject.api.sharing.InvitationResponse;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ForumInvitationResponse extends InvitationResponse { public class ForumInvitationResponse extends InvitationResponse {
public ForumInvitationResponse(MessageId id, SessionId sessionId, public ForumInvitationResponse(@NotNull MessageId id, SessionId sessionId,
ContactId contactId, boolean accept, long time, boolean local, GroupId groupId, ContactId contactId, boolean accept, long time, boolean local,
boolean sent, boolean seen, boolean read) { boolean sent, boolean seen, boolean read) {
super(id, sessionId, contactId, accept, time, local, sent, seen, read); super(id, sessionId, groupId, contactId, accept, time, local, sent,
seen, read);
} }
} }

View File

@@ -11,6 +11,7 @@ import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG; import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.sharing.SharingConstants.TIME;
public interface ForumSharingMessage { public interface ForumSharingMessage {
@@ -20,9 +21,9 @@ public interface ForumSharingMessage {
private final byte[] forumSalt; private final byte[] forumSalt;
public ForumInvitation(GroupId groupId, SessionId sessionId, public ForumInvitation(GroupId groupId, SessionId sessionId,
String forumName, byte[] forumSalt, String message) { String forumName, byte[] forumSalt, long time, String message) {
super(groupId, sessionId, message); super(groupId, sessionId, time, message);
this.forumName = forumName; this.forumName = forumName;
this.forumSalt = forumSalt; this.forumSalt = forumSalt;
@@ -53,9 +54,10 @@ public interface ForumSharingMessage {
String forumName = d.getString(FORUM_NAME); String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT); byte[] forumSalt = d.getRaw(FORUM_SALT);
String message = d.getOptionalString(INVITATION_MSG); String message = d.getOptionalString(INVITATION_MSG);
long time = d.getLong(TIME);
return new ForumInvitation(groupId, sessionId, forumName, forumSalt, return new ForumInvitation(groupId, sessionId, forumName, forumSalt,
message); time, message);
} }
public String getForumName() { public String getForumName() {

View File

@@ -1,32 +1,36 @@
package org.briarproject.api.introduction; package org.briarproject.api.introduction;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.clients.BaseMessageHeader; import org.briarproject.api.clients.BaseMessageHeader;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCEE;
import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER; import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
public class IntroductionMessage extends BaseMessageHeader { public class IntroductionMessage extends BaseMessageHeader {
private final SessionId sessionId; private final SessionId sessionId;
private final MessageId messageId; private final MessageId messageId;
private final int role; private final int role;
public IntroductionMessage(SessionId sessionId, MessageId messageId, public IntroductionMessage(@NotNull SessionId sessionId,
int role, long time, boolean local, boolean sent, boolean seen, @NotNull MessageId messageId, @NotNull GroupId groupId, int role,
long time, boolean local, boolean sent, boolean seen,
boolean read) { boolean read) {
super(messageId, time, local, read, sent, seen); super(messageId, groupId, time, local, read, sent, seen);
this.sessionId = sessionId; this.sessionId = sessionId;
this.messageId = messageId; this.messageId = messageId;
this.role = role; this.role = role;
} }
@NotNull
public SessionId getSessionId() { public SessionId getSessionId() {
return sessionId; return sessionId;
} }
@NotNull
public MessageId getMessageId() { public MessageId getMessageId() {
return messageId; return messageId;
} }
@@ -35,9 +39,4 @@ import static org.briarproject.api.introduction.IntroductionConstants.ROLE_INTRO
return role == ROLE_INTRODUCER; return role == ROLE_INTRODUCER;
} }
public boolean isIntroducee() {
return role == ROLE_INTRODUCEE;
}
} }

View File

@@ -2,21 +2,25 @@ package org.briarproject.api.introduction;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class IntroductionRequest extends IntroductionResponse { public class IntroductionRequest extends IntroductionResponse {
private final String message; private final String message;
private final boolean answered, exists, introducesOtherIdentity; private final boolean answered, exists, introducesOtherIdentity;
public IntroductionRequest(SessionId sessionId, MessageId messageId, public IntroductionRequest(@NotNull SessionId sessionId,
int role, long time, boolean local, boolean sent, boolean seen, @NotNull MessageId messageId, @NotNull GroupId groupId, int role,
boolean read, AuthorId authorId, String name, boolean accepted, long time, boolean local, boolean sent, boolean seen, boolean read,
String message, boolean answered, boolean exists, AuthorId authorId, String name, boolean accepted,
@Nullable String message, boolean answered, boolean exists,
boolean introducesOtherIdentity) { boolean introducesOtherIdentity) {
super(sessionId, messageId, role, time, local, sent, seen, read, super(sessionId, messageId, groupId, role, time, local, sent, seen,
authorId, name, accepted); read, authorId, name, accepted);
this.message = message; this.message = message;
this.answered = answered; this.answered = answered;
@@ -24,6 +28,7 @@ public class IntroductionRequest extends IntroductionResponse {
this.introducesOtherIdentity = introducesOtherIdentity; this.introducesOtherIdentity = introducesOtherIdentity;
} }
@Nullable
public String getMessage() { public String getMessage() {
return message; return message;
} }

View File

@@ -2,7 +2,9 @@ package org.briarproject.api.introduction;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.identity.AuthorId; import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
public class IntroductionResponse extends IntroductionMessage { public class IntroductionResponse extends IntroductionMessage {
@@ -10,12 +12,13 @@ public class IntroductionResponse extends IntroductionMessage {
private final String name; private final String name;
private final boolean accepted; private final boolean accepted;
public IntroductionResponse(SessionId sessionId, MessageId messageId, public IntroductionResponse(@NotNull SessionId sessionId,
int role, long time, boolean local, boolean sent, boolean seen, @NotNull MessageId messageId, @NotNull GroupId groupId, int role,
boolean read, AuthorId remoteAuthorId, String name, long time, boolean local, boolean sent, boolean seen, boolean read,
boolean accepted) { AuthorId remoteAuthorId, String name, boolean accepted) {
super(sessionId, messageId, role, time, local, sent, seen, read); super(sessionId, messageId, groupId, role, time, local, sent, seen,
read);
this.remoteAuthorId = remoteAuthorId; this.remoteAuthorId = remoteAuthorId;
this.name = name; this.name = name;

View File

@@ -0,0 +1,28 @@
package org.briarproject.api.messaging;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.GroupId;
public interface ConversationManager {
/**
* Clients that present messages in a private conversation need to
* register themselves here.
*/
void registerConversationClient(ConversationClient client);
/** Get the main group ID that represents this conversation */
GroupId getConversationId(ContactId contactId) throws DbException;
/** Get the unified group count for all private conversation messages. */
GroupCount getGroupCount(ContactId contactId) throws DbException;
interface ConversationClient {
GroupCount getGroupCount(Transaction txn, ContactId contactId)
throws DbException;
}
}

View File

@@ -1,17 +1,18 @@
package org.briarproject.api.messaging; package org.briarproject.api.messaging;
import org.briarproject.api.clients.BaseMessageHeader; import org.briarproject.api.clients.BaseMessageHeader;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
public class PrivateMessageHeader extends BaseMessageHeader { public class PrivateMessageHeader extends BaseMessageHeader {
private final String contentType; private final String contentType;
public PrivateMessageHeader(MessageId id, long timestamp, public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp,
String contentType, boolean local, boolean read, boolean sent, String contentType, boolean local, boolean read, boolean sent,
boolean seen) { boolean seen) {
super(id, timestamp, local, read, sent, seen); super(id, groupId, timestamp, local, read, sent, seen);
this.contentType = contentType; this.contentType = contentType;
} }

View File

@@ -1,28 +1,33 @@
package org.briarproject.api.sharing; package org.briarproject.api.sharing;
import org.briarproject.api.clients.BaseMessageHeader;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.clients.BaseMessageHeader; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
public abstract class InvitationMessage extends BaseMessageHeader { public abstract class InvitationMessage extends BaseMessageHeader {
private final SessionId sessionId; private final SessionId sessionId;
private final ContactId contactId; private final ContactId contactId;
public InvitationMessage(MessageId id, SessionId sessionId, public InvitationMessage(@NotNull MessageId id,
ContactId contactId, long time, boolean local, boolean sent, @NotNull SessionId sessionId, @NotNull GroupId groupId,
boolean seen, boolean read) { @NotNull ContactId contactId, long time, boolean local,
boolean sent, boolean seen, boolean read) {
super(id, time, local, read, sent, seen); super(id, groupId, time, local, read, sent, seen);
this.sessionId = sessionId; this.sessionId = sessionId;
this.contactId = contactId; this.contactId = contactId;
} }
@NotNull
public SessionId getSessionId() { public SessionId getSessionId() {
return sessionId; return sessionId;
} }
@NotNull
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }

View File

@@ -2,23 +2,28 @@ package org.briarproject.api.sharing;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class InvitationRequest extends InvitationMessage { public abstract class InvitationRequest extends InvitationMessage {
private final String message; private final String message;
private final boolean available; private final boolean available;
public InvitationRequest(MessageId id, SessionId sessionId, public InvitationRequest(@NotNull MessageId id,
ContactId contactId, String message, @NotNull SessionId sessionId, @NotNull GroupId groupId,
@NotNull ContactId contactId, @Nullable String message,
boolean available, long time, boolean local, boolean sent, boolean available, long time, boolean local, boolean sent,
boolean seen, boolean read) { boolean seen, boolean read) {
super(id, sessionId, contactId, time, local, read, sent, seen); super(id, sessionId, groupId, contactId, time, local, sent, seen, read);
this.message = message; this.message = message;
this.available = available; this.available = available;
} }
@Nullable
public String getMessage() { public String getMessage() {
return message; return message;
} }

View File

@@ -2,17 +2,19 @@ package org.briarproject.api.sharing;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
public abstract class InvitationResponse extends InvitationMessage { public abstract class InvitationResponse extends InvitationMessage {
private final boolean accept; private final boolean accept;
public InvitationResponse(MessageId id, SessionId sessionId, public InvitationResponse(@NotNull MessageId id, SessionId sessionId,
ContactId contactId, boolean accept, long time, boolean local, GroupId groupId, ContactId contactId, boolean accept, long time,
boolean sent, boolean seen, boolean read) { boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, contactId, time, local, read, sent, seen); super(id, sessionId, groupId, contactId, time, local, sent, seen, read);
this.accept = accept; this.accept = accept;
} }

View File

@@ -19,6 +19,8 @@ public interface SharingConstants {
String IS_SHARER = "isSharer"; String IS_SHARER = "isSharer";
String SHAREABLE_ID = "shareableId"; String SHAREABLE_ID = "shareableId";
String INVITATION_MSG = "invitationMsg"; String INVITATION_MSG = "invitationMsg";
String INVITATION_ID = "invitationId";
String RESPONSE_ID = "responseId";
int SHARE_MSG_TYPE_INVITATION = 1; int SHARE_MSG_TYPE_INVITATION = 1;
int SHARE_MSG_TYPE_ACCEPT = 2; int SHARE_MSG_TYPE_ACCEPT = 2;
int SHARE_MSG_TYPE_DECLINE = 3; int SHARE_MSG_TYPE_DECLINE = 3;

View File

@@ -14,6 +14,7 @@ import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEP
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.sharing.SharingConstants.TIME;
import static org.briarproject.api.sharing.SharingConstants.TYPE; import static org.briarproject.api.sharing.SharingConstants.TYPE;
public interface SharingMessage { public interface SharingMessage {
@@ -21,10 +22,12 @@ public interface SharingMessage {
abstract class BaseMessage { abstract class BaseMessage {
private final GroupId groupId; private final GroupId groupId;
private final SessionId sessionId; private final SessionId sessionId;
private final long time;
BaseMessage(GroupId groupId, SessionId sessionId) { BaseMessage(GroupId groupId, SessionId sessionId, long time) {
this.groupId = groupId; this.groupId = groupId;
this.sessionId = sessionId; this.sessionId = sessionId;
this.time = time;
} }
public BdfList toBdfList() { public BdfList toBdfList() {
@@ -62,16 +65,20 @@ public interface SharingMessage {
public SessionId getSessionId() { public SessionId getSessionId() {
return sessionId; return sessionId;
} }
public long getTime() {
return time;
}
} }
abstract class Invitation extends BaseMessage { abstract class Invitation extends BaseMessage {
protected final String message; protected final String message;
public Invitation(GroupId groupId, SessionId sessionId, public Invitation(GroupId groupId, SessionId sessionId, long time,
String message) { String message) {
super(groupId, sessionId); super(groupId, sessionId, time);
this.message = message; this.message = message;
} }
@@ -90,8 +97,9 @@ public interface SharingMessage {
private final long type; private final long type;
public SimpleMessage(long type, GroupId groupId, SessionId sessionId) { public SimpleMessage(long type, GroupId groupId, SessionId sessionId,
super(groupId, sessionId); long time) {
super(groupId, sessionId, time);
this.type = type; this.type = type;
} }
@@ -114,7 +122,8 @@ public interface SharingMessage {
type != SHARE_MSG_TYPE_ABORT) throw new FormatException(); type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID)); SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
return new SimpleMessage(type, groupId, sessionId); long time = d.getLong(TIME);
return new SimpleMessage(type, groupId, sessionId, time);
} }
} }

View File

@@ -81,8 +81,8 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook,
protected void trackMessage(Transaction txn, GroupId g, long time, protected void trackMessage(Transaction txn, GroupId g, long time,
boolean read) throws DbException { boolean read) throws DbException {
GroupCount c = getGroupCount(txn, g); GroupCount c = getGroupCount(txn, g);
long msgCount = c.getMsgCount() + 1; int msgCount = c.getMsgCount() + 1;
long unreadCount = c.getUnreadCount() + (read ? 0 : 1); int unreadCount = c.getUnreadCount() + (read ? 0 : 1);
long latestTime = long latestTime =
time > c.getLatestMsgTime() ? time : c.getLatestMsgTime(); time > c.getLatestMsgTime() ? time : c.getLatestMsgTime();
storeGroupCount(txn, g, storeGroupCount(txn, g,
@@ -103,14 +103,14 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook,
return count; return count;
} }
private GroupCount getGroupCount(Transaction txn, GroupId g) protected GroupCount getGroupCount(Transaction txn, GroupId g)
throws DbException { throws DbException {
GroupCount count; GroupCount count;
try { try {
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g); BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
count = new GroupCount( count = new GroupCount(
d.getLong(GROUP_KEY_MSG_COUNT, 0L), d.getLong(GROUP_KEY_MSG_COUNT, 0L).intValue(),
d.getLong(GROUP_KEY_UNREAD_COUNT, 0L), d.getLong(GROUP_KEY_UNREAD_COUNT, 0L).intValue(),
d.getLong(GROUP_KEY_LATEST_MSG, 0L) d.getLong(GROUP_KEY_LATEST_MSG, 0L)
); );
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -0,0 +1,33 @@
package org.briarproject.clients;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.MetadataParser;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.messaging.ConversationManager.ConversationClient;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
public abstract class ConversationClientImpl extends BdfIncomingMessageHook
implements ConversationClient {
protected ConversationClientImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser) {
super(db, clientHelper, metadataParser);
}
protected abstract Group getContactGroup(Contact contact);
@Override
public GroupCount getGroupCount(Transaction txn, ContactId contactId)
throws DbException {
Contact contact = db.getContact(txn, contactId);
GroupId groupId = getContactGroup(contact).getId();
return getGroupCount(txn, groupId);
}
}

View File

@@ -12,6 +12,7 @@ import org.briarproject.api.introduction.IntroduceeAction;
import org.briarproject.api.introduction.IntroduceeProtocolState; import org.briarproject.api.introduction.IntroduceeProtocolState;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import java.util.ArrayList; import java.util.ArrayList;
@@ -343,6 +344,7 @@ public class IntroduceeEngine
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID)); SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID)); MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID));
GroupId groupId = new GroupId(msg.getRaw(GROUP_ID));
long time = msg.getLong(MESSAGE_TIME); long time = msg.getLong(MESSAGE_TIME);
String name = msg.getString(NAME); String name = msg.getString(NAME);
String message = msg.getOptionalString(MSG); String message = msg.getOptionalString(MSG);
@@ -351,8 +353,9 @@ public class IntroduceeEngine
localState.getBoolean(REMOTE_AUTHOR_IS_US); localState.getBoolean(REMOTE_AUTHOR_IS_US);
IntroductionRequest ir = new IntroductionRequest(sessionId, messageId, IntroductionRequest ir = new IntroductionRequest(sessionId, messageId,
ROLE_INTRODUCEE, time, false, false, false, false, authorId, groupId, ROLE_INTRODUCEE, time, false, false, false, false,
name, false, message, false, exists, introducesOtherIdentity); authorId, name, false, message, false, exists,
introducesOtherIdentity);
return new IntroductionRequestReceivedEvent(contactId, ir); return new IntroductionRequestReceivedEvent(contactId, ir);
} }

View File

@@ -12,6 +12,7 @@ import org.briarproject.api.introduction.IntroducerAction;
import org.briarproject.api.introduction.IntroducerProtocolState; import org.briarproject.api.introduction.IntroducerProtocolState;
import org.briarproject.api.introduction.IntroductionResponse; import org.briarproject.api.introduction.IntroductionResponse;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import java.util.ArrayList; import java.util.ArrayList;
@@ -298,14 +299,15 @@ public class IntroducerEngine
SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID)); SessionId sessionId = new SessionId(localState.getRaw(SESSION_ID));
MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID)); MessageId messageId = new MessageId(msg.getRaw(MESSAGE_ID));
GroupId groupId = new GroupId(msg.getRaw(GROUP_ID));
long time = msg.getLong(MESSAGE_TIME); long time = msg.getLong(MESSAGE_TIME);
String name = getOtherContact(localState, msg); String name = getOtherContact(localState, msg);
boolean accept = msg.getBoolean(ACCEPT); boolean accept = msg.getBoolean(ACCEPT);
IntroductionResponse ir = IntroductionResponse ir =
new IntroductionResponse(sessionId, messageId, ROLE_INTRODUCER, new IntroductionResponse(sessionId, messageId, groupId,
time, false, false, false, false, authorId, name, ROLE_INTRODUCER, time, false, false, false, false,
accept); authorId, name, accept);
return new IntroductionResponseReceivedEvent(contactId, ir); return new IntroductionResponseReceivedEvent(contactId, ir);
} }

View File

@@ -29,7 +29,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus; import org.briarproject.api.sync.MessageStatus;
import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.clients.ConversationClientImpl;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.io.IOException; import java.io.IOException;
@@ -76,7 +76,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUE
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE; import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class IntroductionManagerImpl extends BdfIncomingMessageHook class IntroductionManagerImpl extends ConversationClientImpl
implements IntroductionManager, Client, AddContactHook, implements IntroductionManager, Client, AddContactHook,
RemoveContactHook { RemoveContactHook {
@@ -119,7 +119,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
try { try {
// Create an introduction group for sending introduction messages // Create an introduction group for sending introduction messages
Group g = introductionGroupFactory.createIntroductionGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact // Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return; if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact // Store the group and share it with the contact
@@ -196,7 +196,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
// remove the group (all messages will be removed with it) // remove the group (all messages will be removed with it)
// this contact won't get our abort message, but the other will // this contact won't get our abort message, but the other will
db.removeGroup(txn, introductionGroupFactory.createIntroductionGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
/** /**
@@ -288,6 +288,11 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
return false; return false;
} }
@Override
protected Group getContactGroup(Contact contact) {
return introductionGroupFactory.createIntroductionGroup(contact);
}
@Override @Override
public void makeIntroduction(Contact c1, Contact c2, String msg, public void makeIntroduction(Contact c1, Contact c2, String msg,
final long timestamp) final long timestamp)
@@ -296,8 +301,8 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
introducerManager.makeIntroduction(txn, c1, c2, msg, timestamp); introducerManager.makeIntroduction(txn, c1, c2, msg, timestamp);
Group g1 = introductionGroupFactory.createIntroductionGroup(c1); Group g1 = getContactGroup(c1);
Group g2 = introductionGroupFactory.createIntroductionGroup(c2); Group g2 = getContactGroup(c2);
trackMessage(txn, g1.getId(), timestamp, true); trackMessage(txn, g1.getId(), timestamp, true);
trackMessage(txn, g2.getId(), timestamp, true); trackMessage(txn, g2.getId(), timestamp, true);
txn.setComplete(); txn.setComplete();
@@ -314,7 +319,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
Contact c = db.getContact(txn, contactId); Contact c = db.getContact(txn, contactId);
Group g = introductionGroupFactory.createIntroductionGroup(c); Group g = getContactGroup(c);
BdfDictionary state = BdfDictionary state =
getSessionState(txn, g.getId(), sessionId.getBytes()); getSessionState(txn, g.getId(), sessionId.getBytes());
@@ -334,7 +339,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
Contact c = db.getContact(txn, contactId); Contact c = db.getContact(txn, contactId);
Group g = introductionGroupFactory.createIntroductionGroup(c); Group g = getContactGroup(c);
BdfDictionary state = BdfDictionary state =
getSessionState(txn, g.getId(), sessionId.getBytes()); getSessionState(txn, g.getId(), sessionId.getBytes());
@@ -358,9 +363,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
// get messages and their status // get messages and their status
GroupId g = introductionGroupFactory GroupId g = getContactGroup(db.getContact(txn, contactId)).getId();
.createIntroductionGroup(db.getContact(txn, contactId))
.getId();
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g); metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
statuses = db.getMessageStatus(txn, contactId, g); statuses = db.getMessageStatus(txn, contactId, g);
@@ -415,7 +418,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
name = state.getString(NAME); name = state.getString(NAME);
} }
IntroductionResponse ir = new IntroductionResponse( IntroductionResponse ir = new IntroductionResponse(
sessionId, messageId, role, time, local, sessionId, messageId, g, role, time, local,
s.isSent(), s.isSeen(), read, authorId, name, s.isSent(), s.isSeen(), read, authorId, name,
accepted); accepted);
list.add(ir); list.add(ir);
@@ -445,7 +448,7 @@ class IntroductionManagerImpl extends BdfIncomingMessageHook
state.getBoolean(REMOTE_AUTHOR_IS_US); state.getBoolean(REMOTE_AUTHOR_IS_US);
} }
IntroductionRequest ir = new IntroductionRequest( IntroductionRequest ir = new IntroductionRequest(
sessionId, messageId, role, time, local, sessionId, messageId, g, role, time, local,
s.isSent(), s.isSeen(), read, authorId, name, s.isSent(), s.isSeen(), read, authorId, name,
accepted, message, answered, exists, accepted, message, answered, exists,
introducesOtherIdentity); introducesOtherIdentity);

View File

@@ -6,6 +6,7 @@ import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.introduction.IntroductionManager; import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import javax.inject.Inject; import javax.inject.Inject;
@@ -47,6 +48,7 @@ public class IntroductionModule {
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, MessageQueueManager messageQueueManager,
ConversationManager conversationManager,
IntroductionManagerImpl introductionManager) { IntroductionManagerImpl introductionManager) {
lifecycleManager.registerClient(introductionManager); lifecycleManager.registerClient(introductionManager);
@@ -55,6 +57,7 @@ public class IntroductionModule {
messageQueueManager.registerIncomingMessageHook( messageQueueManager.registerIncomingMessageHook(
introductionManager.getClientId(), introductionManager.getClientId(),
introductionManager); introductionManager);
conversationManager.registerConversationClient(introductionManager);
return introductionManager; return introductionManager;
} }

View File

@@ -0,0 +1,80 @@
package org.briarproject.messaging;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.inject.Inject;
class ConversationManagerImpl implements ConversationManager {
private final DatabaseComponent db;
private final ContactGroupFactory contactGroupFactory;
private final Set<ConversationClient> clients;
@Inject
ConversationManagerImpl(DatabaseComponent db,
ContactGroupFactory contactGroupFactory) {
this.db = db;
this.contactGroupFactory = contactGroupFactory;
clients = new CopyOnWriteArraySet<ConversationClient>();
}
@Override
public void registerConversationClient(ConversationClient client) {
if (!clients.add(client)) {
throw new IllegalStateException(
"This client is already registered");
}
}
@Override
public GroupId getConversationId(ContactId contactId) throws DbException {
// TODO we should probably transition this to its own group
// and/or work with the ContactId in the UI instead
Contact contact;
Transaction txn = db.startTransaction(true);
try {
contact = db.getContact(txn, contactId);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
Group group = contactGroupFactory
.createContactGroup(MessagingManagerImpl.CLIENT_ID, contact);
return group.getId();
}
@Override
public GroupCount getGroupCount(ContactId contactId)
throws DbException {
int msgCount = 0, unreadCount = 0;
long latestTime = 0;
Transaction txn = db.startTransaction(true);
try {
for (ConversationClient client : clients) {
GroupCount count = client.getGroupCount(txn, contactId);
msgCount += count.getMsgCount();
unreadCount += count.getUnreadCount();
if (count.getLatestMsgTime() > latestTime)
latestTime = count.getLatestMsgTime();
}
txn.setComplete();
} finally {
db.endTransaction(txn);
}
return new GroupCount(msgCount, unreadCount, latestTime);
}
}

View File

@@ -24,7 +24,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message; import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus; import org.briarproject.api.sync.MessageStatus;
import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.clients.ConversationClientImpl;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
@@ -35,7 +35,7 @@ import javax.inject.Inject;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
class MessagingManagerImpl extends BdfIncomingMessageHook class MessagingManagerImpl extends ConversationClientImpl
implements MessagingManager, Client, AddContactHook, RemoveContactHook { implements MessagingManager, Client, AddContactHook, RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
@@ -77,7 +77,8 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
} }
} }
private Group getContactGroup(Contact c) { @Override
protected Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, c); return contactGroupFactory.createContactGroup(CLIENT_ID, c);
} }
@@ -101,7 +102,8 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
boolean local = meta.getBoolean("local"); boolean local = meta.getBoolean("local");
boolean read = meta.getBoolean(MSG_KEY_READ); boolean read = meta.getBoolean(MSG_KEY_READ);
PrivateMessageHeader header = new PrivateMessageHeader( PrivateMessageHeader header = new PrivateMessageHeader(
m.getId(), timestamp, contentType, local, read, false, false); m.getId(), m.getGroupId(), timestamp, contentType, local, read,
false, false);
PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent( PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent(
header, groupId); header, groupId);
txn.attach(event); txn.attach(event);
@@ -159,9 +161,10 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
throws DbException { throws DbException {
Map<MessageId, BdfDictionary> metadata; Map<MessageId, BdfDictionary> metadata;
Collection<MessageStatus> statuses; Collection<MessageStatus> statuses;
GroupId g;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
GroupId g = getContactGroup(db.getContact(txn, c)).getId(); g = getContactGroup(db.getContact(txn, c)).getId();
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g); metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
statuses = db.getMessageStatus(txn, c, g); statuses = db.getMessageStatus(txn, c, g);
txn.setComplete(); txn.setComplete();
@@ -181,8 +184,8 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
String contentType = meta.getString("contentType"); String contentType = meta.getString("contentType");
boolean local = meta.getBoolean("local"); boolean local = meta.getBoolean("local");
boolean read = meta.getBoolean("read"); boolean read = meta.getBoolean("read");
headers.add(new PrivateMessageHeader(id, timestamp, contentType, headers.add(new PrivateMessageHeader(id, g, timestamp,
local, read, s.isSent(), s.isSeen())); contentType, local, read, s.isSent(), s.isSeen()));
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} }
@@ -201,4 +204,14 @@ class MessagingManagerImpl extends BdfIncomingMessageHook
} }
} }
@Override
public GroupCount getGroupCount(Transaction txn, ContactId contactId)
throws DbException {
Contact contact = db.getContact(txn, contactId);
GroupId groupId = getContactGroup(contact).getId();
return getGroupCount(txn, groupId);
}
} }

View File

@@ -4,6 +4,7 @@ import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.contact.ContactManager; import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
@@ -22,6 +23,7 @@ public class MessagingModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject MessagingManager messagingManager; @Inject MessagingManager messagingManager;
@Inject ConversationManager conversationManager;
@Inject PrivateMessageValidator privateMessageValidator; @Inject PrivateMessageValidator privateMessageValidator;
} }
@@ -46,12 +48,22 @@ public class MessagingModule {
@Singleton @Singleton
MessagingManager getMessagingManager(LifecycleManager lifecycleManager, MessagingManager getMessagingManager(LifecycleManager lifecycleManager,
ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager,
ConversationManager conversationManager,
MessagingManagerImpl messagingManager) { MessagingManagerImpl messagingManager) {
lifecycleManager.registerClient(messagingManager); lifecycleManager.registerClient(messagingManager);
contactManager.registerAddContactHook(messagingManager); contactManager.registerAddContactHook(messagingManager);
contactManager.registerRemoveContactHook(messagingManager); contactManager.registerRemoveContactHook(messagingManager);
validationManager validationManager
.registerIncomingMessageHook(CLIENT_ID, messagingManager); .registerIncomingMessageHook(CLIENT_ID, messagingManager);
conversationManager.registerConversationClient(messagingManager);
return messagingManager; return messagingManager;
} }
@Provides
@Singleton
ConversationManager getConversationManager(
ConversationManagerImpl conversationManager) {
return conversationManager;
}
} }

View File

@@ -5,6 +5,8 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME; import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC; import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
@@ -21,8 +23,9 @@ public class BlogInviteeSessionState extends InviteeSessionState {
public BlogInviteeSessionState(SessionId sessionId, MessageId storageId, public BlogInviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId blogId, GroupId groupId, State state, ContactId contactId, GroupId blogId,
String blogTitle, String blogDesc, String blogAuthorName, String blogTitle, String blogDesc, String blogAuthorName,
byte[] blogPublicKey) { byte[] blogPublicKey, @NotNull MessageId invitationId) {
super(sessionId, storageId, groupId, state, contactId, blogId); super(sessionId, storageId, groupId, state, contactId, blogId,
invitationId);
this.blogTitle = blogTitle; this.blogTitle = blogTitle;
this.blogDesc = blogDesc; this.blogDesc = blogDesc;

View File

@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.Nullable;
import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME; import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC; import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
@@ -21,8 +22,9 @@ public class BlogSharerSessionState extends SharerSessionState {
public BlogSharerSessionState(SessionId sessionId, MessageId storageId, public BlogSharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId blogId, GroupId groupId, State state, ContactId contactId, GroupId blogId,
String blogTitle, String blogDesc, String blogAuthorName, String blogTitle, String blogDesc, String blogAuthorName,
byte[] blogPublicKey) { byte[] blogPublicKey, @Nullable MessageId responseId) {
super(sessionId, storageId, groupId, state, contactId, blogId); super(sessionId, storageId, groupId, state, contactId, blogId,
responseId);
this.blogTitle = blogTitle; this.blogTitle = blogTitle;
this.blogDesc = blogDesc; this.blogDesc = blogDesc;

View File

@@ -10,8 +10,8 @@ import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation; import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.ContactGroupFactory; import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -43,6 +43,8 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC; import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY; import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE; import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.api.sharing.SharingConstants.RESPONSE_ID;
class BlogSharingManagerImpl extends class BlogSharingManagerImpl extends
SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent> SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
@@ -108,17 +110,18 @@ class BlogSharingManagerImpl extends
BlogInvitation msg, ContactId contactId, boolean available, BlogInvitation msg, ContactId contactId, boolean available,
long time, boolean local, boolean sent, boolean seen, long time, boolean local, boolean sent, boolean seen,
boolean read) { boolean read) {
return new BlogInvitationRequest(id, msg.getSessionId(), contactId, return new BlogInvitationRequest(id, msg.getSessionId(),
msg.getBlogAuthorName(), msg.getMessage(), available, time, msg.getGroupId(), contactId, msg.getBlogAuthorName(),
local, sent, seen, read); msg.getMessage(), available, time, local, sent, seen, read);
} }
@Override @Override
protected InvitationMessage createInvitationResponse(MessageId id, protected InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, ContactId contactId, boolean accept, long time, SessionId sessionId, GroupId groupId, ContactId contactId,
boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) { boolean local, boolean sent, boolean seen, boolean read) {
return new BlogInvitationResponse(id, sessionId, contactId, accept, return new BlogInvitationResponse(id, sessionId, groupId, contactId,
time, local, sent, seen, read); accept, time, local, sent, seen, read);
} }
@Override @Override
@@ -163,7 +166,7 @@ class BlogSharingManagerImpl extends
private final BlogFactory blogFactory; private final BlogFactory blogFactory;
private final BlogManager blogManager; private final BlogManager blogManager;
SFactory(AuthorFactory authorFactory, BlogFactory BlogFactory, private SFactory(AuthorFactory authorFactory, BlogFactory BlogFactory,
BlogManager BlogManager) { BlogManager BlogManager) {
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.blogFactory = BlogFactory; this.blogFactory = BlogFactory;
@@ -230,11 +233,13 @@ class BlogSharingManagerImpl extends
} }
@Override @Override
public BlogInvitation build(BlogSharerSessionState localState) { public BlogInvitation build(BlogSharerSessionState localState,
long time) {
return new BlogInvitation(localState.getGroupId(), return new BlogInvitation(localState.getGroupId(),
localState.getSessionId(), localState.getBlogTitle(), localState.getSessionId(), localState.getBlogTitle(),
localState.getBlogDesc(), localState.getBlogAuthorName(), localState.getBlogDesc(), localState.getBlogAuthorName(),
localState.getBlogPublicKey(), localState.getMessage()); localState.getBlogPublicKey(), time,
localState.getMessage());
} }
} }
@@ -249,20 +254,21 @@ class BlogSharingManagerImpl extends
String blogDesc = d.getString(BLOG_DESC); String blogDesc = d.getString(BLOG_DESC);
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME); String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY); byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
MessageId invitationId = new MessageId(d.getRaw(INVITATION_ID));
return new BlogInviteeSessionState(sessionId, storageId, return new BlogInviteeSessionState(sessionId, storageId,
groupId, state, contactId, blogId, blogTitle, blogDesc, groupId, state, contactId, blogId, blogTitle, blogDesc,
blogAuthorName, blogPublicKey); blogAuthorName, blogPublicKey, invitationId);
} }
@Override @Override
public BlogInviteeSessionState build(SessionId sessionId, public BlogInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId, InviteeSessionState.State state, ContactId contactId,
Blog blog) { Blog blog, MessageId invitationId) {
return new BlogInviteeSessionState(sessionId, storageId, return new BlogInviteeSessionState(sessionId, storageId,
groupId, state, contactId, blog.getId(), blog.getName(), groupId, state, contactId, blog.getId(), blog.getName(),
blog.getDescription(), blog.getAuthor().getName(), blog.getDescription(), blog.getAuthor().getName(),
blog.getAuthor().getPublicKey()); blog.getAuthor().getPublicKey(), invitationId);
} }
} }
@@ -277,9 +283,13 @@ class BlogSharingManagerImpl extends
String blogDesc = d.getString(BLOG_DESC); String blogDesc = d.getString(BLOG_DESC);
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME); String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY); byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
MessageId responseId = null;
byte[] responseIdBytes = d.getOptionalRaw(RESPONSE_ID);
if (responseIdBytes != null)
responseId = new MessageId(responseIdBytes);
return new BlogSharerSessionState(sessionId, storageId, return new BlogSharerSessionState(sessionId, storageId,
groupId, state, contactId, blogId, blogTitle, blogDesc, groupId, state, contactId, blogId, blogTitle, blogDesc,
blogAuthorName, blogPublicKey); blogAuthorName, blogPublicKey, responseId);
} }
@Override @Override
@@ -290,7 +300,7 @@ class BlogSharingManagerImpl extends
return new BlogSharerSessionState(sessionId, storageId, return new BlogSharerSessionState(sessionId, storageId,
groupId, state, contactId, blog.getId(), blog.getName(), groupId, state, contactId, blog.getId(), blog.getName(),
blog.getDescription(), blog.getAuthor().getName(), blog.getDescription(), blog.getAuthor().getName(),
blog.getAuthor().getPublicKey()); blog.getAuthor().getPublicKey(), null);
} }
} }
@@ -299,16 +309,21 @@ class BlogSharingManagerImpl extends
private final SFactory sFactory; private final SFactory sFactory;
IRFactory(SFactory sFactory) { private IRFactory(SFactory sFactory) {
this.sFactory = sFactory; this.sFactory = sFactory;
} }
@Override @Override
public BlogInvitationReceivedEvent build( public BlogInvitationReceivedEvent build(
BlogInviteeSessionState localState) { BlogInviteeSessionState localState, long time, String msg) {
Blog blog = sFactory.parse(localState); Blog blog = sFactory.parse(localState);
ContactId contactId = localState.getContactId(); ContactId contactId = localState.getContactId();
return new BlogInvitationReceivedEvent(blog, contactId); BlogInvitationRequest request =
new BlogInvitationRequest(localState.getInvitationId(),
localState.getSessionId(), localState.getGroupId(),
contactId, blog.getAuthor().getName(), msg, true,
time, false, false, false, false);
return new BlogInvitationReceivedEvent(blog, contactId, request);
} }
} }
@@ -316,10 +331,18 @@ class BlogSharingManagerImpl extends
InvitationResponseReceivedEventFactory<BlogSharerSessionState, BlogInvitationResponseReceivedEvent> { InvitationResponseReceivedEventFactory<BlogSharerSessionState, BlogInvitationResponseReceivedEvent> {
@Override @Override
public BlogInvitationResponseReceivedEvent build( public BlogInvitationResponseReceivedEvent build(
BlogSharerSessionState localState) { BlogSharerSessionState localState, boolean accept, long time) {
String title = localState.getBlogTitle(); String title = localState.getBlogTitle();
ContactId c = localState.getContactId(); ContactId c = localState.getContactId();
return new BlogInvitationResponseReceivedEvent(title, c); MessageId responseId = localState.getResponseId();
if (responseId == null)
throw new IllegalStateException("No responseId");
BlogInvitationResponse response =
new BlogInvitationResponse(responseId,
localState.getSessionId(), localState.getGroupId(),
localState.getContactId(), accept, time, false,
false, false, false);
return new BlogInvitationResponseReceivedEvent(title, c, response);
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
@@ -16,8 +17,10 @@ public class ForumInviteeSessionState extends InviteeSessionState {
public ForumInviteeSessionState(SessionId sessionId, MessageId storageId, public ForumInviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId, GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) { String forumName, byte[] forumSalt,
super(sessionId, storageId, groupId, state, contactId, forumId); @NotNull MessageId invitationId) {
super(sessionId, storageId, groupId, state, contactId, forumId,
invitationId);
this.forumName = forumName; this.forumName = forumName;
this.forumSalt = forumSalt; this.forumSalt = forumSalt;

View File

@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.Nullable;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
@@ -16,8 +17,10 @@ class ForumSharerSessionState extends SharerSessionState {
ForumSharerSessionState(SessionId sessionId, MessageId storageId, ForumSharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId, GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) { String forumName, byte[] forumSalt,
super(sessionId, storageId, groupId, state, contactId, forumId); @Nullable MessageId responseId) {
super(sessionId, storageId, groupId, state, contactId, forumId,
responseId);
this.forumName = forumName; this.forumName = forumName;
this.forumSalt = forumSalt; this.forumSalt = forumSalt;

View File

@@ -2,8 +2,8 @@ package org.briarproject.sharing;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.ContactGroupFactory; import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
@@ -35,6 +35,8 @@ import javax.inject.Inject;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.api.sharing.SharingConstants.RESPONSE_ID;
class ForumSharingManagerImpl extends class ForumSharingManagerImpl extends
SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent> SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent>
@@ -82,17 +84,18 @@ class ForumSharingManagerImpl extends
ForumInvitation msg, ContactId contactId, boolean available, ForumInvitation msg, ContactId contactId, boolean available,
long time, boolean local, boolean sent, boolean seen, long time, boolean local, boolean sent, boolean seen,
boolean read) { boolean read) {
return new ForumInvitationRequest(id, msg.getSessionId(), contactId, return new ForumInvitationRequest(id, msg.getSessionId(),
msg.getForumName(), msg.getMessage(), available, time, local, msg.getGroupId(), contactId, msg.getForumName(),
sent, seen, read); msg.getMessage(), available, time, local, sent, seen, read);
} }
@Override @Override
protected InvitationMessage createInvitationResponse(MessageId id, protected InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, ContactId contactId, boolean accept, SessionId sessionId, GroupId groupId, ContactId contactId,
long time, boolean local, boolean sent, boolean seen, boolean read) { boolean accept, long time, boolean local, boolean sent,
return new ForumInvitationResponse(id, sessionId, contactId, accept, boolean seen, boolean read) {
time, local, sent, seen, read); return new ForumInvitationResponse(id, sessionId, groupId, contactId,
accept, time, local, sent, seen, read);
} }
@Override @Override
@@ -136,7 +139,7 @@ class ForumSharingManagerImpl extends
private final ForumFactory forumFactory; private final ForumFactory forumFactory;
private final ForumManager forumManager; private final ForumManager forumManager;
SFactory(ForumFactory forumFactory, ForumManager forumManager) { private SFactory(ForumFactory forumFactory, ForumManager forumManager) {
this.forumFactory = forumFactory; this.forumFactory = forumFactory;
this.forumManager = forumManager; this.forumManager = forumManager;
} }
@@ -185,10 +188,11 @@ class ForumSharingManagerImpl extends
} }
@Override @Override
public ForumInvitation build(ForumSharerSessionState localState) { public ForumInvitation build(ForumSharerSessionState localState,
long time) {
return new ForumInvitation(localState.getGroupId(), return new ForumInvitation(localState.getGroupId(),
localState.getSessionId(), localState.getForumName(), localState.getSessionId(), localState.getForumName(),
localState.getForumSalt(), localState.getMessage()); localState.getForumSalt(), time, localState.getMessage());
} }
} }
@@ -201,18 +205,20 @@ class ForumSharingManagerImpl extends
GroupId forumId, BdfDictionary d) throws FormatException { GroupId forumId, BdfDictionary d) throws FormatException {
String forumName = d.getString(FORUM_NAME); String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT); byte[] forumSalt = d.getRaw(FORUM_SALT);
MessageId invitationId = new MessageId(d.getRaw(INVITATION_ID));
return new ForumInviteeSessionState(sessionId, storageId, return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt); groupId, state, contactId, forumId, forumName, forumSalt,
invitationId);
} }
@Override @Override
public ForumInviteeSessionState build(SessionId sessionId, public ForumInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId, InviteeSessionState.State state, ContactId contactId,
Forum forum) { Forum forum, MessageId invitationId) {
return new ForumInviteeSessionState(sessionId, storageId, return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(), groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt()); forum.getSalt(), invitationId);
} }
} }
@@ -225,8 +231,13 @@ class ForumSharingManagerImpl extends
GroupId forumId, BdfDictionary d) throws FormatException { GroupId forumId, BdfDictionary d) throws FormatException {
String forumName = d.getString(FORUM_NAME); String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT); byte[] forumSalt = d.getRaw(FORUM_SALT);
MessageId responseId = null;
byte[] responseIdBytes = d.getOptionalRaw(RESPONSE_ID);
if (responseIdBytes != null)
responseId = new MessageId(responseIdBytes);
return new ForumSharerSessionState(sessionId, storageId, return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt); groupId, state, contactId, forumId, forumName, forumSalt,
responseId);
} }
@Override @Override
@@ -236,7 +247,7 @@ class ForumSharingManagerImpl extends
Forum forum) { Forum forum) {
return new ForumSharerSessionState(sessionId, storageId, return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(), groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt()); forum.getSalt(), null);
} }
} }
@@ -245,16 +256,20 @@ class ForumSharingManagerImpl extends
private final SFactory sFactory; private final SFactory sFactory;
IRFactory(SFactory sFactory) { private IRFactory(SFactory sFactory) {
this.sFactory = sFactory; this.sFactory = sFactory;
} }
@Override @Override
public ForumInvitationReceivedEvent build( public ForumInvitationReceivedEvent build(
ForumInviteeSessionState localState) { ForumInviteeSessionState localState, long time, String msg) {
Forum forum = sFactory.parse(localState); Forum forum = sFactory.parse(localState);
ContactId contactId = localState.getContactId(); ContactId contactId = localState.getContactId();
return new ForumInvitationReceivedEvent(forum, contactId); ForumInvitationRequest request = new ForumInvitationRequest(
localState.getInvitationId(), localState.getSessionId(),
localState.getGroupId(), contactId, forum.getName(), msg,
true, time, false, false, false, false);
return new ForumInvitationReceivedEvent(forum, contactId, request);
} }
} }
@@ -262,10 +277,17 @@ class ForumSharingManagerImpl extends
InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> { InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
@Override @Override
public ForumInvitationResponseReceivedEvent build( public ForumInvitationResponseReceivedEvent build(
ForumSharerSessionState localState) { ForumSharerSessionState localState, boolean accept, long time) {
String name = localState.getForumName(); String name = localState.getForumName();
ContactId c = localState.getContactId(); ContactId c = localState.getContactId();
return new ForumInvitationResponseReceivedEvent(name, c); MessageId responseId = localState.getResponseId();
if (responseId == null)
throw new IllegalStateException("No responseId");
ForumInvitationResponse response = new ForumInvitationResponse(
responseId, localState.getSessionId(),
localState.getGroupId(), localState.getContactId(), accept,
time, false, false, false, false);
return new ForumInvitationResponseReceivedEvent(name, c, response);
} }
} }
} }

View File

@@ -5,5 +5,5 @@ import org.briarproject.api.sharing.SharingMessage;
public interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState> extends public interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState> extends
org.briarproject.api.sharing.InvitationFactory<I> { org.briarproject.api.sharing.InvitationFactory<I> {
I build(SS localState); I build(SS localState, long time);
} }

View File

@@ -1,8 +1,8 @@
package org.briarproject.sharing; package org.briarproject.sharing;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
public interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationReceivedEvent> { public interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> {
IR build(IS localState); IR build(IS localState, long time, String msg);
} }

View File

@@ -4,5 +4,5 @@ import org.briarproject.api.event.InvitationResponseReceivedEvent;
public interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> { public interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
IRR build(SS localState); IRR build(SS localState, boolean accept, long time);
} }

View File

@@ -3,7 +3,9 @@ package org.briarproject.sharing;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine; import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.sharing.SharingMessage.Invitation;
import org.briarproject.api.system.Clock;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -23,16 +25,20 @@ import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREAB
import static org.briarproject.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage; import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceivedEvent> class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent>
implements ProtocolEngine<InviteeSessionState.Action, IS, BaseMessage> { implements ProtocolEngine<InviteeSessionState.Action, IS, BaseMessage> {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(InviteeEngine.class.getName()); Logger.getLogger(InviteeEngine.class.getName());
private final InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory; private final InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory;
private final Clock clock;
InviteeEngine(InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory) { InviteeEngine(
InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory,
Clock clock) {
this.invitationReceivedEventFactory = invitationReceivedEventFactory; this.invitationReceivedEventFactory = invitationReceivedEventFactory;
this.clock = clock;
} }
@Override @Override
@@ -63,19 +69,22 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
if (action == InviteeSessionState.Action.LOCAL_ACCEPT) { if (action == InviteeSessionState.Action.LOCAL_ACCEPT) {
localState.setTask(TASK_ADD_SHARED_SHAREABLE); localState.setTask(TASK_ADD_SHARED_SHAREABLE);
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT, msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
localState.getGroupId(), localState.getSessionId()); localState.getGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
} else { } else {
localState.setTask( localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US); TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE, msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
localState.getGroupId(), localState.getSessionId()); localState.getGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
} }
messages = Collections.singletonList(msg); messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg); logLocalAction(currentState, localState, msg);
} }
else if (action == InviteeSessionState.Action.LOCAL_LEAVE) { else if (action == InviteeSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE, BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId()); localState.getGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
messages = Collections.singletonList(msg); messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg); logLocalAction(currentState, localState, msg);
} }
@@ -133,7 +142,9 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
// we have just received our invitation // we have just received our invitation
else if (action == InviteeSessionState.Action.REMOTE_INVITATION) { else if (action == InviteeSessionState.Action.REMOTE_INVITATION) {
localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US); localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US);
Event event = invitationReceivedEventFactory.build(localState); Invitation invitation = (Invitation) msg;
Event event = invitationReceivedEventFactory.build(localState,
msg.getTime(), invitation.getMessage());
events = Collections.singletonList(event); events = Collections.singletonList(event);
} }
else { else {
@@ -203,7 +214,7 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
localState.setState(InviteeSessionState.State.ERROR); localState.setState(InviteeSessionState.State.ERROR);
BaseMessage msg = BaseMessage msg =
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(), new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
localState.getSessionId()); localState.getSessionId(), clock.currentTimeMillis());
List<BaseMessage> messages = Collections.singletonList(msg); List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList(); List<Event> events = Collections.emptyList();

View File

@@ -5,7 +5,9 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.NotNull;
import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
@@ -21,19 +23,23 @@ import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
public abstract class InviteeSessionState extends SharingSessionState { public abstract class InviteeSessionState extends SharingSessionState {
private State state; private State state;
@NotNull
private final MessageId invitationId;
public InviteeSessionState(SessionId sessionId, MessageId storageId, public InviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId groupId, State state, ContactId contactId,
GroupId shareableId) { GroupId shareableId, @NotNull MessageId invitationId) {
super(sessionId, storageId, groupId, contactId, shareableId); super(sessionId, storageId, groupId, contactId, shareableId);
this.state = state; this.state = state;
this.invitationId = invitationId;
} }
public BdfDictionary toBdfDictionary() { public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary(); BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue()); d.put(STATE, getState().getValue());
d.put(IS_SHARER, false); d.put(IS_SHARER, false);
d.put(INVITATION_ID, invitationId);
return d; return d;
} }
@@ -45,6 +51,11 @@ public abstract class InviteeSessionState extends SharingSessionState {
return state; return state;
} }
@NotNull
public MessageId getInvitationId() {
return invitationId;
}
public enum State { public enum State {
ERROR(0), ERROR(0),
AWAIT_INVITATION(1) { AWAIT_INVITATION(1) {

View File

@@ -15,5 +15,6 @@ public interface InviteeSessionStateFactory<S extends Shareable, IS extends Invi
GroupId shareableId, BdfDictionary d) throws FormatException; GroupId shareableId, BdfDictionary d) throws FormatException;
IS build(SessionId sessionId, MessageId storageId, GroupId groupId, IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId, S shareable); InviteeSessionState.State state, ContactId contactId, S shareable,
MessageId invitationId);
} }

View File

@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine; import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.system.Clock;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -22,6 +23,8 @@ import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREAB
import static org.briarproject.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.Invitation; import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage; import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_DECLINE;
class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> { implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> {
@@ -32,12 +35,15 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
private final InvitationFactory<I, SS> invitationFactory; private final InvitationFactory<I, SS> invitationFactory;
private final InvitationResponseReceivedEventFactory<SS, IRR> private final InvitationResponseReceivedEventFactory<SS, IRR>
invitationResponseReceivedEventFactory; invitationResponseReceivedEventFactory;
private final Clock clock;
SharerEngine(InvitationFactory<I, SS> invitationFactory, SharerEngine(InvitationFactory<I, SS> invitationFactory,
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory) { InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory,
Clock clock) {
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;
this.invitationResponseReceivedEventFactory = this.invitationResponseReceivedEventFactory =
invitationResponseReceivedEventFactory; invitationResponseReceivedEventFactory;
this.clock = clock;
} }
@Override @Override
@@ -65,7 +71,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
List<Event> events = Collections.emptyList(); List<Event> events = Collections.emptyList();
if (action == SharerSessionState.Action.LOCAL_INVITATION) { if (action == SharerSessionState.Action.LOCAL_INVITATION) {
BaseMessage msg = invitationFactory.build(localState); BaseMessage msg = invitationFactory.build(localState,
clock.currentTimeMillis());
messages = Collections.singletonList(msg); messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg); logLocalAction(currentState, nextState, msg);
@@ -74,7 +81,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
.setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US); .setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US);
} else if (action == SharerSessionState.Action.LOCAL_LEAVE) { } else if (action == SharerSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE, BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId()); localState.getGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
messages = Collections.singletonList(msg); messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg); logLocalAction(currentState, nextState, msg);
} else { } else {
@@ -122,9 +130,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
deleteMsg = true; deleteMsg = true;
} }
// we have sent our invitation and just got a response // we have sent our invitation and just got a response
else if (action == SharerSessionState.Action.REMOTE_ACCEPT || else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
action == SharerSessionState.Action.REMOTE_DECLINE) { if (action == REMOTE_ACCEPT) {
if (action == SharerSessionState.Action.REMOTE_ACCEPT) {
localState.setTask(TASK_SHARE_SHAREABLE); localState.setTask(TASK_SHARE_SHAREABLE);
} else { } else {
// this ensures that the forum can be shared again // this ensures that the forum can be shared again
@@ -132,7 +139,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US); TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US);
} }
Event event = invitationResponseReceivedEventFactory Event event = invitationResponseReceivedEventFactory
.build(localState); .build(localState, action == REMOTE_ACCEPT,
msg.getTime());
events = Collections.singletonList(event); events = Collections.singletonList(event);
} else { } else {
throw new IllegalArgumentException("Bad state"); throw new IllegalArgumentException("Bad state");
@@ -204,7 +212,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
localState.setState(SharerSessionState.State.ERROR); localState.setState(SharerSessionState.State.ERROR);
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT, BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
localState.getGroupId(), localState.getSessionId()); localState.getGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
List<BaseMessage> messages = Collections.singletonList(msg); List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList(); List<Event> events = Collections.emptyList();

View File

@@ -5,8 +5,10 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.jetbrains.annotations.Nullable;
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.sharing.SharingConstants.RESPONSE_ID;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
@@ -22,20 +24,25 @@ import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_LEAVE;
public abstract class SharerSessionState extends SharingSessionState { public abstract class SharerSessionState extends SharingSessionState {
private State state; private State state;
@Nullable
private String msg = null; private String msg = null;
@Nullable
private MessageId responseId;
public SharerSessionState(SessionId sessionId, MessageId storageId, public SharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId groupId, State state, ContactId contactId,
GroupId shareableId) { GroupId shareableId, @Nullable MessageId responseId) {
super(sessionId, storageId, groupId, contactId, shareableId); super(sessionId, storageId, groupId, contactId, shareableId);
this.state = state; this.state = state;
this.responseId = responseId;
} }
public BdfDictionary toBdfDictionary() { public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary(); BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue()); d.put(STATE, getState().getValue());
d.put(IS_SHARER, true); d.put(IS_SHARER, true);
if (responseId != null) d.put(RESPONSE_ID, responseId);
return d; return d;
} }
@@ -55,6 +62,15 @@ public abstract class SharerSessionState extends SharingSessionState {
return this.msg; return this.msg;
} }
public void setResponseId(@Nullable MessageId responseId) {
this.responseId = responseId;
}
@Nullable
public MessageId getResponseId() {
return responseId;
}
public enum State { public enum State {
ERROR(0), ERROR(0),
PREPARE_INVITATION(1) { PREPARE_INVITATION(1) {

View File

@@ -4,8 +4,8 @@ import org.briarproject.api.Bytes;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.Client; import org.briarproject.api.clients.Client;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.ContactGroupFactory; import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -22,7 +22,7 @@ import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.InvitationReceivedEvent; import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sharing.InvitationItem; import org.briarproject.api.sharing.InvitationItem;
@@ -36,7 +36,7 @@ import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageStatus; import org.briarproject.api.sync.MessageStatus;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.clients.ConversationClientImpl;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
import java.io.IOException; import java.io.IOException;
@@ -85,8 +85,8 @@ import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.clients.BdfConstants.MSG_KEY_READ; import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent> abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationRequestReceivedEvent, IRR extends InvitationResponseReceivedEvent>
extends BdfIncomingMessageHook extends ConversationClientImpl
implements SharingManager<S>, Client, AddContactHook, implements SharingManager<S>, Client, AddContactHook,
RemoveContactHook { RemoveContactHook {
@@ -117,13 +117,14 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
public abstract ClientId getClientId(); public abstract ClientId getClientId();
protected abstract InvitationMessage createInvitationRequest(MessageId id, I msg, protected abstract InvitationMessage createInvitationRequest(MessageId id,
ContactId contactId, boolean available, long time, boolean local, I msg, ContactId contactId, boolean available, long time,
boolean sent, boolean seen, boolean read); boolean local, boolean sent, boolean seen, boolean read);
protected abstract InvitationMessage createInvitationResponse(MessageId id, protected abstract InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, ContactId contactId, boolean accept, long time, SessionId sessionId, GroupId groupId, ContactId contactId,
boolean local, boolean sent, boolean seen, boolean read); boolean accept, long time, boolean local, boolean sent,
boolean seen, boolean read);
protected abstract ShareableFactory<S, I, IS, SS> getSFactory(); protected abstract ShareableFactory<S, I, IS, SS> getSFactory();
@@ -219,9 +220,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
checkForRaceCondition(txn, f, contact); checkForRaceCondition(txn, f, contact);
// initialize state and process invitation // initialize state and process invitation
IS state = initializeInviteeState(txn, contactId, invitation); IS state = initializeInviteeState(txn, contactId, invitation,
m.getId());
InviteeEngine<IS, IR> engine = InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory()); new InviteeEngine<IS, IR>(getIRFactory(), clock);
processInviteeStateUpdate(txn, m.getId(), processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
trackIncomingMessage(txn, m); trackIncomingMessage(txn, m);
@@ -233,9 +235,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
msg.getType() == SHARE_MSG_TYPE_DECLINE) { msg.getType() == SHARE_MSG_TYPE_DECLINE) {
// we are a sharer who just received a response // we are a sharer who just received a response
SS state = getSessionStateForSharer(txn, sessionId); SS state = getSessionStateForSharer(txn, sessionId);
state.setResponseId(m.getId());
SharerEngine<I, SS, IRR> engine = SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(), new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory()); getIRRFactory(), clock);
processSharerStateUpdate(txn, m.getId(), processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
trackIncomingMessage(txn, m); trackIncomingMessage(txn, m);
@@ -248,14 +251,14 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
SS state = (SS) s; SS state = (SS) s;
SharerEngine<I, SS, IRR> engine = SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(), new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory()); getIRRFactory(), clock);
processSharerStateUpdate(txn, m.getId(), processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} else { } else {
// we are an invitee and the sharer wants to leave or abort // we are an invitee and the sharer wants to leave or abort
IS state = (IS) s; IS state = (IS) s;
InviteeEngine<IS, IR> engine = InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory()); new InviteeEngine<IS, IR>(getIRFactory(), clock);
processInviteeStateUpdate(txn, m.getId(), processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} }
@@ -285,13 +288,15 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
// start engine and process its state update // start engine and process its state update
SharerEngine<I, SS, IRR> engine = SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(), new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory()); getIRRFactory(), clock);
processSharerStateUpdate(txn, null, StateUpdate<SS, BaseMessage> update =
engine.onLocalAction(localState, engine.onLocalAction(localState,
SharerSessionState.Action.LOCAL_INVITATION)); SharerSessionState.Action.LOCAL_INVITATION);
processSharerStateUpdate(txn, null, update);
// track message // track message
long time = clock.currentTimeMillis(); // TODO handle this properly without engine hacks (#376)
long time = update.toSend.get(0).getTime();
trackMessage(txn, localState.getGroupId(), time, true); trackMessage(txn, localState.getGroupId(), time, true);
txn.setComplete(); txn.setComplete();
@@ -321,12 +326,14 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
// start engine and process its state update // start engine and process its state update
InviteeEngine<IS, IR> engine = InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory()); new InviteeEngine<IS, IR>(getIRFactory(), clock);
processInviteeStateUpdate(txn, null, StateUpdate<IS, BaseMessage> update =
engine.onLocalAction(localState, localAction)); engine.onLocalAction(localState, localAction);
processInviteeStateUpdate(txn, null, update);
// track message // track message
long time = clock.currentTimeMillis(); // TODO handle this properly without engine hacks (#376)
long time = update.toSend.get(0).getTime();
trackMessage(txn, localState.getGroupId(), time, true); trackMessage(txn, localState.getGroupId(), time, true);
txn.setComplete(); txn.setComplete();
@@ -387,8 +394,9 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
.from(getIFactory(), group.getId(), d); .from(getIFactory(), group.getId(), d);
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
InvitationMessage im = createInvitationResponse( InvitationMessage im = createInvitationResponse(
m.getKey(), sessionId, contactId, accept, time, m.getKey(), sessionId, group.getId(), contactId,
local, status.isSent(), status.isSeen(), read); accept, time, local, status.isSent(),
status.isSeen(), read);
list.add(im); list.add(im);
} }
else { else {
@@ -555,6 +563,16 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
} }
} }
@Override
public GroupCount getGroupCount(Transaction txn, ContactId contactId)
throws DbException {
Contact contact = db.getContact(txn, contactId);
GroupId groupId = getContactGroup(contact).getId();
return getGroupCount(txn, groupId);
}
void removingShareable(Transaction txn, S f) throws DbException { void removingShareable(Transaction txn, S f) throws DbException {
try { try {
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
@@ -642,7 +660,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
} }
private IS initializeInviteeState(Transaction txn, private IS initializeInviteeState(Transaction txn,
ContactId contactId, I msg) ContactId contactId, I msg, MessageId id)
throws FormatException, DbException { throws FormatException, DbException {
Contact c = db.getContact(txn, contactId); Contact c = db.getContact(txn, contactId);
@@ -656,9 +674,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
Message m = clientHelper.createMessage(localGroup.getId(), now, Message m = clientHelper.createMessage(localGroup.getId(), now,
BdfList.of(mSalt)); BdfList.of(mSalt));
IS s = getISFactory().build(msg.getSessionId(), IS s = getISFactory()
m.getId(), group.getId(), .build(msg.getSessionId(), m.getId(), group.getId(),
InviteeSessionState.State.AWAIT_INVITATION, contactId, f); InviteeSessionState.State.AWAIT_INVITATION, contactId,
f, id);
// save local state to database // save local state to database
BdfDictionary d = s.toBdfDictionary(); BdfDictionary d = s.toBdfDictionary();
@@ -898,19 +917,19 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
byte[] body = clientHelper.toByteArray(m.toBdfList()); byte[] body = clientHelper.toByteArray(m.toBdfList());
Group group = db.getGroup(txn, m.getGroupId()); Group group = db.getGroup(txn, m.getGroupId());
long timestamp = clock.currentTimeMillis();
// add message itself as metadata // add message itself as metadata
BdfDictionary d = m.toBdfDictionary(); BdfDictionary d = m.toBdfDictionary();
d.put(LOCAL, true); d.put(LOCAL, true);
d.put(TIME, timestamp); d.put(TIME, m.getTime());
Metadata meta = metadataEncoder.encode(d); Metadata meta = metadataEncoder.encode(d);
messageQueueManager messageQueueManager
.sendMessage(txn, group, timestamp, body, meta); .sendMessage(txn, group, m.getTime(), body, meta);
} }
private Group getContactGroup(Contact c) { @Override
protected Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(getClientId(), c); return contactGroupFactory.createContactGroup(getClientId(), c);
} }
@@ -930,14 +949,14 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
SharerSessionState.Action.LOCAL_LEAVE; SharerSessionState.Action.LOCAL_LEAVE;
SharerEngine<I, SS, IRR> engine = SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(), new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory()); getIRRFactory(), clock);
processSharerStateUpdate(txn, null, processSharerStateUpdate(txn, null,
engine.onLocalAction((SS) state, action)); engine.onLocalAction((SS) state, action));
} else { } else {
InviteeSessionState.Action action = InviteeSessionState.Action action =
InviteeSessionState.Action.LOCAL_LEAVE; InviteeSessionState.Action.LOCAL_LEAVE;
InviteeEngine<IS, IR> engine = InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory()); new InviteeEngine<IS, IR>(getIRFactory(), clock);
processInviteeStateUpdate(txn, null, processInviteeStateUpdate(txn, null,
engine.onLocalAction((IS) state, action)); engine.onLocalAction((IS) state, action));
} }

View File

@@ -9,6 +9,7 @@ import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import javax.inject.Inject; import javax.inject.Inject;
@@ -52,6 +53,7 @@ public class SharingModule {
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, MessageQueueManager messageQueueManager,
ConversationManager conversationManager,
BlogManager blogManager, BlogManager blogManager,
BlogSharingManagerImpl blogSharingManager) { BlogSharingManagerImpl blogSharingManager) {
@@ -60,6 +62,7 @@ public class SharingModule {
contactManager.registerRemoveContactHook(blogSharingManager); contactManager.registerRemoveContactHook(blogSharingManager);
messageQueueManager.registerIncomingMessageHook( messageQueueManager.registerIncomingMessageHook(
BlogSharingManagerImpl.CLIENT_ID, blogSharingManager); BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
conversationManager.registerConversationClient(blogSharingManager);
blogManager.registerRemoveBlogHook(blogSharingManager); blogManager.registerRemoveBlogHook(blogSharingManager);
return blogSharingManager; return blogSharingManager;
@@ -86,6 +89,7 @@ public class SharingModule {
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager,
ContactManager contactManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, MessageQueueManager messageQueueManager,
ConversationManager conversationManager,
ForumManager forumManager, ForumManager forumManager,
ForumSharingManagerImpl forumSharingManager) { ForumSharingManagerImpl forumSharingManager) {
@@ -94,6 +98,7 @@ public class SharingModule {
contactManager.registerRemoveContactHook(forumSharingManager); contactManager.registerRemoveContactHook(forumSharingManager);
messageQueueManager.registerIncomingMessageHook( messageQueueManager.registerIncomingMessageHook(
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager); ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
conversationManager.registerConversationClient(forumSharingManager);
forumManager.registerRemoveForumHook(forumSharingManager); forumManager.registerRemoveForumHook(forumSharingManager);
return forumSharingManager; return forumSharingManager;

View File

@@ -96,13 +96,13 @@ public class IntroductionManagerImplTest extends BriarTestCase {
TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1) TestUtils.getRandomBytes(MESSAGE_HEADER_LENGTH + 1)
); );
metadataBefore = BdfDictionary.of( metadataBefore = BdfDictionary.of(
new BdfEntry(GROUP_KEY_MSG_COUNT, 41L), new BdfEntry(GROUP_KEY_MSG_COUNT, 41),
new BdfEntry(GROUP_KEY_UNREAD_COUNT, 0L), new BdfEntry(GROUP_KEY_UNREAD_COUNT, 0),
new BdfEntry(GROUP_KEY_LATEST_MSG, 0L) new BdfEntry(GROUP_KEY_LATEST_MSG, 0)
); );
metadataAfter = BdfDictionary.of( metadataAfter = BdfDictionary.of(
new BdfEntry(GROUP_KEY_MSG_COUNT, 42L), new BdfEntry(GROUP_KEY_MSG_COUNT, 42),
new BdfEntry(GROUP_KEY_UNREAD_COUNT, 0L), new BdfEntry(GROUP_KEY_UNREAD_COUNT, 0),
new BdfEntry(GROUP_KEY_LATEST_MSG, time) new BdfEntry(GROUP_KEY_LATEST_MSG, time)
); );
@@ -273,8 +273,8 @@ public class IntroductionManagerImplTest extends BriarTestCase {
final BdfDictionary state = new BdfDictionary(); final BdfDictionary state = new BdfDictionary();
txn = new Transaction(null, false); txn = new Transaction(null, false);
metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 1L); metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 1);
metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 2L); metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 2);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(introduceeManager) oneOf(introduceeManager)
@@ -314,8 +314,8 @@ public class IntroductionManagerImplTest extends BriarTestCase {
txn = new Transaction(null, false); txn = new Transaction(null, false);
metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 41L); metadataBefore.put(GROUP_KEY_UNREAD_COUNT, 41);
metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 42L); metadataAfter.put(GROUP_KEY_UNREAD_COUNT, 42);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId); oneOf(clientHelper).getMessageMetadataAsDictionary(txn, sessionId);