Move adding new ThreadList items to ViewModel

This commit is contained in:
Torsten Grote
2021-01-07 16:57:12 -03:00
parent d393b79ced
commit 21e56284fb
13 changed files with 224 additions and 259 deletions

View File

@@ -8,20 +8,15 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.forum.ForumController.ForumListener;
import org.briarproject.briar.android.threaded.ThreadListControllerImpl; import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostHeader; import org.briarproject.briar.api.forum.ForumPostHeader;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent; import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
@@ -33,16 +28,13 @@ import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault @NotNullByDefault
class ForumControllerImpl extends class ForumControllerImpl extends ThreadListControllerImpl<ForumPostItem>
ThreadListControllerImpl<ForumPostItem, ForumPostHeader, ForumPost, ForumListener>
implements ForumController { implements ForumController {
private static final Logger LOG = private static final Logger LOG =
@@ -56,10 +48,10 @@ class ForumControllerImpl extends
LifecycleManager lifecycleManager, IdentityManager identityManager, LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
ForumManager forumManager, ForumSharingManager forumSharingManager, ForumManager forumManager, ForumSharingManager forumSharingManager,
EventBus eventBus, Clock clock, MessageTracker messageTracker, EventBus eventBus, Clock clock,
AndroidNotificationManager notificationManager) { AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager, messageTracker); eventBus, clock, notificationManager);
this.forumManager = forumManager; this.forumManager = forumManager;
this.forumSharingManager = forumSharingManager; this.forumSharingManager = forumSharingManager;
} }
@@ -74,6 +66,8 @@ class ForumControllerImpl extends
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
super.eventOccurred(e); super.eventOccurred(e);
ForumListener listener = (ForumListener) this.listener;
if (e instanceof ForumPostReceivedEvent) { if (e instanceof ForumPostReceivedEvent) {
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e; ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
if (f.getGroupId().equals(getGroupId())) { if (f.getGroupId().equals(getGroupId())) {
@@ -120,44 +114,7 @@ class ForumControllerImpl extends
}); });
} }
@Override private ForumPostItem buildItem(ForumPostHeader header, String text) {
public void createAndStoreMessage(String text,
@Nullable ForumPostItem parentItem,
ResultExceptionHandler<ForumPostItem, DbException> handler) {
runOnDbThread(() -> {
try {
LocalAuthor author = identityManager.getLocalAuthor();
GroupCount count = forumManager.getGroupCount(getGroupId());
long timestamp = max(count.getLatestMsgTime() + 1,
clock.currentTimeMillis());
MessageId parentId = parentItem != null ?
parentItem.getId() : null;
createMessage(text, timestamp, parentId, author, handler);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
private void createMessage(String text, long timestamp,
@Nullable MessageId parentId, LocalAuthor author,
ResultExceptionHandler<ForumPostItem, DbException> handler) {
cryptoExecutor.execute(() -> {
LOG.info("Creating forum post...");
ForumPost msg = forumManager.createLocalPost(getGroupId(), text,
timestamp, parentId, author);
storePost(msg, text, handler);
});
}
@Override
protected ForumPostHeader addLocalMessage(ForumPost p) throws DbException {
return forumManager.addLocalPost(p);
}
@Override
protected ForumPostItem buildItem(ForumPostHeader header, String text) {
return new ForumPostItem(header, text); return new ForumPostItem(header, text);
} }

View File

@@ -11,32 +11,37 @@ import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadListViewModel; import org.briarproject.briar.android.threaded.ThreadListViewModel;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostHeader; import org.briarproject.briar.api.forum.ForumPostHeader;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -98,12 +103,52 @@ class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
List<ForumPostHeader> headers = List<ForumPostHeader> headers =
forumManager.getPostHeaders(txn, groupId); forumManager.getPostHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start); logDuration(LOG, "Loading headers", start);
List<ForumPostItem> items = return recreateItems(txn, headers, this::buildItem);
buildItems(txn, headers, this::buildItem);
return new MessageTreeImpl<>(items).depthFirstOrder();
}, this::setItems); }, this::setItems);
} }
@Override
public void createAndStoreMessage(String text,
@Nullable ForumPostItem parentItem) {
runOnDbThread(() -> {
try {
LocalAuthor author = identityManager.getLocalAuthor();
GroupCount count = forumManager.getGroupCount(groupId);
long timestamp = max(count.getLatestMsgTime() + 1,
clock.currentTimeMillis());
MessageId parentId =
parentItem != null ? parentItem.getId() : null;
createMessage(text, timestamp, parentId, author);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void createMessage(String text, long timestamp,
@Nullable MessageId parentId, LocalAuthor author) {
cryptoExecutor.execute(() -> {
LOG.info("Creating forum post...");
ForumPost msg = forumManager.createLocalPost(groupId, text,
timestamp, parentId, author);
storePost(msg, text);
});
}
private void storePost(ForumPost msg, String text) {
runOnDbThread(() -> {
try {
long start = now();
ForumPostHeader header = forumManager.addLocalPost(msg);
textCache.put(msg.getMessage().getId(), text);
addItem(buildItem(header, text), true);
logDuration(LOG, "Storing forum post", start);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private ForumPostItem buildItem(ForumPostHeader header, String text) { private ForumPostItem buildItem(ForumPostHeader header, String text) {
return new ForumPostItem(header, text); return new ForumPostItem(header, text);
} }

View File

@@ -7,21 +7,15 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener;
import org.briarproject.briar.android.threaded.ThreadListControllerImpl; import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.GroupMessageHeader; import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.JoinMessageHeader; import org.briarproject.briar.api.privategroup.JoinMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -36,37 +30,32 @@ import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class GroupControllerImpl extends class GroupControllerImpl extends ThreadListControllerImpl<GroupMessageItem>
ThreadListControllerImpl<GroupMessageItem, GroupMessageHeader, GroupMessage, GroupListener>
implements GroupController { implements GroupController {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(GroupControllerImpl.class.getName()); getLogger(GroupControllerImpl.class.getName());
private final PrivateGroupManager privateGroupManager; private final PrivateGroupManager privateGroupManager;
private final GroupMessageFactory groupMessageFactory;
@Inject @Inject
GroupControllerImpl(@DatabaseExecutor Executor dbExecutor, GroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, IdentityManager identityManager, LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
GroupMessageFactory groupMessageFactory, EventBus eventBus, EventBus eventBus, Clock clock,
MessageTracker messageTracker, Clock clock,
AndroidNotificationManager notificationManager) { AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager, messageTracker); eventBus, clock, notificationManager);
this.privateGroupManager = privateGroupManager; this.privateGroupManager = privateGroupManager;
this.groupMessageFactory = groupMessageFactory;
} }
@Override @Override
@@ -79,6 +68,8 @@ class GroupControllerImpl extends
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
super.eventOccurred(e); super.eventOccurred(e);
GroupListener listener = (GroupListener) this.listener;
if (e instanceof GroupMessageAddedEvent) { if (e instanceof GroupMessageAddedEvent) {
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e; GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) { if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
@@ -132,52 +123,7 @@ class GroupControllerImpl extends
}); });
} }
@Override private GroupMessageItem buildItem(GroupMessageHeader header, String text) {
public void createAndStoreMessage(String text,
@Nullable GroupMessageItem parentItem,
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
runOnDbThread(() -> {
try {
LocalAuthor author = identityManager.getLocalAuthor();
MessageId parentId = null;
MessageId previousMsgId =
privateGroupManager.getPreviousMsgId(getGroupId());
GroupCount count =
privateGroupManager.getGroupCount(getGroupId());
long timestamp = count.getLatestMsgTime();
if (parentItem != null) parentId = parentItem.getId();
timestamp = max(clock.currentTimeMillis(), timestamp + 1);
createMessage(text, timestamp, parentId, author, previousMsgId,
handler);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
private void createMessage(String text, long timestamp,
@Nullable MessageId parentId, LocalAuthor author,
MessageId previousMsgId,
ResultExceptionHandler<GroupMessageItem, DbException> handler) {
cryptoExecutor.execute(() -> {
LOG.info("Creating group message...");
GroupMessage msg = groupMessageFactory
.createGroupMessage(getGroupId(), timestamp,
parentId, author, text, previousMsgId);
storePost(msg, text, handler);
});
}
@Override
protected GroupMessageHeader addLocalMessage(GroupMessage message)
throws DbException {
return privateGroupManager.addLocalMessage(message);
}
@Override
protected GroupMessageItem buildItem(GroupMessageHeader header,
String text) {
if (header instanceof JoinMessageHeader) { if (header instanceof JoinMessageHeader) {
return new JoinMessageItem((JoinMessageHeader) header, text); return new JoinMessageItem((JoinMessageHeader) header, text);
} }

View File

@@ -58,7 +58,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
GroupMessageItem item = getItem(position); GroupMessageItem item = getItem(position);
if (item instanceof JoinMessageItem) { if (item instanceof JoinMessageItem) {
((JoinMessageItem) item).setVisibility(v); ((JoinMessageItem) item).setVisibility(v);
notifyItemChanged(findItemPosition(item), item); notifyItemChanged(findItemPosition(item.getId()), item);
} }
} }
} }
@@ -73,12 +73,4 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
return NO_POSITION; // Not found return NO_POSITION; // Not found
} }
@Deprecated
private int findItemPosition(GroupMessageItem itemToFind) {
for (int i = 0; i < getItemCount(); i++) {
if (getItem(i).equals(itemToFind)) return i;
}
return NO_POSITION; // Not found
}
} }

View File

@@ -11,32 +11,37 @@ import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.threaded.ThreadListViewModel; import org.briarproject.briar.android.threaded.ThreadListViewModel;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.GroupMessageHeader; import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.JoinMessageHeader; import org.briarproject.briar.api.privategroup.JoinMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -109,9 +114,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
List<GroupMessageHeader> headers = List<GroupMessageHeader> headers =
privateGroupManager.getHeaders(txn, groupId); privateGroupManager.getHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start); logDuration(LOG, "Loading headers", start);
List<GroupMessageItem> items = return recreateItems(txn, headers, this::buildItem);
buildItems(txn, headers, this::buildItem);
return new MessageTreeImpl<>(items).depthFirstOrder();
}, this::setItems); }, this::setItems);
} }
@@ -132,6 +135,52 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
return privateGroupManager.getMessageText(txn, header.getId()); return privateGroupManager.getMessageText(txn, header.getId());
} }
@Override
public void createAndStoreMessage(String text,
@Nullable GroupMessageItem parentItem) {
runOnDbThread(() -> {
try {
LocalAuthor author = identityManager.getLocalAuthor();
MessageId parentId = null;
MessageId previousMsgId =
privateGroupManager.getPreviousMsgId(groupId);
GroupCount count = privateGroupManager.getGroupCount(groupId);
long timestamp = count.getLatestMsgTime();
if (parentItem != null) parentId = parentItem.getId();
timestamp = max(clock.currentTimeMillis(), timestamp + 1);
createMessage(text, timestamp, parentId, author, previousMsgId);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void createMessage(String text, long timestamp,
@Nullable MessageId parentId, LocalAuthor author,
MessageId previousMsgId) {
cryptoExecutor.execute(() -> {
LOG.info("Creating group message...");
GroupMessage msg = groupMessageFactory.createGroupMessage(groupId,
timestamp, parentId, author, text, previousMsgId);
storePost(msg, text);
});
}
private void storePost(GroupMessage msg, String text) {
runOnDbThread(() -> {
try {
long start = now();
GroupMessageHeader header =
privateGroupManager.addLocalMessage(msg);
textCache.put(msg.getMessage().getId(), text);
addItem(buildItem(header, text), true);
logDuration(LOG, "Storing group message", start);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
void deletePrivateGroup() { void deletePrivateGroup() {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {

View File

@@ -66,16 +66,11 @@ public class ThreadItemAdapter<I extends ThreadItem>
ui.bind(item, listener); ui.bind(item, listener);
} }
void setItemWithIdVisible(MessageId messageId) { public int findItemPosition(MessageId id) {
int pos = 0;
for (int i = 0; i < getItemCount(); i++) { for (int i = 0; i < getItemCount(); i++) {
I item = getItem(i); if (id.equals(getItem(i).getId())) return i;
if (item.getId().equals(messageId)) {
layoutManager.scrollToPosition(pos);
break;
}
pos++;
} }
return NO_POSITION; // Not found
} }
/** /**

View File

@@ -136,6 +136,12 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
loadSharingContacts(); loadSharingContacts();
} }
@Override
protected void onDestroy() {
super.onDestroy();
getViewModel().storeMessageId(getFirstVisibleMessageId());
}
@Override @Override
@Nullable @Nullable
public MessageId getFirstVisibleMessageId() { public MessageId getFirstVisibleMessageId() {
@@ -154,21 +160,29 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
if (items.isEmpty()) { if (items.isEmpty()) {
list.showData(); list.showData();
} else { } else {
adapter.submitList(items); adapter.submitList(items, this::scrollAfterListCommitted);
// TODO get this ID from elsewhere
MessageId messageId = null; // items.getFirstVisibleItemId();
if (messageId != null)
adapter.setItemWithIdVisible(messageId);
list.showData();
if (layoutManagerState == null) {
list.scrollToPosition(0); // Scroll to the top
} else {
layoutManager.onRestoreInstanceState(layoutManagerState);
}
updateTextInput(); updateTextInput();
} }
} }
/**
* Scrolls to the first visible item last time the activity was open,
* if one exists and this is the first time, the list gets displayed.
* Or scrolls to a locally added item that has just been added to the list.
*/
private void scrollAfterListCommitted() {
MessageId restoredFirstVisibleItemId =
getViewModel().getAndResetRestoredMessageId();
MessageId scrollToItem =
getViewModel().getAndResetScrollToItem();
if (restoredFirstVisibleItemId != null) {
scrollToItemAtTop(restoredFirstVisibleItemId);
} else if (scrollToItem != null) {
scrollToItemAtTop(scrollToItem);
}
scrollListener.updateUnreadButtons(layoutManager);
}
protected void loadSharingContacts() { protected void loadSharingContacts() {
getController().loadSharingContacts( getController().loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>, DbException>( new UiResultExceptionHandler<Collection<ContactId>, DbException>(
@@ -206,10 +220,6 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
if (layoutManager != null) {
layoutManagerState = layoutManager.onSaveInstanceState();
outState.putParcelable("layoutManager", layoutManagerState);
}
if (replyId != null) { if (replyId != null) {
outState.putByteArray(KEY_REPLY_ID, replyId.getBytes()); outState.putByteArray(KEY_REPLY_ID, replyId.getBytes());
} }
@@ -218,7 +228,6 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) { protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState); super.onRestoreInstanceState(savedInstanceState);
layoutManagerState = savedInstanceState.getParcelable("layoutManager");
} }
@Override @Override
@@ -247,11 +256,11 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
updateTextInput(); updateTextInput();
// FIXME This does not work for a hardware keyboard // FIXME This does not work for a hardware keyboard
if (textInput.isKeyboardOpen()) { if (textInput.isKeyboardOpen()) {
scrollToItemAtTop(item); scrollToItemAtTop(item.getId());
} else { } else {
// wait with scrolling until keyboard opened // wait with scrolling until keyboard opened
textInput.setOnKeyboardShownListener(() -> { textInput.setOnKeyboardShownListener(() -> {
scrollToItemAtTop(item); scrollToItemAtTop(item.getId());
textInput.setOnKeyboardShownListener(null); textInput.setOnKeyboardShownListener(null);
}); });
} }
@@ -277,11 +286,10 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
} }
} }
private void scrollToItemAtTop(I item) { private void scrollToItemAtTop(MessageId messageId) {
int position = NO_POSITION;// adapter.findItemPosition(item); int position = adapter.findItemPosition(messageId);
if (position != NO_POSITION) { if (position != NO_POSITION) {
layoutManager layoutManager.scrollToPositionWithOffset(position, 0);
.scrollToPositionWithOffset(position, 0);
} }
} }
@@ -307,19 +315,7 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
if (isNullOrEmpty(text)) throw new AssertionError(); if (isNullOrEmpty(text)) throw new AssertionError();
I replyItem = adapter.getHighlightedItem(); I replyItem = adapter.getHighlightedItem();
UiResultExceptionHandler<I, DbException> handler = getViewModel().createAndStoreMessage(text, replyItem);
new UiResultExceptionHandler<I, DbException>(this) {
@Override
public void onResultUi(I result) {
addItem(result, true);
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
};
getController().createAndStoreMessage(text, replyItem, handler);
textInput.hideSoftKeyboard(); textInput.hideSoftKeyboard();
textInput.clearText(); textInput.clearText();
replyId = null; replyId = null;
@@ -330,7 +326,7 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override @Override
public void onItemReceived(I item) { public void onItemReceived(I item) {
addItem(item, false); getViewModel().addItem(item, false);
} }
@Override @Override
@@ -338,21 +334,4 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
private void addItem(I item, boolean isLocal) {
MessageId parent = item.getParentId();
if (parent != null) {
// 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;
}
// TODO submit new list
if (isLocal) {
scrollToItemAtTop(item);
} else {
scrollListener.updateUnreadButtons(layoutManager);
}
}
} }

View File

@@ -27,9 +27,6 @@ public interface ThreadListController<I extends ThreadItem>
void markItemsRead(Collection<I> items); void markItemsRead(Collection<I> items);
void createAndStoreMessage(String text, @Nullable I parentItem,
ResultExceptionHandler<I, DbException> handler);
interface ThreadListListener<I> extends ThreadListDataSource { interface ThreadListListener<I> extends ThreadListDataSource {
@UiThread @UiThread

View File

@@ -17,17 +17,10 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.client.ThreadedMessage;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -40,39 +33,34 @@ import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<I>> public abstract class ThreadListControllerImpl<I extends ThreadItem>
extends DbControllerImpl extends DbControllerImpl
implements ThreadListController<I>, EventListener { implements ThreadListController<I>, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ThreadListControllerImpl.class.getName()); Logger.getLogger(ThreadListControllerImpl.class.getName());
private final EventBus eventBus;
private final MessageTracker messageTracker;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private volatile GroupId groupId; private volatile GroupId groupId;
private final EventBus eventBus;
protected final IdentityManager identityManager; protected final IdentityManager identityManager;
protected final AndroidNotificationManager notificationManager; protected final AndroidNotificationManager notificationManager;
protected final Executor cryptoExecutor; protected final Executor cryptoExecutor;
protected final Clock clock; protected final Clock clock;
// UI thread // UI thread
protected L listener; protected ThreadListListener<I> listener;
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor, protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, IdentityManager identityManager, LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus, @CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
Clock clock, AndroidNotificationManager notificationManager, Clock clock, AndroidNotificationManager notificationManager) {
MessageTracker messageTracker) {
super(dbExecutor, lifecycleManager); super(dbExecutor, lifecycleManager);
this.identityManager = identityManager; this.identityManager = identityManager;
this.cryptoExecutor = cryptoExecutor; this.cryptoExecutor = cryptoExecutor;
this.notificationManager = notificationManager; this.notificationManager = notificationManager;
this.clock = clock; this.clock = clock;
this.eventBus = eventBus; this.eventBus = eventBus;
this.messageTracker = messageTracker;
} }
@Override @Override
@@ -84,7 +72,7 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void onActivityCreate(Activity activity) { public void onActivityCreate(Activity activity) {
listener = (L) activity; listener = (ThreadListListener<I>) activity;
} }
@CallSuper @CallSuper
@@ -103,16 +91,7 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@Override @Override
public void onActivityDestroy() { public void onActivityDestroy() {
MessageId messageId = listener.getFirstVisibleMessageId();
if (messageId != null) {
dbExecutor.execute(() -> {
try {
messageTracker.storeMessageId(groupId, messageId);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
} }
@CallSuper @CallSuper
@@ -150,27 +129,6 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@DatabaseExecutor @DatabaseExecutor
protected abstract void markRead(MessageId id) throws DbException; protected abstract void markRead(MessageId id) throws DbException;
protected void storePost(M msg, String text,
ResultExceptionHandler<I, DbException> resultHandler) {
runOnDbThread(() -> {
try {
long start = now();
H header = addLocalMessage(msg);
textCache.put(msg.getMessage().getId(), text);
logDuration(LOG, "Storing message", start);
resultHandler.onResult(buildItem(header, text));
} catch (DbException e) {
logException(LOG, WARNING, e);
resultHandler.onException(e);
}
});
}
@DatabaseExecutor
protected abstract H addLocalMessage(M message) throws DbException;
protected abstract I buildItem(H header, String text);
protected GroupId getGroupId() { protected GroupId getGroupId() {
checkGroupId(); checkGroupId();
return groupId; return groupId;

View File

@@ -21,28 +21,36 @@ import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTree;
import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewModel public abstract class ThreadListViewModel<I extends ThreadItem>
extends DbViewModel
implements EventListener { implements EventListener {
private static final Logger LOG = private static final Logger LOG =
@@ -55,11 +63,18 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final EventBus eventBus; private final EventBus eventBus;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>(); @DatabaseExecutor
private final MessageTree<I> messageTree = new MessageTreeImpl<>();
protected final Map<MessageId, String> textCache =
new ConcurrentHashMap<>();
private final MutableLiveData<LiveResult<List<I>>> items = private final MutableLiveData<LiveResult<List<I>>> items =
new MutableLiveData<>(); new MutableLiveData<>();
private final AtomicReference<MessageId> scrollToItem =
new AtomicReference<>();
protected volatile GroupId groupId; protected volatile GroupId groupId;
private final AtomicReference<MessageId> storedMessageId =
new AtomicReference<>();
public ThreadListViewModel(Application application, public ThreadListViewModel(Application application,
@DatabaseExecutor Executor dbExecutor, @DatabaseExecutor Executor dbExecutor,
@@ -95,18 +110,37 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
@CallSuper @CallSuper
public void setGroupId(GroupId groupId) { public void setGroupId(GroupId groupId) {
this.groupId = groupId; this.groupId = groupId;
loadStoredMessageId();
loadItems(); loadItems();
} }
private void loadStoredMessageId() {
runOnDbThread(() -> {
try {
storedMessageId
.set(messageTracker.loadStoredMessageId(groupId));
if (LOG.isLoggable(INFO)) {
LOG.info("Loaded last top visible message id " +
storedMessageId);
}
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
public abstract void loadItems(); public abstract void loadItems();
public abstract void createAndStoreMessage(String text,
@Nullable I parentItem);
@UiThread @UiThread
protected void setItems(LiveResult<List<I>> items) { protected void setItems(LiveResult<List<I>> items) {
this.items.setValue(items); this.items.setValue(items);
} }
@DatabaseExecutor @DatabaseExecutor
protected <H extends PostHeader> List<I> buildItems( protected <H extends PostHeader> List<I> recreateItems(
Transaction txn, Collection<H> headers, ItemGetter<H, I> itemGetter) Transaction txn, Collection<H> headers, ItemGetter<H, I> itemGetter)
throws DbException { throws DbException {
long start = now(); long start = now();
@@ -122,23 +156,45 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
} }
logDuration(LOG, "Loading bodies and creating items", start); logDuration(LOG, "Loading bodies and creating items", start);
MessageId msgId = messageTracker.loadStoredMessageId(txn, groupId); messageTree.clear();
if (LOG.isLoggable(INFO)) { messageTree.add(items);
LOG.info("Loaded last top visible message id " + msgId); return messageTree.depthFirstOrder();
} }
// TODO store this elsewhere
items.setFirstVisibleId(msgId); protected void addItem(I item, boolean local) {
return items; messageTree.add(item);
if (local) scrollToItem.set(item.getId());
items.postValue(new LiveResult<>(messageTree.depthFirstOrder()));
} }
@DatabaseExecutor @DatabaseExecutor
protected abstract String loadMessageText(Transaction txn, protected abstract String loadMessageText(Transaction txn,
PostHeader header) throws DbException; PostHeader header) throws DbException;
void storeMessageId(@Nullable MessageId messageId) {
if (messageId != null) runOnDbThread(() -> {
try {
messageTracker.storeMessageId(groupId, messageId);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
@Nullable
MessageId getAndResetRestoredMessageId() {
return storedMessageId.getAndSet(null);
}
LiveData<LiveResult<List<I>>> getItems() { LiveData<LiveResult<List<I>>> getItems() {
return items; return items;
} }
@Nullable
MessageId getAndResetScrollToItem() {
return scrollToItem.getAndSet(null);
}
public interface ItemGetter<H extends PostHeader, I> { public interface ItemGetter<H extends PostHeader, I> {
I getItem(H header, String text); I getItem(H header, String text);
} }

View File

@@ -13,15 +13,12 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
void add(Collection<T> nodes); void add(Collection<T> nodes);
@Deprecated
void add(T node); void add(T node);
@Deprecated
void clear(); void clear();
List<T> depthFirstOrder(); List<T> depthFirstOrder();
@Deprecated
boolean contains(MessageId m); boolean contains(MessageId m);
@NotNullByDefault @NotNullByDefault

View File

@@ -109,7 +109,6 @@ public interface ForumManager {
/** /**
* Returns the group count for the given forum. * Returns the group count for the given forum.
*/ */
@Deprecated
GroupCount getGroupCount(GroupId g) throws DbException; GroupCount getGroupCount(GroupId g) throws DbException;
/** /**

View File

@@ -33,11 +33,6 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
private final Comparator<T> comparator = (o1, o2) -> private final Comparator<T> comparator = (o1, o2) ->
Long.valueOf(o1.getTimestamp()).compareTo(o2.getTimestamp()); Long.valueOf(o1.getTimestamp()).compareTo(o2.getTimestamp());
public MessageTreeImpl(Collection<T> collection) {
super();
add(collection);
}
@Override @Override
public synchronized void clear() { public synchronized void clear() {
roots.clear(); roots.clear();