diff --git a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java index dc1628088..1475608ec 100644 --- a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java @@ -12,6 +12,7 @@ import android.widget.TextView; import org.briarproject.R; import org.briarproject.api.contact.ContactId; import org.briarproject.api.identity.Author; +import org.briarproject.api.sync.GroupId; import org.briarproject.util.StringUtils; import java.util.List; @@ -67,7 +68,7 @@ public abstract class BaseContactListAdapter contacts) { this.contacts.addAll(contacts); } @@ -130,7 +140,7 @@ public abstract class BaseContactListAdapter { @Override diff --git a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java index 1c2951b40..63d978ff9 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java @@ -68,11 +68,11 @@ public class ContactListAdapter extends BaseContactListAdapter.BaseContactHolder { public final ImageView bulb; - public final TextView unread; + final TextView unread; public final TextView date; public final TextView identity; - public ContactHolder(View v) { + ContactHolder(View v) { super(v); bulb = (ImageView) v.findViewById(R.id.bulbView); diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java index 7b21f28ac..d0514a172 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java @@ -31,6 +31,7 @@ import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; import org.briarproject.api.event.MessageStateChangedEvent; +import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.forum.ForumInvitationMessage; import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.identity.IdentityManager; @@ -231,12 +232,16 @@ public class ContactListFragment extends BaseFragment implements EventListener { } else if (e instanceof ContactRemovedEvent) { LOG.info("Contact removed"); removeItem(((ContactRemovedEvent) e).getContactId()); + } else if (e instanceof PrivateMessageReceivedEvent) { + LOG.info("Message received, update contact"); + PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e; + PrivateMessageHeader h = p.getMessageHeader(); + updateItem(p.getGroupId(), ConversationItem.from(h)); } else if (e instanceof MessageStateChangedEvent) { MessageStateChangedEvent m = (MessageStateChangedEvent) e; ClientId c = m.getClientId(); if (m.getState() == DELIVERED && - (c.equals(messagingManager.getClientId()) || - c.equals(introductionManager.getClientId()) || + (c.equals(introductionManager.getClientId()) || c.equals(forumSharingManager.getClientId()))) { LOG.info("Message added, reloading"); reloadConversation(m.getMessage().getGroupId()); @@ -278,6 +283,20 @@ public class ContactListFragment extends BaseFragment implements EventListener { }); } + private void updateItem(final GroupId g, final ConversationItem m) { + listener.runOnUiThread(new Runnable() { + @Override + public void run() { + int position = adapter.findItemPosition(g); + ContactListItem item = adapter.getItem(position); + if (item != null) { + item.addMessage(m); + adapter.updateItem(position, item); + } + } + }); + } + private void removeItem(final ContactId c) { listener.runOnUiThread(new Runnable() { @Override diff --git a/briar-android/src/org/briarproject/android/contact/ContactListItem.java b/briar-android/src/org/briarproject/android/contact/ContactListItem.java index ab43b1ed5..e436d4a9b 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListItem.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListItem.java @@ -35,13 +35,21 @@ public class ContactListItem { unread = 0; if (!empty) { for (ConversationItem i : messages) { - if (i.getTime() > timestamp) timestamp = i.getTime(); - if (i instanceof IncomingItem && !((IncomingItem) i).isRead()) - unread++; + addMessage(i); } } } + void addMessage(ConversationItem message) { + empty = empty && message == null; + if (message != null) { + if (message.getTime() > timestamp) timestamp = message.getTime(); + if (message instanceof IncomingItem && + !((IncomingItem) message).isRead()) + unread++; + } + } + public Contact getContact() { return contact; } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index f91d06440..b1507615a 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -47,9 +47,9 @@ import org.briarproject.api.event.EventListener; import org.briarproject.api.event.ForumInvitationReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent; -import org.briarproject.api.event.MessageStateChangedEvent; import org.briarproject.api.event.MessagesAckedEvent; import org.briarproject.api.event.MessagesSentEvent; +import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.forum.ForumInvitationMessage; import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.introduction.IntroductionManager; @@ -62,7 +62,6 @@ import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.sync.GroupId; -import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; import org.briarproject.util.StringUtils; @@ -87,7 +86,6 @@ import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.contact.ConversationItem.IncomingItem; import static org.briarproject.android.contact.ConversationItem.OutgoingItem; -import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; public class ConversationActivity extends BriarActivity implements EventListener, OnClickListener, @@ -97,14 +95,13 @@ public class ConversationActivity extends BriarActivity Logger.getLogger(ConversationActivity.class.getName()); @Inject - protected AndroidNotificationManager notificationManager; + AndroidNotificationManager notificationManager; @Inject protected ConnectionRegistry connectionRegistry; @Inject @CryptoExecutor protected Executor cryptoExecutor; - private Map bodyCache = new HashMap<>(); private ConversationAdapter adapter; private CircleImageView toolbarAvatar; private ImageView toolbarStatus; @@ -121,7 +118,7 @@ public class ConversationActivity extends BriarActivity @Inject protected volatile EventBus eventBus; @Inject - protected volatile PrivateMessageFactory privateMessageFactory; + volatile PrivateMessageFactory privateMessageFactory; @Inject protected volatile IntroductionManager introductionManager; @Inject @@ -132,6 +129,7 @@ public class ConversationActivity extends BriarActivity private volatile String contactName = null; private volatile byte[] contactIdenticonKey = null; private volatile boolean connected = false; + private volatile Map bodyCache = new HashMap<>(); @Override public void onCreate(Bundle state) { @@ -146,10 +144,13 @@ public class ConversationActivity extends BriarActivity // Custom Toolbar Toolbar tb = (Toolbar) findViewById(R.id.toolbar); - toolbarAvatar = (CircleImageView) tb.findViewById(R.id.contactAvatar); - toolbarStatus = (ImageView) tb.findViewById(R.id.contactStatus); - toolbarTitle = (TextView) tb.findViewById(R.id.contactName); - setSupportActionBar(tb); + if (tb != null) { + toolbarAvatar = + (CircleImageView) tb.findViewById(R.id.contactAvatar); + toolbarStatus = (ImageView) tb.findViewById(R.id.contactStatus); + toolbarTitle = (TextView) tb.findViewById(R.id.contactName); + setSupportActionBar(tb); + } ActionBar ab = getSupportActionBar(); if (ab != null) { ab.setDisplayShowHomeEnabled(true); @@ -170,8 +171,11 @@ public class ConversationActivity extends BriarActivity content = (EditText) findViewById(R.id.contentView); sendButton = (ImageButton) findViewById(R.id.sendButton); - sendButton.setEnabled(false); // Enabled after loading the conversation - sendButton.setOnClickListener(this); + if (sendButton != null) { + // Enabled after loading the conversation + sendButton.setEnabled(false); + sendButton.setOnClickListener(this); + } } @Override @@ -235,7 +239,7 @@ public class ConversationActivity extends BriarActivity @Override public void onBackPressed() { - // FIXME disabled exit transition, because it doesn't work for some reason + // FIXME disabled exit transition, because it doesn't work for some reason #318 //supportFinishAfterTransition(); finish(); } @@ -416,7 +420,7 @@ public class ConversationActivity extends BriarActivity }); } - private void addIntroduction(final ConversationItem item) { + private void addConversationItem(final ConversationItem item) { runOnUiThread(new Runnable() { @Override public void run() { @@ -469,14 +473,14 @@ public class ConversationActivity extends BriarActivity LOG.info("Contact removed"); finishOnUiThread(); } - } else if (e instanceof MessageStateChangedEvent) { - MessageStateChangedEvent m = (MessageStateChangedEvent) e; - if (m.getState() == DELIVERED && - m.getMessage().getGroupId().equals(groupId)) { - LOG.info("Message added, reloading"); - // Mark new incoming messages as read directly - if (m.isLocal()) loadMessages(); - else markMessageReadIfNew(m.getMessage()); + } else if (e instanceof PrivateMessageReceivedEvent) { + PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e; + if (p.getGroupId().equals(groupId)) { + LOG.info("Message received, adding"); + PrivateMessageHeader h = p.getMessageHeader(); + addConversationItem(ConversationItem.from(h)); + loadMessageBody(h); + markMessageReadIfNew(h); } } else if (e instanceof MessagesSentEvent) { MessagesSentEvent m = (MessagesSentEvent) e; @@ -510,7 +514,7 @@ public class ConversationActivity extends BriarActivity if (event.getContactId().equals(contactId)) { IntroductionRequest ir = event.getIntroductionRequest(); ConversationItem item = new ConversationIntroductionInItem(ir); - addIntroduction(item); + addConversationItem(item); } } else if (e instanceof IntroductionResponseReceivedEvent) { IntroductionResponseReceivedEvent event = @@ -519,7 +523,7 @@ public class ConversationActivity extends BriarActivity IntroductionResponse ir = event.getIntroductionResponse(); ConversationItem item = ConversationItem.from(this, contactName, ir); - addIntroduction(item); + addConversationItem(item); } } else if (e instanceof ForumInvitationReceivedEvent) { ForumInvitationReceivedEvent event = @@ -530,7 +534,7 @@ public class ConversationActivity extends BriarActivity } } - private void markMessageReadIfNew(final Message m) { + private void markMessageReadIfNew(final PrivateMessageHeader h) { runOnUiThread(new Runnable() { @Override public void run() { @@ -538,23 +542,23 @@ public class ConversationActivity extends BriarActivity if (item != null) { // Mark the message read if it's the newest message long lastMsgTime = item.getTime(); - long newMsgTime = m.getTimestamp(); - if (newMsgTime > lastMsgTime) markNewMessageRead(m); + long newMsgTime = h.getTimestamp(); + if (newMsgTime > lastMsgTime) markNewMessageRead(h.getId()); else loadMessages(); } else { // mark the message as read as well if it is the first one - markNewMessageRead(m); + markNewMessageRead(h.getId()); } } }); } - private void markNewMessageRead(final Message m) { + private void markNewMessageRead(final MessageId m) { runOnDbThread(new Runnable() { @Override public void run() { try { - messagingManager.setReadFlag(m.getId(), true); + messagingManager.setReadFlag(m, true); loadMessages(); } catch (DbException e) { if (LOG.isLoggable(WARNING)) @@ -605,8 +609,9 @@ public class ConversationActivity extends BriarActivity @Override public void run() { try { - storeMessage(privateMessageFactory.createPrivateMessage( - groupId, timestamp, null, "text/plain", body)); + storeMessage(privateMessageFactory + .createPrivateMessage(groupId, timestamp, null, + "text/plain", body), body); } catch (FormatException e) { throw new RuntimeException(e); } @@ -614,7 +619,7 @@ public class ConversationActivity extends BriarActivity }); } - private void storeMessage(final PrivateMessage m) { + private void storeMessage(final PrivateMessage m, final byte[] body) { runOnDbThread(new Runnable() { @Override public void run() { @@ -624,6 +629,16 @@ public class ConversationActivity extends BriarActivity long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Storing message took " + duration + " ms"); + + PrivateMessageHeader h = new PrivateMessageHeader( + m.getMessage().getId(), + m.getMessage().getTimestamp(), m.getContentType(), + true, false, false, false); + ConversationMessageItem item = + (ConversationMessageItem) ConversationItem.from(h); + item.setBody(body); + bodyCache.put(m.getMessage().getId(), body); + addConversationItem(item); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); diff --git a/briar-api/src/org/briarproject/api/event/PrivateMessageReceivedEvent.java b/briar-api/src/org/briarproject/api/event/PrivateMessageReceivedEvent.java new file mode 100644 index 000000000..04f7f9164 --- /dev/null +++ b/briar-api/src/org/briarproject/api/event/PrivateMessageReceivedEvent.java @@ -0,0 +1,27 @@ +package org.briarproject.api.event; + +import org.briarproject.api.messaging.PrivateMessageHeader; +import org.briarproject.api.sync.GroupId; + +/** + * An event that is broadcast when a new private message was received. + */ +public class PrivateMessageReceivedEvent extends Event { + + private final PrivateMessageHeader messageHeader; + private final GroupId groupId; + + public PrivateMessageReceivedEvent(PrivateMessageHeader messageHeader, + GroupId groupId) { + this.messageHeader = messageHeader; + this.groupId = groupId; + } + + public PrivateMessageHeader getMessageHeader() { + return messageHeader; + } + + public GroupId getGroupId() { + return groupId; + } +} diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java index 93a0cc979..052df83c3 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java +++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java @@ -10,17 +10,21 @@ import org.briarproject.api.contact.ContactManager.AddContactHook; import org.briarproject.api.contact.ContactManager.RemoveContactHook; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; +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.event.PrivateMessageReceivedEvent; import org.briarproject.api.messaging.MessagingManager; import org.briarproject.api.messaging.PrivateMessage; import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.Group; import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.Message; import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageStatus; +import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.util.StringUtils; import java.util.ArrayList; @@ -29,22 +33,23 @@ import java.util.Map; import javax.inject.Inject; -class MessagingManagerImpl implements MessagingManager, Client, AddContactHook, - RemoveContactHook { +class MessagingManagerImpl extends BdfIncomingMessageHook + implements MessagingManager, Client, AddContactHook, RemoveContactHook { static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( "6bcdc006c0910b0f44e40644c3b31f1a" + "8bf9a6d6021d40d219c86b731b903070")); private final DatabaseComponent db; - private final ClientHelper clientHelper; private final PrivateGroupFactory privateGroupFactory; @Inject MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, + MetadataParser metadataParser, PrivateGroupFactory privateGroupFactory) { + super(clientHelper, metadataParser); + this.db = db; - this.clientHelper = clientHelper; this.privateGroupFactory = privateGroupFactory; } @@ -87,6 +92,22 @@ class MessagingManagerImpl implements MessagingManager, Client, AddContactHook, return CLIENT_ID; } + @Override + protected void incomingMessage(Transaction txn, Message m, BdfList body, + BdfDictionary meta) throws DbException, FormatException { + + GroupId groupId = m.getGroupId(); + long timestamp = meta.getLong("timestamp"); + String contentType = meta.getString("contentType"); + boolean local = meta.getBoolean("local"); + boolean read = meta.getBoolean("read"); + PrivateMessageHeader header = new PrivateMessageHeader( + m.getId(), timestamp, contentType, local, read, false, false); + PrivateMessageReceivedEvent event = new PrivateMessageReceivedEvent( + header, groupId); + txn.attach(event); + } + @Override public void addLocalMessage(PrivateMessage m) throws DbException { try { diff --git a/briar-core/src/org/briarproject/messaging/MessagingModule.java b/briar-core/src/org/briarproject/messaging/MessagingModule.java index 8fec2d9b6..d22782cf4 100644 --- a/briar-core/src/org/briarproject/messaging/MessagingModule.java +++ b/briar-core/src/org/briarproject/messaging/MessagingModule.java @@ -45,11 +45,13 @@ public class MessagingModule { @Provides @Singleton MessagingManager getMessagingManager(LifecycleManager lifecycleManager, - ContactManager contactManager, + ContactManager contactManager, ValidationManager validationManager, MessagingManagerImpl messagingManager) { lifecycleManager.registerClient(messagingManager); contactManager.registerAddContactHook(messagingManager); contactManager.registerRemoveContactHook(messagingManager); + validationManager + .registerIncomingMessageHook(CLIENT_ID, messagingManager); return messagingManager; } }