diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java index eafa66098ca47026d91cf04be40e47c756145e49..81b41f99fed3dbc19467fd6ce6e56dc7c6067ee4 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java @@ -34,7 +34,7 @@ import static android.widget.Toast.LENGTH_SHORT; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH; public class ForumActivity extends - ThreadListActivity<Forum, ForumEntry, ForumPostHeader, NestedForumAdapter> { + ThreadListActivity<Forum, ForumItem, ForumPostHeader, NestedForumAdapter> { private static final int REQUEST_FORUM_SHARED = 3; @@ -47,7 +47,7 @@ public class ForumActivity extends } @Override - protected ThreadListController<Forum, ForumEntry, ForumPostHeader> getController() { + protected ThreadListController<Forum, ForumItem, ForumPostHeader> getController() { return forumController; } diff --git a/briar-android/src/org/briarproject/android/forum/ForumController.java b/briar-android/src/org/briarproject/android/forum/ForumController.java index 275ccc135e3344272e961a8b133f738061a9be5f..9b1fba5641b032c862b4fb9ba1ffcf254b773fd6 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumController.java +++ b/briar-android/src/org/briarproject/android/forum/ForumController.java @@ -1,12 +1,10 @@ package org.briarproject.android.forum; -import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.threaded.ThreadListController; -import org.briarproject.api.db.DbException; import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.ForumPostHeader; public interface ForumController - extends ThreadListController<Forum, ForumEntry, ForumPostHeader> { + extends ThreadListController<Forum, ForumItem, ForumPostHeader> { } diff --git a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java index 9ec21e76cfa49adf1da8d1a7b64171b3b6451821..21a19cad4107f1b150323a6cc0b36c26b6662d10 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java +++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java @@ -4,6 +4,7 @@ import android.support.annotation.Nullable; import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.threaded.ThreadListControllerImpl; +import org.briarproject.api.clients.MessageTracker.GroupCount; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.db.DbException; @@ -14,20 +15,20 @@ import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumPost; import org.briarproject.api.forum.ForumPostHeader; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.sync.MessageId; import org.briarproject.util.StringUtils; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.inject.Inject; public class ForumControllerImpl - extends ThreadListControllerImpl<Forum, ForumEntry, ForumPostHeader, ForumPost> + extends ThreadListControllerImpl<Forum, ForumItem, ForumPostHeader, ForumPost> implements ForumController { private static final Logger LOG = @@ -37,12 +38,12 @@ public class ForumControllerImpl @Inject ForumControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, + LifecycleManager lifecycleManager, IdentityManager identityManager, @CryptoExecutor Executor cryptoExecutor, ForumManager forumManager, EventBus eventBus, AndroidNotificationManager notificationManager) { - super(dbExecutor, lifecycleManager, cryptoExecutor, eventBus, - notificationManager); + super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, + eventBus, notificationManager); this.forumManager = forumManager; } @@ -72,7 +73,7 @@ public class ForumControllerImpl } @Override - protected Forum loadGroupItem() throws DbException { + protected Forum loadNamedGroup() throws DbException { return forumManager.getForum(getGroupId()); } @@ -82,18 +83,8 @@ public class ForumControllerImpl } @Override - protected Map<MessageId, String> loadBodies( - Collection<ForumPostHeader> headers) - throws DbException { - Map<MessageId, String> bodies = new HashMap<>(); - for (ForumPostHeader header : headers) { - if (!bodyCache.containsKey(header.getId())) { - String body = StringUtils - .fromUtf8(forumManager.getPostBody(header.getId())); - bodies.put(header.getId(), body); - } - } - return bodies; + protected String loadMessageBody(MessageId id) throws DbException { + return StringUtils.fromUtf8(forumManager.getPostBody(id)); } @Override @@ -102,9 +93,17 @@ public class ForumControllerImpl } @Override - protected ForumPost createLocalMessage(String body, - @Nullable MessageId parentId) throws DbException { - return forumManager.createLocalPost(getGroupId(), body, parentId); + protected long getLatestTimestamp() throws DbException { + GroupCount count = forumManager.getGroupCount(getGroupId()); + return count.getLatestMsgTime(); + } + + @Override + protected ForumPost createLocalMessage(String body, long timestamp, + @Nullable MessageId parentId, LocalAuthor author) { + return forumManager + .createLocalPost(getGroupId(), body, timestamp, parentId, + author); } @Override @@ -114,13 +113,13 @@ public class ForumControllerImpl } @Override - protected void deleteGroupItem(Forum forum) throws DbException { + protected void deleteNamedGroup(Forum forum) throws DbException { forumManager.removeForum(forum); } @Override - protected ForumEntry buildItem(ForumPostHeader header, String body) { - return new ForumEntry(header, body); + protected ForumItem buildItem(ForumPostHeader header, String body) { + return new ForumItem(header, body); } } diff --git a/briar-android/src/org/briarproject/android/forum/ForumEntry.java b/briar-android/src/org/briarproject/android/forum/ForumItem.java similarity index 61% rename from briar-android/src/org/briarproject/android/forum/ForumEntry.java rename to briar-android/src/org/briarproject/android/forum/ForumItem.java index 02817e1ab92bd80870b1b1c18cef4c81106c7a3a..80f28e2d3aaf508e8d0581db1e23cb3d3051efbc 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumEntry.java +++ b/briar-android/src/org/briarproject/android/forum/ForumItem.java @@ -6,15 +6,17 @@ import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author.Status; import org.briarproject.api.sync.MessageId; -/* This class is not thread safe */ -public class ForumEntry extends ThreadItem { +import javax.annotation.concurrent.NotThreadSafe; - ForumEntry(ForumPostHeader h, String text) { - super(h.getId(), h.getParentId(), text, h.getTimestamp(), h.getAuthor(), +@NotThreadSafe +public class ForumItem extends ThreadItem { + + ForumItem(ForumPostHeader h, String body) { + super(h.getId(), h.getParentId(), body, h.getTimestamp(), h.getAuthor(), h.getAuthorStatus(), h.isRead()); } - public ForumEntry(MessageId messageId, MessageId parentId, String text, + public ForumItem(MessageId messageId, MessageId parentId, String text, long timestamp, Author author, Status status) { super(messageId, parentId, text, timestamp, author, status, true); } diff --git a/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java b/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java index d1cb2400838d677716d59695f01c84b51b4eddac..f690df90afea7c5222b737d08afd3cfd9372f349 100644 --- a/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java +++ b/briar-android/src/org/briarproject/android/forum/NestedForumAdapter.java @@ -10,9 +10,9 @@ import org.briarproject.R; import org.briarproject.android.threaded.ThreadItemAdapter; @UiThread -public class NestedForumAdapter extends ThreadItemAdapter<ForumEntry> { +public class NestedForumAdapter extends ThreadItemAdapter<ForumItem> { - public NestedForumAdapter(ThreadItemListener<ForumEntry> listener, + public NestedForumAdapter(ThreadItemListener<ForumItem> listener, LinearLayoutManager layoutManager) { super(listener, layoutManager); } diff --git a/briar-android/src/org/briarproject/android/forum/NestedForumHolder.java b/briar-android/src/org/briarproject/android/forum/NestedForumHolder.java index 8263ccb4aa11b4eda2ab539639c352b79a8d0724..b73558ff5d668c1adf37f0e8e69a9f971411967c 100644 --- a/briar-android/src/org/briarproject/android/forum/NestedForumHolder.java +++ b/briar-android/src/org/briarproject/android/forum/NestedForumHolder.java @@ -4,7 +4,7 @@ import android.view.View; import org.briarproject.android.threaded.ThreadItemViewHolder; -public class NestedForumHolder extends ThreadItemViewHolder<ForumEntry> { +public class NestedForumHolder extends ThreadItemViewHolder<ForumItem> { public NestedForumHolder(View v) { super(v); diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java index dfadc9d414f846b91f6e29435f9444d19703b10a..87bef06bfe692bdd2331836f1c58c6b0a672fde6 100644 --- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java +++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java @@ -4,12 +4,15 @@ import android.support.annotation.Nullable; import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.threaded.ThreadListControllerImpl; +import org.briarproject.api.clients.MessageTracker.GroupCount; import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.db.DbException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.GroupMessageAddedEvent; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.privategroup.GroupMessage; import org.briarproject.api.privategroup.GroupMessageHeader; @@ -18,8 +21,6 @@ import org.briarproject.api.privategroup.PrivateGroupManager; import org.briarproject.api.sync.MessageId; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -36,19 +37,19 @@ public class GroupControllerImpl @Inject GroupControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, + LifecycleManager lifecycleManager, IdentityManager identityManager, @CryptoExecutor Executor cryptoExecutor, PrivateGroupManager privateGroupManager, EventBus eventBus, AndroidNotificationManager notificationManager) { - super(dbExecutor, lifecycleManager, cryptoExecutor, eventBus, - notificationManager); + super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, + eventBus, notificationManager); this.privateGroupManager = privateGroupManager; } @Override public void onActivityResume() { super.onActivityResume(); - notificationManager.clearForumPostNotification(getGroupId()); + // TODO: Add new notification manager methods for private groups } @Override @@ -56,7 +57,7 @@ public class GroupControllerImpl super.eventOccurred(e); if (e instanceof GroupMessageAddedEvent) { - final GroupMessageAddedEvent gmae = (GroupMessageAddedEvent) e; + GroupMessageAddedEvent gmae = (GroupMessageAddedEvent) e; if (!gmae.isLocal() && gmae.getGroupId().equals(getGroupId())) { LOG.info("Group message received, adding..."); final GroupMessageHeader h = gmae.getHeader(); @@ -71,7 +72,7 @@ public class GroupControllerImpl } @Override - protected PrivateGroup loadGroupItem() throws DbException { + protected PrivateGroup loadNamedGroup() throws DbException { return privateGroupManager.getPrivateGroup(getGroupId()); } @@ -81,18 +82,8 @@ public class GroupControllerImpl } @Override - protected Map<MessageId, String> loadBodies( - Collection<GroupMessageHeader> headers) - throws DbException { - Map<MessageId, String> bodies = new HashMap<>(); - for (GroupMessageHeader header : headers) { - if (!bodyCache.containsKey(header.getId())) { - String body = - privateGroupManager.getMessageBody(header.getId()); - bodies.put(header.getId(), body); - } - } - return bodies; + protected String loadMessageBody(MessageId id) throws DbException { + return privateGroupManager.getMessageBody(id); } @Override @@ -101,10 +92,17 @@ public class GroupControllerImpl } @Override - protected GroupMessage createLocalMessage(String body, - @Nullable MessageId parentId) throws DbException { + protected long getLatestTimestamp() throws DbException { + GroupCount count = privateGroupManager.getGroupCount(getGroupId()); + return count.getLatestMsgTime(); + } + + @Override + protected GroupMessage createLocalMessage(String body, long timestamp, + @Nullable MessageId parentId, LocalAuthor author) { return privateGroupManager - .createLocalMessage(getGroupId(), body, parentId); + .createLocalMessage(getGroupId(), body, timestamp, parentId, + author); } @Override @@ -114,7 +112,7 @@ public class GroupControllerImpl } @Override - protected void deleteGroupItem(PrivateGroup group) throws DbException { + protected void deleteNamedGroup(PrivateGroup group) throws DbException { privateGroupManager.removePrivateGroup(group.getId()); } diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java index b29fc227b64b347144df3a1abefa829d4844920c..bae48022a7e8599cf4bbeef00f069ee88ed7651c 100644 --- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java +++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListAdapter.java @@ -31,7 +31,7 @@ class GroupListAdapter extends BriarAdapter<GroupItem, GroupViewHolder> { @Override public void onBindViewHolder(GroupViewHolder ui, int position) { - ui.bindView(ctx, getItemAt(position), listener); + ui.bindView(ctx, items.get(position), listener); } @Override diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java index 9ad7536eb70f6544a47282797050577ae4be63b7..9db84cdcadfa143748c975dce863815075ba0d1c 100644 --- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java +++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java @@ -68,6 +68,7 @@ public class GroupListControllerImpl extends DbControllerImpl throw new IllegalStateException( "GroupListListener needs to be attached"); eventBus.addListener(this); + // TODO: Add new notification manager methods for private groups } @Override diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java index 3994a0a1e57d0a93f3d9849c7862e4b9ffe00e4b..898e81fe29730d6320d88280a71cac31276a8b6e 100644 --- a/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java +++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupViewHolder.java @@ -2,7 +2,6 @@ package org.briarproject.android.privategroup.list; import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; import android.support.v4.app.ActivityOptionsCompat; import android.support.v7.widget.RecyclerView; import android.view.View; @@ -51,7 +50,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder { remove = (Button) v.findViewById(R.id.removeButton); } - void bindView(final Context ctx, @Nullable final GroupItem group, + void bindView(final Context ctx, final GroupItem group, @NotNull final OnGroupRemoveClickListener listener) { if (group == null) return; diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java index 01aae5c68b8f50689b88759e1ba8ee43d71d2a86..129e5348593003ac3e4783b2b43324455ea4ad58 100644 --- a/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java +++ b/briar-android/src/org/briarproject/android/threaded/ThreadItemAdapter.java @@ -9,6 +9,7 @@ import android.support.v7.widget.RecyclerView; import org.briarproject.api.sync.MessageId; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -56,7 +57,7 @@ public abstract class ThreadItemAdapter<I extends ThreadItem> return replyItem; } - public void setItems(List<I> items) { + public void setItems(Collection<I> items) { this.items.clear(); this.items.addAll(items); notifyDataSetChanged(); diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadItemViewHolder.java b/briar-android/src/org/briarproject/android/threaded/ThreadItemViewHolder.java index 27ca9d80dce9ae5f775848387ff55a2f5e236e62..0b5a5ddc5b74cc34bee5bb6d63b2534ce2aa0227 100644 --- a/briar-android/src/org/briarproject/android/threaded/ThreadItemViewHolder.java +++ b/briar-android/src/org/briarproject/android/threaded/ThreadItemViewHolder.java @@ -57,7 +57,7 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem> // TODO improve encapsulation, so we don't need to pass the adapter here public void bind(final ThreadItemAdapter<I> adapter, - final ThreadItemListener listener, final I item, int pos) { + final ThreadItemListener<I> listener, final I item, int pos) { textView.setText(StringUtils.trim(item.getText())); diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java index ab480a4bd04f84f0cc51517b6412d6e81518191a..889399525de6ab9a880511dd43789533d65b835d 100644 --- a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java +++ b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java @@ -26,10 +26,9 @@ import org.briarproject.api.clients.PostHeader; import org.briarproject.api.db.DbException; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; +import org.briarproject.util.StringUtils; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import static android.support.design.widget.Snackbar.make; import static android.view.View.GONE; @@ -75,7 +74,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI list.setAdapter(adapter); if (state != null) { - replyId = new MessageId(state.getByteArray(KEY_REPLY_ID)); + byte[] replyIdBytes = state.getByteArray(KEY_REPLY_ID); + if(replyIdBytes != null) replyId = new MessageId(replyIdBytes); } loadItems(); @@ -110,9 +110,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI new UiResultExceptionHandler<Collection<I>, DbException>( this) { @Override - public void onResultUi(Collection<I> result) { - // FIXME What's the benefit of copying the collection? - List<I> items = new ArrayList<>(result); + public void onResultUi(Collection<I> items) { if (items.isEmpty()) { list.showData(); } else { @@ -225,7 +223,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI public void onSendClick(String text) { if (text.trim().length() == 0) return; - if (text.length() > getMaxBodyLength()) { + if (StringUtils.isTooLong(text, getMaxBodyLength())) { displaySnackbarShort(R.string.text_too_long); return; } @@ -243,12 +241,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI finish(); } }; - if (replyItem == null) { - // root post - getController().send(text, handler); - } else { - getController().send(text, replyItem.getId(), handler); - } + getController().createAndStoreMessage(text, + replyItem != null ? replyItem.getId() : null, handler); textInput.hideSoftKeyboard(); textInput.setVisibility(GONE); adapter.setReplyItem(null); @@ -268,6 +262,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI @Override public void onExceptionUi(DbException exception) { // TODO add proper exception handling + finish(); } }); } diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListController.java b/briar-android/src/org/briarproject/android/threaded/ThreadListController.java index b62b6fd27788a2e7009f8b3a1eb59e443abb80b6..a7731d8d48ed2046f39c962e8781b86c4c9c396d 100644 --- a/briar-android/src/org/briarproject/android/threaded/ThreadListController.java +++ b/briar-android/src/org/briarproject/android/threaded/ThreadListController.java @@ -29,9 +29,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem void markItemsRead(Collection<I> items); - void send(String body, ResultExceptionHandler<I, DbException> handler); - - void send(String body, @Nullable MessageId parentId, + void createAndStoreMessage(String body, @Nullable MessageId parentId, ResultExceptionHandler<I, DbException> handler); void deleteNamedGroup(ResultExceptionHandler<Void, DbException> handler); diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java index c412d2eaacfd11c435a13b8623544baf257d017a..02e6bfc66f1bdb7446cd1893d1eb7c6d22398df1 100644 --- a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java +++ b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java @@ -17,6 +17,8 @@ import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; import org.briarproject.api.event.GroupRemovedEvent; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; @@ -40,11 +42,12 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T private static final Logger LOG = Logger.getLogger(ThreadListControllerImpl.class.getName()); - protected final Executor cryptoExecutor; + private final IdentityManager identityManager; + private final Executor cryptoExecutor; protected final AndroidNotificationManager notificationManager; private final EventBus eventBus; - protected final Map<MessageId, String> bodyCache = + private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>(); private volatile GroupId groupId; @@ -52,10 +55,11 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T protected ThreadListListener<H> listener; protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, + LifecycleManager lifecycleManager, IdentityManager identityManager, @CryptoExecutor Executor cryptoExecutor, EventBus eventBus, AndroidNotificationManager notificationManager) { super(dbExecutor, lifecycleManager); + this.identityManager = identityManager; this.cryptoExecutor = cryptoExecutor; this.eventBus = eventBus; this.notificationManager = notificationManager; @@ -76,15 +80,14 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T @CallSuper @Override public void onActivityResume() { - checkGroupId(); - notificationManager.blockNotification(groupId); + notificationManager.blockNotification(getGroupId()); eventBus.addListener(this); } @CallSuper @Override public void onActivityPause() { - notificationManager.unblockNotification(groupId); + notificationManager.unblockNotification(getGroupId()); eventBus.removeListener(this); } @@ -97,7 +100,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T public void eventOccurred(Event e) { if (e instanceof GroupRemovedEvent) { GroupRemovedEvent s = (GroupRemovedEvent) e; - if (s.getGroup().getId().equals(groupId)) { + if (s.getGroup().getId().equals(getGroupId())) { LOG.info("Group removed"); listener.runOnUiThreadUnlessDestroyed(new Runnable() { @Override @@ -118,7 +121,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T public void run() { try { long now = System.currentTimeMillis(); - G groupItem = loadGroupItem(); + G groupItem = loadNamedGroup(); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info( @@ -134,7 +137,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T } @DatabaseExecutor - protected abstract G loadGroupItem() throws DbException; + protected abstract G loadNamedGroup() throws DbException; @Override public void loadItems( @@ -152,10 +155,14 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T if (LOG.isLoggable(INFO)) LOG.info("Loading headers took " + duration + " ms"); - // Load bodies + // Load bodies into cache now = System.currentTimeMillis(); - Map<MessageId, String> bodies = loadBodies(headers); - bodyCache.putAll(bodies); + for (H header : headers) { + if (!bodyCache.containsKey(header.getId())) { + bodyCache.put(header.getId(), + loadMessageBody(header.getId())); + } + } duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Loading bodies took " + duration + " ms"); @@ -175,8 +182,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T protected abstract Collection<H> loadHeaders() throws DbException; @DatabaseExecutor - protected abstract Map<MessageId, String> loadBodies(Collection<H> headers) - throws DbException; + protected abstract String loadMessageBody(MessageId id) throws DbException; @Override public void loadItem(final H header, @@ -186,9 +192,13 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T public void run() { LOG.info("Loading item..."); try { - String body = loadBodies(Collections.singletonList(header)) - .get(header.getId()); - bodyCache.put(header.getId(), body); + String body; + if (!bodyCache.containsKey(header.getId())) { + body = loadMessageBody(header.getId()); + bodyCache.put(header.getId(), body); + } else { + body = bodyCache.get(header.getId()); + } I item = buildItem(header, body); handler.onResult(item); } catch (DbException e) { @@ -230,21 +240,19 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T protected abstract void markRead(MessageId id) throws DbException; @Override - public void send(String body, - ResultExceptionHandler<I, DbException> resultHandler) { - send(body, null, resultHandler); - } - - @Override - public void send(final String body, @Nullable final MessageId parentId, + public void createAndStoreMessage(final String body, + @Nullable final MessageId parentId, final ResultExceptionHandler<I, DbException> handler) { - cryptoExecutor.execute(new Runnable() { + runOnDbThread(new Runnable() { @Override public void run() { - LOG.info("Creating message..."); try { - M msg = createLocalMessage(body, parentId); - storePost(msg, body, handler); + LocalAuthor author = identityManager.getLocalAuthor(); + long timestamp = getLatestTimestamp(); + timestamp = + Math.max(timestamp, System.currentTimeMillis()); + createMessage(body, timestamp, parentId, author, + handler); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -255,8 +263,24 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T } @DatabaseExecutor - protected abstract M createLocalMessage(String body, - @Nullable MessageId parentId) throws DbException; + protected abstract long getLatestTimestamp() throws DbException; + + private void createMessage(final String body, final long timestamp, + final @Nullable MessageId parentId, final LocalAuthor author, + final ResultExceptionHandler<I, DbException> handler) { + cryptoExecutor.execute(new Runnable() { + @Override + public void run() { + LOG.info("Creating message..."); + M msg = createLocalMessage(body, timestamp, parentId, author); + storePost(msg, body, handler); + } + }); + } + + @CryptoExecutor + protected abstract M createLocalMessage(String body, long timestamp, + @Nullable MessageId parentId, LocalAuthor author); private void storePost(final M msg, final String body, final ResultExceptionHandler<I, DbException> resultHandler) { @@ -292,8 +316,8 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T public void run() { try { long now = System.currentTimeMillis(); - G groupItem = loadGroupItem(); - deleteGroupItem(groupItem); + G groupItem = loadNamedGroup(); + deleteNamedGroup(groupItem); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Removing group took " + duration + " ms"); @@ -309,7 +333,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T } @DatabaseExecutor - protected abstract void deleteGroupItem(G groupItem) throws DbException; + protected abstract void deleteNamedGroup(G groupItem) throws DbException; private List<I> buildItems(Collection<H> headers) { List<I> entries = new ArrayList<>(); diff --git a/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java b/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java index 4a7fcf02e0b766b20d601c8f4f1478ea5361f238..e6290e9716af40673203e0cfba28027469a8b249 100644 --- a/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java +++ b/briar-android/test/java/org/briarproject/android/forum/ForumActivityTest.java @@ -11,14 +11,12 @@ import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.api.db.DbException; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorId; -import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricGradleTestRunner; @@ -82,7 +80,7 @@ public class ForumActivityTest { private TestForumActivity forumActivity; @Captor - private ArgumentCaptor<UiResultExceptionHandler<Collection<ForumEntry>, DbException>> + private ArgumentCaptor<UiResultExceptionHandler<Collection<ForumItem>, DbException>> rc; @Before @@ -94,14 +92,14 @@ public class ForumActivityTest { .withIntent(intent).create().resume().get(); } - private List<ForumEntry> getDummyData() { - ForumEntry[] forumEntries = new ForumEntry[6]; + private List<ForumItem> getDummyData() { + ForumItem[] forumEntries = new ForumItem[6]; for (int i = 0; i < forumEntries.length; i++) { AuthorId authorId = new AuthorId(TestUtils.getRandomId()); byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); Author author = new Author(authorId, AUTHORS[i], publicKey); forumEntries[i] = - new ForumEntry(AUTHOR_IDS[i], PARENT_AUTHOR_IDS[i], + new ForumItem(AUTHOR_IDS[i], PARENT_AUTHOR_IDS[i], AUTHORS[i], System.currentTimeMillis(), author, UNKNOWN); forumEntries[i].setLevel(LEVELS[i]); @@ -112,7 +110,7 @@ public class ForumActivityTest { @Test public void testNestedEntries() { ForumController mc = forumActivity.getController(); - List<ForumEntry> dummyData = getDummyData(); + List<ForumItem> dummyData = getDummyData(); verify(mc, times(1)).loadItems(rc.capture()); rc.getValue().onResult(dummyData); NestedForumAdapter adapter = forumActivity.getAdapter(); diff --git a/briar-api/src/org/briarproject/api/blogs/Blog.java b/briar-api/src/org/briarproject/api/blogs/Blog.java index 14bfbd97bdb883ef97db58ef34a88b3132e6b7db..153eb6e27ccef2ccf6ca0d73d6aae7935f9287df 100644 --- a/briar-api/src/org/briarproject/api/blogs/Blog.java +++ b/briar-api/src/org/briarproject/api/blogs/Blog.java @@ -7,9 +7,9 @@ import org.briarproject.api.sharing.Shareable; import org.briarproject.api.sync.Group; import org.jetbrains.annotations.NotNull; -import javax.annotation.concurrent.ThreadSafe; +import javax.annotation.concurrent.Immutable; -@ThreadSafe +@Immutable @NotNullByDefault public class Blog extends BaseGroup implements Shareable { diff --git a/briar-api/src/org/briarproject/api/clients/BaseGroup.java b/briar-api/src/org/briarproject/api/clients/BaseGroup.java index d2e1e8428a4a6442fd91f35d323f009f29021968..f101106e38c1d836e3f1c0d1366e867bdcebb924 100644 --- a/briar-api/src/org/briarproject/api/clients/BaseGroup.java +++ b/briar-api/src/org/briarproject/api/clients/BaseGroup.java @@ -5,9 +5,9 @@ import org.briarproject.api.sync.Group; import org.briarproject.api.sync.GroupId; import org.jetbrains.annotations.NotNull; -import javax.annotation.concurrent.ThreadSafe; +import javax.annotation.concurrent.Immutable; -@ThreadSafe +@Immutable @NotNullByDefault public abstract class BaseGroup { diff --git a/briar-api/src/org/briarproject/api/clients/NamedGroup.java b/briar-api/src/org/briarproject/api/clients/NamedGroup.java index c41429e7aca8f33963a68a0ba4db63c39d49225d..f4b95fdc5cd5a8ced6eb8f18fe9f1b2540d4ce12 100644 --- a/briar-api/src/org/briarproject/api/clients/NamedGroup.java +++ b/briar-api/src/org/briarproject/api/clients/NamedGroup.java @@ -4,9 +4,9 @@ import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.sync.Group; import org.jetbrains.annotations.NotNull; -import javax.annotation.concurrent.ThreadSafe; +import javax.annotation.concurrent.Immutable; -@ThreadSafe +@Immutable @NotNullByDefault public abstract class NamedGroup extends BaseGroup { diff --git a/briar-api/src/org/briarproject/api/forum/Forum.java b/briar-api/src/org/briarproject/api/forum/Forum.java index 3cbf297a07792509062813248ff34cb4bd9a8873..f73cd049afff86ea54c15bb905b9dd9bb4ccdd0f 100644 --- a/briar-api/src/org/briarproject/api/forum/Forum.java +++ b/briar-api/src/org/briarproject/api/forum/Forum.java @@ -5,9 +5,9 @@ import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.sharing.Shareable; import org.briarproject.api.sync.Group; -import javax.annotation.concurrent.ThreadSafe; +import javax.annotation.concurrent.Immutable; -@ThreadSafe +@Immutable @NotNullByDefault public class Forum extends NamedGroup implements Shareable { diff --git a/briar-api/src/org/briarproject/api/forum/ForumManager.java b/briar-api/src/org/briarproject/api/forum/ForumManager.java index edfd53f5508580907090f0944bc380fea4515651..7561fc9636ab1a0544ee8d4212c00a47d3067c50 100644 --- a/briar-api/src/org/briarproject/api/forum/ForumManager.java +++ b/briar-api/src/org/briarproject/api/forum/ForumManager.java @@ -1,8 +1,10 @@ package org.briarproject.api.forum; import org.briarproject.api.clients.MessageTracker; +import org.briarproject.api.crypto.CryptoExecutor; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Transaction; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; @@ -22,8 +24,9 @@ public interface ForumManager extends MessageTracker { void removeForum(Forum f) throws DbException; /** Creates a local forum post. */ - ForumPost createLocalPost(GroupId groupId, String text, - @Nullable MessageId parentId) throws DbException; + @CryptoExecutor + ForumPost createLocalPost(GroupId groupId, String body, long timestamp, + @Nullable MessageId parentId, LocalAuthor author); /** Stores a local forum post. */ ForumPostHeader addLocalPost(ForumPost p) throws DbException; diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java index 3141ab40b7af64a8a8bc73622766576ff4a06e95..310611361b960acc44ef6b5e9f6d30207f488360 100644 --- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java +++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java @@ -6,9 +6,9 @@ import org.briarproject.api.nullsafety.NotNullByDefault; import org.briarproject.api.sync.Group; import org.jetbrains.annotations.NotNull; -import javax.annotation.concurrent.ThreadSafe; +import javax.annotation.concurrent.Immutable; -@ThreadSafe +@Immutable @NotNullByDefault public class PrivateGroup extends NamedGroup { diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java index 07ef1c002217399f893d71e03a6e15d6c776c65e..956a42344649b2d88cdbf0924d4c5ab1cda0986d 100644 --- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java +++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java @@ -3,6 +3,7 @@ package org.briarproject.api.privategroup; import org.briarproject.api.clients.MessageTracker; import org.briarproject.api.db.DbException; import org.briarproject.api.db.Transaction; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; @@ -21,8 +22,8 @@ public interface PrivateGroupManager extends MessageTracker { void removePrivateGroup(GroupId g) throws DbException; /** Creates a local group message. */ - GroupMessage createLocalMessage(GroupId groupId, String text, - @Nullable MessageId parentId) throws DbException; + GroupMessage createLocalMessage(GroupId groupId, String body, + long timestamp, @Nullable MessageId parentId, LocalAuthor author); /** Stores (and sends) a local group message. */ GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException; diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java index 4983385c0a956514da6ebd08b7a13de43affd75e..8d1511f7d8a8e2665fe4c5781d9c1e95b91a4542 100644 --- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java +++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java @@ -25,7 +25,6 @@ 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.system.Clock; import org.briarproject.clients.BdfIncomingMessageHook; import org.briarproject.util.StringUtils; import org.jetbrains.annotations.Nullable; @@ -64,20 +63,17 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { private final IdentityManager identityManager; private final ForumFactory forumFactory; private final ForumPostFactory forumPostFactory; - private final Clock clock; private final List<RemoveForumHook> removeHooks; @Inject ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager, ClientHelper clientHelper, MetadataParser metadataParser, - ForumFactory forumFactory, ForumPostFactory forumPostFactory, - Clock clock) { + ForumFactory forumFactory, ForumPostFactory forumPostFactory) { super(db, clientHelper, metadataParser); this.identityManager = identityManager; this.forumFactory = forumFactory; this.forumPostFactory = forumPostFactory; - this.clock = clock; removeHooks = new CopyOnWriteArrayList<RemoveForumHook>(); } @@ -129,23 +125,9 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager { } @Override - public ForumPost createLocalPost(final GroupId groupId, - final String body, final @Nullable MessageId parentId) - throws DbException { - - LocalAuthor author; - GroupCount count; - Transaction txn = db.startTransaction(true); - try { - author = identityManager.getLocalAuthor(txn); - count = getGroupCount(txn, groupId); - txn.setComplete(); - } finally { - db.endTransaction(txn); - } - long timestamp = clock.currentTimeMillis(); - timestamp = Math.max(timestamp, count.getLatestMsgTime()); - + public ForumPost createLocalPost(final GroupId groupId, final String body, + final long timestamp, final @Nullable MessageId parentId, + final LocalAuthor author) { ForumPost p; try { p = forumPostFactory diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java index b0ef7e072db3382199f5d99fad503c12f0a7df4f..6f40bd9c6c9c069c4e9de80d9cbfa7c887626b53 100644 --- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java +++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java @@ -77,16 +77,13 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements @Override public GroupMessage createLocalMessage(GroupId groupId, String body, - @Nullable MessageId parentId) throws DbException { - - long timestamp = clock.currentTimeMillis(); - LocalAuthor author = identityManager.getLocalAuthor(); + long timestamp, @Nullable MessageId parentId, LocalAuthor author) { try { return groupMessageFactory .createGroupMessage(groupId, timestamp, parentId, author, body); } catch (FormatException e) { - throw new DbException(e); + throw new RuntimeException(e); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } diff --git a/briar-core/src/org/briarproject/util/StringUtils.java b/briar-core/src/org/briarproject/util/StringUtils.java index f8a97a6d635bd403c59eb9ea659decdde3c68f5a..ec43eebf75e42e3d8bb480eee52f55b2e3b098dd 100644 --- a/briar-core/src/org/briarproject/util/StringUtils.java +++ b/briar-core/src/org/briarproject/util/StringUtils.java @@ -86,4 +86,11 @@ public class StringUtils { public static String trim(String s) { return s.trim(); } + + /** + * Returns true if the string is longer than maxLength + */ + public static boolean isTooLong(String s, int maxLength) { + return toUtf8(s).length > maxLength; + } }