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.EventBus;
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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
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.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.ForumManager;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostHeader;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
@@ -33,16 +28,13 @@ import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class ForumControllerImpl extends
ThreadListControllerImpl<ForumPostItem, ForumPostHeader, ForumPost, ForumListener>
class ForumControllerImpl extends ThreadListControllerImpl<ForumPostItem>
implements ForumController {
private static final Logger LOG =
@@ -56,10 +48,10 @@ class ForumControllerImpl extends
LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor,
ForumManager forumManager, ForumSharingManager forumSharingManager,
EventBus eventBus, Clock clock, MessageTracker messageTracker,
EventBus eventBus, Clock clock,
AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager, messageTracker);
eventBus, clock, notificationManager);
this.forumManager = forumManager;
this.forumSharingManager = forumSharingManager;
}
@@ -74,6 +66,8 @@ class ForumControllerImpl extends
public void eventOccurred(Event e) {
super.eventOccurred(e);
ForumListener listener = (ForumListener) this.listener;
if (e instanceof ForumPostReceivedEvent) {
ForumPostReceivedEvent f = (ForumPostReceivedEvent) e;
if (f.getGroupId().equals(getGroupId())) {
@@ -120,44 +114,7 @@ class ForumControllerImpl extends
});
}
@Override
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) {
private ForumPostItem buildItem(ForumPostHeader header, String 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.EventBus;
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.nullsafety.MethodsNotNullByDefault;
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.Clock;
import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadListViewModel;
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.client.PostHeader;
import org.briarproject.briar.api.forum.Forum;
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.ForumSharingManager;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
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.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -98,12 +103,52 @@ class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
List<ForumPostHeader> headers =
forumManager.getPostHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start);
List<ForumPostItem> items =
buildItems(txn, headers, this::buildItem);
return new MessageTreeImpl<>(items).depthFirstOrder();
return recreateItems(txn, headers, this::buildItem);
}, 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) {
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.EventBus;
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.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
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.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.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import org.briarproject.briar.api.privategroup.JoinMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -36,37 +30,32 @@ import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class GroupControllerImpl extends
ThreadListControllerImpl<GroupMessageItem, GroupMessageHeader, GroupMessage, GroupListener>
class GroupControllerImpl extends ThreadListControllerImpl<GroupMessageItem>
implements GroupController {
private static final Logger LOG =
Logger.getLogger(GroupControllerImpl.class.getName());
getLogger(GroupControllerImpl.class.getName());
private final PrivateGroupManager privateGroupManager;
private final GroupMessageFactory groupMessageFactory;
@Inject
GroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor,
PrivateGroupManager privateGroupManager,
GroupMessageFactory groupMessageFactory, EventBus eventBus,
MessageTracker messageTracker, Clock clock,
EventBus eventBus, Clock clock,
AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager, messageTracker);
eventBus, clock, notificationManager);
this.privateGroupManager = privateGroupManager;
this.groupMessageFactory = groupMessageFactory;
}
@Override
@@ -79,6 +68,8 @@ class GroupControllerImpl extends
public void eventOccurred(Event e) {
super.eventOccurred(e);
GroupListener listener = (GroupListener) this.listener;
if (e instanceof GroupMessageAddedEvent) {
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
if (!g.isLocal() && g.getGroupId().equals(getGroupId())) {
@@ -132,52 +123,7 @@ class GroupControllerImpl extends
});
}
@Override
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) {
private GroupMessageItem buildItem(GroupMessageHeader header, String text) {
if (header instanceof JoinMessageHeader) {
return new JoinMessageItem((JoinMessageHeader) header, text);
}

View File

@@ -58,7 +58,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
GroupMessageItem item = getItem(position);
if (item instanceof JoinMessageItem) {
((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
}
@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.identity.Author;
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.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
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.Clock;
import org.briarproject.briar.android.threaded.ThreadListViewModel;
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.client.PostHeader;
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.JoinMessageHeader;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.client.MessageTreeImpl;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.lang.Math.max;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
@@ -109,9 +114,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
List<GroupMessageHeader> headers =
privateGroupManager.getHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start);
List<GroupMessageItem> items =
buildItems(txn, headers, this::buildItem);
return new MessageTreeImpl<>(items).depthFirstOrder();
return recreateItems(txn, headers, this::buildItem);
}, this::setItems);
}
@@ -132,6 +135,52 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
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() {
runOnDbThread(() -> {
try {

View File

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

View File

@@ -136,6 +136,12 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
loadSharingContacts();
}
@Override
protected void onDestroy() {
super.onDestroy();
getViewModel().storeMessageId(getFirstVisibleMessageId());
}
@Override
@Nullable
public MessageId getFirstVisibleMessageId() {
@@ -154,21 +160,29 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
if (items.isEmpty()) {
list.showData();
} else {
adapter.submitList(items);
// 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);
}
adapter.submitList(items, this::scrollAfterListCommitted);
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() {
getController().loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>, DbException>(
@@ -206,10 +220,6 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (layoutManager != null) {
layoutManagerState = layoutManager.onSaveInstanceState();
outState.putParcelable("layoutManager", layoutManagerState);
}
if (replyId != null) {
outState.putByteArray(KEY_REPLY_ID, replyId.getBytes());
}
@@ -218,7 +228,6 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
layoutManagerState = savedInstanceState.getParcelable("layoutManager");
}
@Override
@@ -247,11 +256,11 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
updateTextInput();
// FIXME This does not work for a hardware keyboard
if (textInput.isKeyboardOpen()) {
scrollToItemAtTop(item);
scrollToItemAtTop(item.getId());
} else {
// wait with scrolling until keyboard opened
textInput.setOnKeyboardShownListener(() -> {
scrollToItemAtTop(item);
scrollToItemAtTop(item.getId());
textInput.setOnKeyboardShownListener(null);
});
}
@@ -277,11 +286,10 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
}
}
private void scrollToItemAtTop(I item) {
int position = NO_POSITION;// adapter.findItemPosition(item);
private void scrollToItemAtTop(MessageId messageId) {
int position = adapter.findItemPosition(messageId);
if (position != NO_POSITION) {
layoutManager
.scrollToPositionWithOffset(position, 0);
layoutManager.scrollToPositionWithOffset(position, 0);
}
}
@@ -307,19 +315,7 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
if (isNullOrEmpty(text)) throw new AssertionError();
I replyItem = adapter.getHighlightedItem();
UiResultExceptionHandler<I, DbException> handler =
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);
getViewModel().createAndStoreMessage(text, replyItem);
textInput.hideSoftKeyboard();
textInput.clearText();
replyId = null;
@@ -330,7 +326,7 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
@Override
public void onItemReceived(I item) {
addItem(item, false);
getViewModel().addItem(item, false);
}
@Override
@@ -338,21 +334,4 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
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 createAndStoreMessage(String text, @Nullable I parentItem,
ResultExceptionHandler<I, DbException> handler);
interface ThreadListListener<I> extends ThreadListDataSource {
@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.system.Clock;
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.client.MessageTracker;
import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.client.ThreadedMessage;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -40,39 +33,34 @@ import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault
@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
implements ThreadListController<I>, EventListener {
private static final Logger LOG =
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 final EventBus eventBus;
protected final IdentityManager identityManager;
protected final AndroidNotificationManager notificationManager;
protected final Executor cryptoExecutor;
protected final Clock clock;
// UI thread
protected L listener;
protected ThreadListListener<I> listener;
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
Clock clock, AndroidNotificationManager notificationManager,
MessageTracker messageTracker) {
Clock clock, AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager);
this.identityManager = identityManager;
this.cryptoExecutor = cryptoExecutor;
this.notificationManager = notificationManager;
this.clock = clock;
this.eventBus = eventBus;
this.messageTracker = messageTracker;
}
@Override
@@ -84,7 +72,7 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@SuppressWarnings("unchecked")
@Override
public void onActivityCreate(Activity activity) {
listener = (L) activity;
listener = (ThreadListListener<I>) activity;
}
@CallSuper
@@ -103,16 +91,7 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@Override
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
@@ -150,27 +129,6 @@ public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends P
@DatabaseExecutor
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() {
checkGroupId();
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.api.android.AndroidNotificationManager;
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.client.MessageTreeImpl;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewModel
public abstract class ThreadListViewModel<I extends ThreadItem>
extends DbViewModel
implements EventListener {
private static final Logger LOG =
@@ -55,11 +63,18 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
private final MessageTracker messageTracker;
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 =
new MutableLiveData<>();
private final AtomicReference<MessageId> scrollToItem =
new AtomicReference<>();
protected volatile GroupId groupId;
private final AtomicReference<MessageId> storedMessageId =
new AtomicReference<>();
public ThreadListViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
@@ -95,18 +110,37 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
@CallSuper
public void setGroupId(GroupId groupId) {
this.groupId = groupId;
loadStoredMessageId();
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 createAndStoreMessage(String text,
@Nullable I parentItem);
@UiThread
protected void setItems(LiveResult<List<I>> items) {
this.items.setValue(items);
}
@DatabaseExecutor
protected <H extends PostHeader> List<I> buildItems(
protected <H extends PostHeader> List<I> recreateItems(
Transaction txn, Collection<H> headers, ItemGetter<H, I> itemGetter)
throws DbException {
long start = now();
@@ -122,23 +156,45 @@ public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewMo
}
logDuration(LOG, "Loading bodies and creating items", start);
MessageId msgId = messageTracker.loadStoredMessageId(txn, groupId);
if (LOG.isLoggable(INFO)) {
LOG.info("Loaded last top visible message id " + msgId);
}
// TODO store this elsewhere
items.setFirstVisibleId(msgId);
return items;
messageTree.clear();
messageTree.add(items);
return messageTree.depthFirstOrder();
}
protected void addItem(I item, boolean local) {
messageTree.add(item);
if (local) scrollToItem.set(item.getId());
items.postValue(new LiveResult<>(messageTree.depthFirstOrder()));
}
@DatabaseExecutor
protected abstract String loadMessageText(Transaction txn,
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() {
return items;
}
@Nullable
MessageId getAndResetScrollToItem() {
return scrollToItem.getAndSet(null);
}
public interface ItemGetter<H extends PostHeader, I> {
I getItem(H header, String text);
}

View File

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

View File

@@ -109,7 +109,6 @@ public interface ForumManager {
/**
* Returns the group count for the given forum.
*/
@Deprecated
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) ->
Long.valueOf(o1.getTimestamp()).compareTo(o2.getTimestamp());
public MessageTreeImpl(Collection<T> collection) {
super();
add(collection);
}
@Override
public synchronized void clear() {
roots.clear();