mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 05:39:53 +01:00
Merge branch '1024-message-tree-npe' into 'master'
Don't add threaded messages to the UI before their parents Closes #1024 See merge request !585
This commit is contained in:
@@ -28,7 +28,6 @@ import org.briarproject.briar.android.threaded.ThreadItemAdapter;
|
|||||||
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||||
import org.briarproject.briar.api.forum.Forum;
|
import org.briarproject.briar.api.forum.Forum;
|
||||||
import org.briarproject.briar.api.forum.ForumPostHeader;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -41,7 +40,7 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BOD
|
|||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class ForumActivity extends
|
public class ForumActivity extends
|
||||||
ThreadListActivity<Forum, ThreadItemAdapter<ForumItem>, ForumItem, ForumPostHeader>
|
ThreadListActivity<Forum, ForumItem, ThreadItemAdapter<ForumItem>>
|
||||||
implements ForumListener {
|
implements ForumListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -53,7 +52,7 @@ public class ForumActivity extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ThreadListController<Forum, ForumItem, ForumPostHeader> getController() {
|
protected ThreadListController<Forum, ForumItem> getController() {
|
||||||
return forumController;
|
return forumController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,11 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||||
import org.briarproject.briar.api.forum.Forum;
|
import org.briarproject.briar.api.forum.Forum;
|
||||||
import org.briarproject.briar.api.forum.ForumPostHeader;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface ForumController
|
interface ForumController extends ThreadListController<Forum, ForumItem> {
|
||||||
extends ThreadListController<Forum, ForumItem, ForumPostHeader> {
|
|
||||||
|
|
||||||
interface ForumListener extends ThreadListListener<ForumPostHeader> {
|
interface ForumListener extends ThreadListListener<ForumItem> {
|
||||||
@UiThread
|
@UiThread
|
||||||
void onForumLeft(ContactId c);
|
void onForumLeft(ContactId c);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ class ForumControllerImpl extends
|
|||||||
super.eventOccurred(e);
|
super.eventOccurred(e);
|
||||||
|
|
||||||
if (e instanceof ForumPostReceivedEvent) {
|
if (e instanceof ForumPostReceivedEvent) {
|
||||||
ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
|
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||||
if (pe.getGroupId().equals(getGroupId())) {
|
if (f.getGroupId().equals(getGroupId())) {
|
||||||
LOG.info("Forum post received, adding...");
|
LOG.info("Forum post received, adding...");
|
||||||
onForumPostHeaderReceived(pe.getForumPostHeader());
|
onForumPostReceived(f.getHeader(), f.getBody());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
||||||
ForumInvitationResponseReceivedEvent f =
|
ForumInvitationResponseReceivedEvent f =
|
||||||
@@ -90,10 +90,10 @@ class ForumControllerImpl extends
|
|||||||
onForumInvitationAccepted(r.getContactId());
|
onForumInvitationAccepted(r.getContactId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof ContactLeftShareableEvent) {
|
} else if (e instanceof ContactLeftShareableEvent) {
|
||||||
ContactLeftShareableEvent s = (ContactLeftShareableEvent) e;
|
ContactLeftShareableEvent c = (ContactLeftShareableEvent) e;
|
||||||
if (s.getGroupId().equals(getGroupId())) {
|
if (c.getGroupId().equals(getGroupId())) {
|
||||||
LOG.info("Forum left by contact");
|
LOG.info("Forum left by contact");
|
||||||
onForumLeft(s.getContactId());
|
onForumLeft(c.getContactId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,11 +195,12 @@ class ForumControllerImpl extends
|
|||||||
return new ForumItem(header, body);
|
return new ForumItem(header, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onForumPostHeaderReceived(final ForumPostHeader h) {
|
private void onForumPostReceived(ForumPostHeader h, String body) {
|
||||||
|
final ForumItem item = buildItem(h, body);
|
||||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
listener.onHeaderReceived(h);
|
listener.onItemReceived(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ public class ForumListFragment extends BaseEventFragment implements
|
|||||||
} else if (e instanceof ForumPostReceivedEvent) {
|
} else if (e instanceof ForumPostReceivedEvent) {
|
||||||
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
|
||||||
LOG.info("Forum post added, updating item");
|
LOG.info("Forum post added, updating item");
|
||||||
updateItem(f.getGroupId(), f.getForumPostHeader());
|
updateItem(f.getGroupId(), f.getHeader());
|
||||||
} else if (e instanceof ForumInvitationRequestReceivedEvent) {
|
} else if (e instanceof ForumInvitationRequestReceivedEvent) {
|
||||||
LOG.info("Forum invitation received, reloading available forums");
|
LOG.info("Forum invitation received, reloading available forums");
|
||||||
loadAvailableForums();
|
loadAvailableForums();
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListAct
|
|||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||||
import org.briarproject.briar.api.privategroup.Visibility;
|
import org.briarproject.briar.api.privategroup.Visibility;
|
||||||
|
|
||||||
@@ -44,7 +43,7 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_
|
|||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class GroupActivity extends
|
public class GroupActivity extends
|
||||||
ThreadListActivity<PrivateGroup, GroupMessageAdapter, GroupMessageItem, GroupMessageHeader>
|
ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageAdapter>
|
||||||
implements GroupListener, OnClickListener {
|
implements GroupListener, OnClickListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -60,7 +59,7 @@ public class GroupActivity extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> getController() {
|
protected ThreadListController<PrivateGroup, GroupMessageItem> getController() {
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +275,7 @@ public class GroupActivity extends
|
|||||||
public void onGroupDissolved() {
|
public void onGroupDissolved() {
|
||||||
setGroupEnabled(false);
|
setGroupEnabled(false);
|
||||||
AlertDialog.Builder builder =
|
AlertDialog.Builder builder =
|
||||||
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
|
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
|
||||||
builder.setTitle(getString(R.string.groups_dissolved_dialog_title));
|
builder.setTitle(getString(R.string.groups_dissolved_dialog_title));
|
||||||
builder.setMessage(getString(R.string.groups_dissolved_dialog_message));
|
builder.setMessage(getString(R.string.groups_dissolved_dialog_message));
|
||||||
builder.setNeutralButton(R.string.ok, null);
|
builder.setNeutralButton(R.string.ok, null);
|
||||||
|
|||||||
@@ -8,13 +8,11 @@ import org.briarproject.bramble.api.identity.AuthorId;
|
|||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||||
import org.briarproject.briar.api.privategroup.Visibility;
|
import org.briarproject.briar.api.privategroup.Visibility;
|
||||||
|
|
||||||
public interface GroupController
|
public interface GroupController
|
||||||
extends
|
extends ThreadListController<PrivateGroup, GroupMessageItem> {
|
||||||
ThreadListController<PrivateGroup, GroupMessageItem, GroupMessageHeader> {
|
|
||||||
|
|
||||||
void loadLocalAuthor(
|
void loadLocalAuthor(
|
||||||
ResultExceptionHandler<LocalAuthor, DbException> handler);
|
ResultExceptionHandler<LocalAuthor, DbException> handler);
|
||||||
@@ -22,7 +20,8 @@ public interface GroupController
|
|||||||
void isDissolved(
|
void isDissolved(
|
||||||
ResultExceptionHandler<Boolean, DbException> handler);
|
ResultExceptionHandler<Boolean, DbException> handler);
|
||||||
|
|
||||||
interface GroupListener extends ThreadListListener<GroupMessageHeader> {
|
interface GroupListener extends ThreadListListener<GroupMessageItem> {
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void onContactRelationshipRevealed(AuthorId memberId,
|
void onContactRelationshipRevealed(AuthorId memberId,
|
||||||
ContactId contactId, Visibility v);
|
ContactId contactId, Visibility v);
|
||||||
|
|||||||
@@ -80,14 +80,15 @@ class GroupControllerImpl extends
|
|||||||
super.eventOccurred(e);
|
super.eventOccurred(e);
|
||||||
|
|
||||||
if (e instanceof GroupMessageAddedEvent) {
|
if (e instanceof GroupMessageAddedEvent) {
|
||||||
GroupMessageAddedEvent gmae = (GroupMessageAddedEvent) e;
|
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||||
if (!gmae.isLocal() && gmae.getGroupId().equals(getGroupId())) {
|
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
|
||||||
LOG.info("Group message received, adding...");
|
LOG.info("Group message received, adding...");
|
||||||
final GroupMessageHeader h = gmae.getHeader();
|
final GroupMessageItem item =
|
||||||
|
buildItem(g.getHeader(), g.getBody());
|
||||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
listener.onHeaderReceived(h);
|
listener.onItemReceived(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package org.briarproject.briar.android.threaded;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.api.client.MessageTree;
|
import org.briarproject.briar.api.client.MessageTree;
|
||||||
|
import org.briarproject.briar.api.client.MessageTree.MessageNode;
|
||||||
import org.briarproject.briar.client.MessageTreeImpl;
|
import org.briarproject.briar.client.MessageTreeImpl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -13,8 +15,7 @@ import java.util.List;
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class NestedTreeList<T extends MessageTree.MessageNode>
|
public class NestedTreeList<T extends MessageNode> implements Iterable<T> {
|
||||||
implements Iterable<T> {
|
|
||||||
|
|
||||||
private final MessageTree<T> tree = new MessageTreeImpl<>();
|
private final MessageTree<T> tree = new MessageTreeImpl<>();
|
||||||
private List<T> depthFirstCollection = new ArrayList<>();
|
private List<T> depthFirstCollection = new ArrayList<>();
|
||||||
@@ -38,14 +39,14 @@ public class NestedTreeList<T extends MessageTree.MessageNode>
|
|||||||
return depthFirstCollection.get(index);
|
return depthFirstCollection.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int indexOf(T elem) {
|
|
||||||
return depthFirstCollection.indexOf(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return depthFirstCollection.size();
|
return depthFirstCollection.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean contains(MessageId m) {
|
||||||
|
return tree.contains(m);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<T> iterator() {
|
public Iterator<T> iterator() {
|
||||||
return depthFirstCollection.iterator();
|
return depthFirstCollection.iterator();
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.threaded;
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -28,7 +27,6 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
||||||
private final ThreadItemListener<I> listener;
|
private final ThreadItemListener<I> listener;
|
||||||
private final LinearLayoutManager layoutManager;
|
private final LinearLayoutManager layoutManager;
|
||||||
private final Handler handler = new Handler();
|
|
||||||
|
|
||||||
private volatile int revision = 0;
|
private volatile int revision = 0;
|
||||||
|
|
||||||
@@ -104,6 +102,10 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
return NO_POSITION; // Not found
|
return NO_POSITION; // Not found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean contains(MessageId m) {
|
||||||
|
return items.contains(m);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Highlights the item with the given {@link MessageId}
|
* Highlights the item with the given {@link MessageId}
|
||||||
* and disables the highlight for a previously highlighted item, if any.
|
* and disables the highlight for a previously highlighted item, if any.
|
||||||
@@ -184,6 +186,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class UnreadCount {
|
static class UnreadCount {
|
||||||
|
|
||||||
final int top, bottom;
|
final int top, bottom;
|
||||||
|
|
||||||
private UnreadCount(int top, int bottom) {
|
private UnreadCount(int top, int bottom) {
|
||||||
@@ -193,6 +196,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public interface ThreadItemListener<I> {
|
public interface ThreadItemListener<I> {
|
||||||
|
|
||||||
void onUnreadItemVisible(I item);
|
void onUnreadItemVisible(I item);
|
||||||
|
|
||||||
void onReplyClick(I item);
|
void onReplyClick(I item);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class ThreadItemListImpl<I extends ThreadItem> extends ArrayList<I>
|
|||||||
return bottomVisibleItemId;
|
return bottomVisibleItemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
|
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
|
||||||
this.bottomVisibleItemId = bottomVisibleItemId;
|
this.bottomVisibleItemId = bottomVisibleItemId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import org.briarproject.briar.android.view.TextInputView;
|
|||||||
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
||||||
import org.briarproject.briar.android.view.UnreadMessageButton;
|
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
import org.briarproject.briar.api.client.PostHeader;
|
|
||||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -49,9 +48,9 @@ import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCo
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
|
public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, A extends ThreadItemAdapter<I>>
|
||||||
extends BriarActivity
|
extends BriarActivity
|
||||||
implements ThreadListListener<H>, TextInputListener, SharingListener,
|
implements ThreadListListener<I>, TextInputListener, SharingListener,
|
||||||
ThreadItemListener<I>, ThreadListDataSource {
|
ThreadItemListener<I>, ThreadListDataSource {
|
||||||
|
|
||||||
protected static final String KEY_REPLY_ID = "replyId";
|
protected static final String KEY_REPLY_ID = "replyId";
|
||||||
@@ -68,7 +67,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
@Nullable
|
@Nullable
|
||||||
private MessageId replyId;
|
private MessageId replyId;
|
||||||
|
|
||||||
protected abstract ThreadListController<G, I, H> getController();
|
protected abstract ThreadListController<G, I> getController();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected SharingController sharingController;
|
protected SharingController sharingController;
|
||||||
@@ -190,7 +189,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
list.showData();
|
list.showData();
|
||||||
} else {
|
} else {
|
||||||
initList(items);
|
displayItems(items);
|
||||||
updateTextInput();
|
updateTextInput();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -206,7 +205,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initList(final ThreadItemList<I> items) {
|
private void displayItems(final ThreadItemList<I> items) {
|
||||||
adapter.setItems(items);
|
adapter.setItems(items);
|
||||||
MessageId messageId = items.getFirstVisibleItemId();
|
MessageId messageId = items.getFirstVisibleItemId();
|
||||||
if (messageId != null)
|
if (messageId != null)
|
||||||
@@ -383,19 +382,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
protected abstract int getMaxBodyLength();
|
protected abstract int getMaxBodyLength();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onHeaderReceived(H header) {
|
public void onItemReceived(I item) {
|
||||||
getController().loadItem(header,
|
addItem(item, false);
|
||||||
new UiResultExceptionHandler<I, DbException>(this) {
|
|
||||||
@Override
|
|
||||||
public void onResultUi(final I result) {
|
|
||||||
addItem(result, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExceptionUi(DbException exception) {
|
|
||||||
handleDbException(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -403,8 +391,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addItem(I item, boolean isLocal) {
|
private void addItem(I item, boolean isLocal) {
|
||||||
adapter.incrementRevision();
|
adapter.incrementRevision();
|
||||||
|
MessageId parent = item.getParentId();
|
||||||
|
if (parent != null && !adapter.contains(parent)) {
|
||||||
|
// We've incremented the adapter's revision, so the item will be
|
||||||
|
// loaded when its parent has been loaded
|
||||||
|
LOG.info("Ignoring item with missing parent");
|
||||||
|
return;
|
||||||
|
}
|
||||||
adapter.add(item);
|
adapter.add(item);
|
||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
|
|||||||
@@ -12,14 +12,13 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
|||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
import org.briarproject.briar.api.client.PostHeader;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ThreadListController<G extends NamedGroup, I extends ThreadItem, H extends PostHeader>
|
public interface ThreadListController<G extends NamedGroup, I extends ThreadItem>
|
||||||
extends ActivityLifecycleController {
|
extends ActivityLifecycleController {
|
||||||
|
|
||||||
void setGroupId(GroupId groupId);
|
void setGroupId(GroupId groupId);
|
||||||
@@ -29,9 +28,8 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
void loadSharingContacts(
|
void loadSharingContacts(
|
||||||
ResultExceptionHandler<Collection<ContactId>, DbException> handler);
|
ResultExceptionHandler<Collection<ContactId>, DbException> handler);
|
||||||
|
|
||||||
void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
|
void loadItems(
|
||||||
|
ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
|
||||||
void loadItems(ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
|
|
||||||
|
|
||||||
void markItemRead(I item);
|
void markItemRead(I item);
|
||||||
|
|
||||||
@@ -42,9 +40,10 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
|
|
||||||
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
||||||
|
|
||||||
interface ThreadListListener<H> extends ThreadListDataSource {
|
interface ThreadListListener<I> extends ThreadListDataSource {
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void onHeaderReceived(H header);
|
void onItemReceived(I item);
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void onGroupRemoved();
|
void onGroupRemoved();
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ import static java.util.logging.Level.WARNING;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<H>>
|
public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<I>>
|
||||||
extends DbControllerImpl
|
extends DbControllerImpl
|
||||||
implements ThreadListController<G, I, H>, EventListener {
|
implements ThreadListController<G, I>, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ThreadListControllerImpl.class.getName());
|
Logger.getLogger(ThreadListControllerImpl.class.getName());
|
||||||
@@ -203,35 +203,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
protected abstract String loadMessageBody(H header) throws DbException;
|
protected abstract String loadMessageBody(H header) throws DbException;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadItem(final H header,
|
|
||||||
final ResultExceptionHandler<I, DbException> handler) {
|
|
||||||
runOnDbThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
String body;
|
|
||||||
if (!bodyCache.containsKey(header.getId())) {
|
|
||||||
body = loadMessageBody(header);
|
|
||||||
bodyCache.put(header.getId(), body);
|
|
||||||
} else {
|
|
||||||
body = bodyCache.get(header.getId());
|
|
||||||
}
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading item took " + duration + " ms");
|
|
||||||
I item = buildItem(header, body);
|
|
||||||
handler.onResult(item);
|
|
||||||
} catch (DbException e) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
handler.onException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void markItemRead(I item) {
|
public void markItemRead(I item) {
|
||||||
markItemsRead(Collections.singletonList(item));
|
markItemsRead(Collections.singletonList(item));
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_group_member_list"
|
|
||||||
android:icon="@drawable/ic_group_white"
|
|
||||||
android:title="@string/groups_member_list"
|
|
||||||
app:showAsAction="ifRoom"/>
|
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_group_invite"
|
android:id="@+id/action_group_invite"
|
||||||
android:icon="@drawable/social_share_white"
|
android:icon="@drawable/social_share_white"
|
||||||
android:title="@string/groups_invite_members"
|
android:title="@string/groups_invite_members"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_group_member_list"
|
||||||
|
android:icon="@drawable/ic_group_white"
|
||||||
|
android:title="@string/groups_member_list"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_group_reveal"
|
android:id="@+id/action_group_reveal"
|
||||||
android:icon="@drawable/ic_visibility_white"
|
android:icon="@drawable/ic_visibility_white"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
|
|||||||
|
|
||||||
Collection<T> depthFirstOrder();
|
Collection<T> depthFirstOrder();
|
||||||
|
|
||||||
|
boolean contains(MessageId m);
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface MessageNode {
|
interface MessageNode {
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,26 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ForumPostReceivedEvent extends Event {
|
public class ForumPostReceivedEvent extends Event {
|
||||||
|
|
||||||
private final ForumPostHeader forumPostHeader;
|
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
|
private final ForumPostHeader header;
|
||||||
|
private final String body;
|
||||||
|
|
||||||
public ForumPostReceivedEvent(ForumPostHeader forumPostHeader,
|
public ForumPostReceivedEvent(GroupId groupId, ForumPostHeader header,
|
||||||
GroupId groupId) {
|
String body) {
|
||||||
|
|
||||||
this.forumPostHeader = forumPostHeader;
|
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
this.header = header;
|
||||||
|
this.body = body;
|
||||||
public ForumPostHeader getForumPostHeader() {
|
|
||||||
return forumPostHeader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupId getGroupId() {
|
public GroupId getGroupId() {
|
||||||
return groupId;
|
return groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ForumPostHeader getHeader() {
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,13 +17,14 @@ public class GroupMessageAddedEvent extends Event {
|
|||||||
|
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
private final GroupMessageHeader header;
|
private final GroupMessageHeader header;
|
||||||
|
private final String body;
|
||||||
private final boolean local;
|
private final boolean local;
|
||||||
|
|
||||||
public GroupMessageAddedEvent(GroupId groupId, GroupMessageHeader header,
|
public GroupMessageAddedEvent(GroupId groupId, GroupMessageHeader header,
|
||||||
boolean local) {
|
String body, boolean local) {
|
||||||
|
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
this.header = header;
|
this.header = header;
|
||||||
|
this.body = body;
|
||||||
this.local = local;
|
this.local = local;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,6 +36,10 @@ public class GroupMessageAddedEvent extends Event {
|
|||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isLocal() {
|
public boolean isLocal() {
|
||||||
return local;
|
return local;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,4 +107,8 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
return orderedList;
|
return orderedList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(MessageId m) {
|
||||||
|
return nodeMap.containsKey(m);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import javax.annotation.Nullable;
|
|||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.ANONYMOUS;
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||||
import static org.briarproject.briar.api.forum.ForumConstants.KEY_AUTHOR;
|
import static org.briarproject.briar.api.forum.ForumConstants.KEY_AUTHOR;
|
||||||
import static org.briarproject.briar.api.forum.ForumConstants.KEY_ID;
|
import static org.briarproject.briar.api.forum.ForumConstants.KEY_ID;
|
||||||
@@ -85,9 +84,10 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
|||||||
|
|
||||||
messageTracker.trackIncomingMessage(txn, m);
|
messageTracker.trackIncomingMessage(txn, m);
|
||||||
|
|
||||||
ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta);
|
ForumPostHeader header = getForumPostHeader(txn, m.getId(), meta);
|
||||||
|
String postBody = getPostBody(body);
|
||||||
ForumPostReceivedEvent event =
|
ForumPostReceivedEvent event =
|
||||||
new ForumPostReceivedEvent(post, m.getGroupId());
|
new ForumPostReceivedEvent(m.getGroupId(), header, postBody);
|
||||||
txn.attach(event);
|
txn.attach(event);
|
||||||
|
|
||||||
// share message
|
// share message
|
||||||
@@ -215,14 +215,19 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
|||||||
public String getPostBody(MessageId m) throws DbException {
|
public String getPostBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
// Parent ID, author, forum post body, signature
|
// Parent ID, author, forum post body, signature
|
||||||
BdfList message = clientHelper.getMessageAsList(m);
|
BdfList body = clientHelper.getMessageAsList(m);
|
||||||
if (message == null) throw new DbException();
|
if (body == null) throw new DbException();
|
||||||
return message.getString(2);
|
return getPostBody(body);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getPostBody(BdfList body) throws FormatException {
|
||||||
|
// Parent ID, author, forum post body, signature
|
||||||
|
return body.getString(2);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<ForumPostHeader> getPostHeaders(GroupId g)
|
public Collection<ForumPostHeader> getPostHeaders(GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -294,24 +299,17 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
|||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
|
|
||||||
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
||||||
Author author = null;
|
|
||||||
Status status = ANONYMOUS;
|
|
||||||
MessageId parentId = null;
|
MessageId parentId = null;
|
||||||
if (meta.containsKey(KEY_PARENT))
|
if (meta.containsKey(KEY_PARENT))
|
||||||
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
||||||
// TODO: Remove support for anonymous forum posts
|
BdfDictionary authorDict = meta.getDictionary(KEY_AUTHOR);
|
||||||
BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null);
|
AuthorId authorId = new AuthorId(authorDict.getRaw(KEY_ID));
|
||||||
if (d1 != null) {
|
String name = authorDict.getString(KEY_NAME);
|
||||||
AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID));
|
byte[] publicKey = authorDict.getRaw(KEY_PUBLIC_NAME);
|
||||||
String name = d1.getString(KEY_NAME);
|
Author author = new Author(authorId, name, publicKey);
|
||||||
byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
|
Status status = statuses.get(authorId);
|
||||||
author = new Author(authorId, name, publicKey);
|
if (status == null)
|
||||||
if (statuses.containsKey(authorId)) {
|
status = identityManager.getAuthorStatus(txn, author.getId());
|
||||||
status = statuses.get(authorId);
|
|
||||||
} else {
|
|
||||||
status = identityManager.getAuthorStatus(txn, author.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean read = meta.getBoolean(MSG_KEY_READ);
|
boolean read = meta.getBoolean(MSG_KEY_READ);
|
||||||
|
|
||||||
return new ForumPostHeader(id, parentId, timestamp, author, status,
|
return new ForumPostHeader(id, parentId, timestamp, author, status,
|
||||||
|
|||||||
@@ -307,16 +307,20 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
|
|||||||
@Override
|
@Override
|
||||||
public String getMessageBody(MessageId m) throws DbException {
|
public String getMessageBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
// type(0), member_name(1), member_public_key(2), parent_id(3),
|
|
||||||
// previous_message_id(4), content(5), signature(6)
|
|
||||||
BdfList body = clientHelper.getMessageAsList(m);
|
BdfList body = clientHelper.getMessageAsList(m);
|
||||||
if (body == null) throw new DbException();
|
if (body == null) throw new DbException();
|
||||||
return body.getString(5);
|
return getMessageBody(body);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getMessageBody(BdfList body) throws FormatException {
|
||||||
|
// type(0), member_name(1), member_public_key(2), parent_id(3),
|
||||||
|
// previous_message_id(4), content(5), signature(6)
|
||||||
|
return body.getString(5);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<GroupMessageHeader> getHeaders(GroupId g)
|
public Collection<GroupMessageHeader> getHeaders(GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -579,21 +583,20 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
|
|||||||
private void attachGroupMessageAddedEvent(Transaction txn, Message m,
|
private void attachGroupMessageAddedEvent(Transaction txn, Message m,
|
||||||
BdfDictionary meta, boolean local)
|
BdfDictionary meta, boolean local)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
GroupMessageHeader h =
|
GroupMessageHeader header = getGroupMessageHeader(txn, m.getGroupId(),
|
||||||
getGroupMessageHeader(txn, m.getGroupId(), m.getId(), meta,
|
m.getId(), meta, Collections.<AuthorId, Status>emptyMap());
|
||||||
Collections.<AuthorId, Status>emptyMap());
|
String body = getMessageBody(clientHelper.toList(m));
|
||||||
Event e = new GroupMessageAddedEvent(m.getGroupId(), h, local);
|
txn.attach(new GroupMessageAddedEvent(m.getGroupId(), header, body,
|
||||||
txn.attach(e);
|
local));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void attachJoinMessageAddedEvent(Transaction txn, Message m,
|
private void attachJoinMessageAddedEvent(Transaction txn, Message m,
|
||||||
BdfDictionary meta, boolean local, Visibility v)
|
BdfDictionary meta, boolean local, Visibility v)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
JoinMessageHeader h =
|
JoinMessageHeader header = getJoinMessageHeader(txn, m.getGroupId(),
|
||||||
getJoinMessageHeader(txn, m.getGroupId(), m.getId(), meta,
|
m.getId(), meta, Collections.<AuthorId, Status>emptyMap(), v);
|
||||||
Collections.<AuthorId, Status>emptyMap(), v);
|
txn.attach(new GroupMessageAddedEvent(m.getGroupId(), header, "",
|
||||||
Event e = new GroupMessageAddedEvent(m.getGroupId(), h, local);
|
local));
|
||||||
txn.attach(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMember(Transaction txn, GroupId g, Author a, Visibility v)
|
private void addMember(Transaction txn, GroupId g, Author a, Visibility v)
|
||||||
|
|||||||
Reference in New Issue
Block a user