mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Submit thread list items to ListAdapter
This commit is contained in:
@@ -19,7 +19,6 @@ import org.briarproject.briar.android.threaded.ThreadItemAdapter;
|
||||
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
@@ -37,7 +36,7 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_TEX
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ForumActivity extends
|
||||
ThreadListActivity<Forum, ForumPostItem, ThreadItemAdapter<ForumPostItem>>
|
||||
ThreadListActivity<ForumPostItem, ThreadItemAdapter<ForumPostItem>>
|
||||
implements ForumListener {
|
||||
|
||||
@Inject
|
||||
@@ -55,12 +54,12 @@ public class ForumActivity extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThreadListController<Forum, ForumPostItem> getController() {
|
||||
protected ThreadListController<ForumPostItem> getController() {
|
||||
return forumController;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThreadListViewModel<Forum, ForumPostItem> getViewModel() {
|
||||
protected ThreadListViewModel<ForumPostItem> getViewModel() {
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,11 @@ package org.briarproject.briar.android.forum;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
@NotNullByDefault
|
||||
interface ForumController extends ThreadListController<Forum, ForumPostItem> {
|
||||
interface ForumController extends ThreadListController<ForumPostItem> {
|
||||
|
||||
interface ForumListener extends ThreadListListener<ForumPostItem> {
|
||||
@UiThread
|
||||
|
||||
@@ -19,7 +19,6 @@ 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.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
import org.briarproject.briar.api.forum.ForumPost;
|
||||
@@ -43,7 +42,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
class ForumControllerImpl extends
|
||||
ThreadListControllerImpl<Forum, ForumPostItem, ForumPostHeader, ForumPost, ForumListener>
|
||||
ThreadListControllerImpl<ForumPostItem, ForumPostHeader, ForumPost, ForumListener>
|
||||
implements ForumController {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -98,16 +97,6 @@ class ForumControllerImpl extends
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ForumPostHeader> loadHeaders() throws DbException {
|
||||
return forumManager.getPostHeaders(getGroupId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageText(ForumPostHeader h) throws DbException {
|
||||
return forumManager.getPostText(h.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markRead(MessageId id) throws DbException {
|
||||
forumManager.setReadFlag(getGroupId(), id, true);
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.widget.Toast;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -18,10 +19,15 @@ 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.PostHeader;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
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;
|
||||
|
||||
@@ -33,11 +39,13 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
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
|
||||
class ForumViewModel extends ThreadListViewModel<Forum, ForumPostItem> {
|
||||
class ForumViewModel extends ThreadListViewModel<ForumPostItem> {
|
||||
|
||||
private static final Logger LOG = getLogger(ForumViewModel.class.getName());
|
||||
|
||||
@@ -54,12 +62,13 @@ class ForumViewModel extends ThreadListViewModel<Forum, ForumPostItem> {
|
||||
AndroidNotificationManager notificationManager,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
Clock clock,
|
||||
MessageTracker messageTracker,
|
||||
EventBus eventBus,
|
||||
ForumManager forumManager,
|
||||
ForumSharingManager forumSharingManager) {
|
||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor,
|
||||
identityManager, notificationManager, cryptoExecutor, clock,
|
||||
eventBus);
|
||||
messageTracker, eventBus);
|
||||
this.forumManager = forumManager;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
}
|
||||
@@ -82,6 +91,29 @@ class ForumViewModel extends ThreadListViewModel<Forum, ForumPostItem> {
|
||||
return forum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadItems() {
|
||||
loadList(txn -> {
|
||||
long start = now();
|
||||
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();
|
||||
}, this::setItems);
|
||||
}
|
||||
|
||||
private ForumPostItem buildItem(ForumPostHeader header, String text) {
|
||||
return new ForumPostItem(header, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageText(Transaction txn, PostHeader header)
|
||||
throws DbException {
|
||||
return forumManager.getPostText(txn, header.getId());
|
||||
}
|
||||
|
||||
void deleteForum() {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity
|
||||
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.briar.api.privategroup.Visibility;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -41,7 +40,7 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class GroupActivity extends
|
||||
ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageAdapter>
|
||||
ThreadListActivity<GroupMessageItem, GroupMessageAdapter>
|
||||
implements GroupListener {
|
||||
|
||||
@Inject
|
||||
@@ -65,12 +64,12 @@ public class GroupActivity extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThreadListController<PrivateGroup, GroupMessageItem> getController() {
|
||||
protected ThreadListController<GroupMessageItem> getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThreadListViewModel<PrivateGroup, GroupMessageItem> getViewModel() {
|
||||
protected ThreadListViewModel<GroupMessageItem> getViewModel() {
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@@ -103,22 +102,11 @@ public class GroupActivity extends
|
||||
}
|
||||
|
||||
setGroupEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupMessageAdapter createAdapter(
|
||||
LinearLayoutManager layoutManager) {
|
||||
return new GroupMessageAdapter(this, layoutManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadItems() {
|
||||
controller.isDissolved(
|
||||
new UiResultExceptionHandler<Boolean, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Boolean isDissolved) {
|
||||
setGroupEnabled(!isDissolved);
|
||||
GroupActivity.super.loadItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,6 +116,12 @@ public class GroupActivity extends
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupMessageAdapter createAdapter(
|
||||
LinearLayoutManager layoutManager) {
|
||||
return new GroupMessageAdapter(this, layoutManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu items for use in the action bar
|
||||
|
||||
@@ -5,13 +5,12 @@ import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.briar.api.privategroup.Visibility;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
public interface GroupController
|
||||
extends ThreadListController<PrivateGroup, GroupMessageItem> {
|
||||
extends ThreadListController<GroupMessageItem> {
|
||||
|
||||
void isDissolved(
|
||||
ResultExceptionHandler<Boolean, DbException> handler);
|
||||
|
||||
@@ -24,7 +24,6 @@ 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.api.privategroup.event.ContactRelationshipRevealedEvent;
|
||||
import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent;
|
||||
@@ -47,7 +46,7 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
class GroupControllerImpl extends
|
||||
ThreadListControllerImpl<PrivateGroup, GroupMessageItem, GroupMessageHeader, GroupMessage, GroupListener>
|
||||
ThreadListControllerImpl<GroupMessageItem, GroupMessageHeader, GroupMessage, GroupListener>
|
||||
implements GroupController {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -108,21 +107,6 @@ class GroupControllerImpl extends
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<GroupMessageHeader> loadHeaders() throws DbException {
|
||||
return privateGroupManager.getHeaders(getGroupId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageText(GroupMessageHeader header)
|
||||
throws DbException {
|
||||
if (header instanceof JoinMessageHeader) {
|
||||
// will be looked up later
|
||||
return "";
|
||||
}
|
||||
return privateGroupManager.getMessageText(header.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markRead(MessageId id) throws DbException {
|
||||
privateGroupManager.setReadFlag(getGroupId(), id, true);
|
||||
|
||||
@@ -32,7 +32,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
||||
@LayoutRes
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
GroupMessageItem item = items.get(position);
|
||||
GroupMessageItem item = getItem(position);
|
||||
return item.getLayout();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
||||
void updateVisibility(AuthorId memberId, Visibility v) {
|
||||
int position = findItemPosition(memberId);
|
||||
if (position != NO_POSITION) {
|
||||
GroupMessageItem item = items.get(position);
|
||||
GroupMessageItem item = getItem(position);
|
||||
if (item instanceof JoinMessageItem) {
|
||||
((JoinMessageItem) item).setVisibility(v);
|
||||
notifyItemChanged(findItemPosition(item), item);
|
||||
@@ -63,14 +63,22 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private int findItemPosition(AuthorId a) {
|
||||
int count = items.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
GroupMessageItem item = items.get(i);
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
GroupMessageItem item = getItem(i);
|
||||
if (item.getAuthor().getId().equals(a))
|
||||
return i;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.Application;
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -18,10 +19,16 @@ 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.PostHeader;
|
||||
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;
|
||||
|
||||
@@ -32,20 +39,22 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
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
|
||||
class GroupViewModel
|
||||
extends ThreadListViewModel<PrivateGroup, GroupMessageItem> {
|
||||
class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
||||
|
||||
private static final Logger LOG = getLogger(GroupViewModel.class.getName());
|
||||
|
||||
private final PrivateGroupManager privateGroupManager;
|
||||
private final GroupMessageFactory groupMessageFactory;
|
||||
|
||||
MutableLiveData<PrivateGroup> privateGroup = new MutableLiveData<>();
|
||||
MutableLiveData<Boolean> isCreator = new MutableLiveData<>();
|
||||
private final MutableLiveData<PrivateGroup> privateGroup =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> isCreator = new MutableLiveData<>();
|
||||
|
||||
@Inject
|
||||
GroupViewModel(Application application,
|
||||
@@ -58,11 +67,12 @@ class GroupViewModel
|
||||
AndroidNotificationManager notificationManager,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
Clock clock,
|
||||
MessageTracker messageTracker,
|
||||
PrivateGroupManager privateGroupManager,
|
||||
GroupMessageFactory groupMessageFactory) {
|
||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor,
|
||||
identityManager, notificationManager, cryptoExecutor, clock,
|
||||
eventBus);
|
||||
messageTracker, eventBus);
|
||||
this.privateGroupManager = privateGroupManager;
|
||||
this.groupMessageFactory = groupMessageFactory;
|
||||
}
|
||||
@@ -91,6 +101,37 @@ class GroupViewModel
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadItems() {
|
||||
loadList(txn -> {
|
||||
// TODO first check if group is dissolved
|
||||
long start = now();
|
||||
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();
|
||||
}, this::setItems);
|
||||
}
|
||||
|
||||
private GroupMessageItem buildItem(GroupMessageHeader header, String text) {
|
||||
if (header instanceof JoinMessageHeader) {
|
||||
return new JoinMessageItem((JoinMessageHeader) header, text);
|
||||
}
|
||||
return new GroupMessageItem(header, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String loadMessageText(
|
||||
Transaction txn, PostHeader header) throws DbException {
|
||||
if (header instanceof JoinMessageHeader) {
|
||||
// will be looked up later
|
||||
return "";
|
||||
}
|
||||
return privateGroupManager.getMessageText(txn, header.getId());
|
||||
}
|
||||
|
||||
void deletePrivateGroup() {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.briarproject.briar.android.threaded;
|
||||
|
||||
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.MessageNode;
|
||||
import org.briarproject.briar.client.MessageTreeImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
public class NestedTreeList<T extends MessageNode> implements Iterable<T> {
|
||||
|
||||
private final MessageTree<T> tree = new MessageTreeImpl<>();
|
||||
private List<T> depthFirstCollection = new ArrayList<>();
|
||||
|
||||
public void addAll(Collection<T> collection) {
|
||||
tree.add(collection);
|
||||
depthFirstCollection = new ArrayList<>(tree.depthFirstOrder());
|
||||
}
|
||||
|
||||
public void add(T elem) {
|
||||
tree.add(elem);
|
||||
depthFirstCollection = new ArrayList<>(tree.depthFirstOrder());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
tree.clear();
|
||||
depthFirstCollection.clear();
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return depthFirstCollection.get(index);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return depthFirstCollection.size();
|
||||
}
|
||||
|
||||
public boolean contains(MessageId m) {
|
||||
return tree.contains(m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return depthFirstCollection.iterator();
|
||||
}
|
||||
}
|
||||
@@ -99,4 +99,14 @@ public abstract class ThreadItem implements MessageNode {
|
||||
return highlighted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return messageId.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return o instanceof ThreadItem &&
|
||||
messageId.equals(((ThreadItem) o).messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,37 +4,48 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.ItemReturningAdapter;
|
||||
import org.briarproject.briar.android.util.VersionedAdapter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
|
||||
import static androidx.recyclerview.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
@UiThread
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ThreadItemAdapter<I extends ThreadItem>
|
||||
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
|
||||
implements VersionedAdapter, ItemReturningAdapter<I> {
|
||||
extends ListAdapter<I, BaseThreadItemViewHolder<I>>
|
||||
implements ItemReturningAdapter<I> {
|
||||
|
||||
static final int UNDEFINED = -1;
|
||||
|
||||
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
||||
private final ThreadItemListener<I> listener;
|
||||
private final LinearLayoutManager layoutManager;
|
||||
|
||||
private volatile int revision = 0;
|
||||
|
||||
public ThreadItemAdapter(ThreadItemListener<I> listener,
|
||||
LinearLayoutManager layoutManager) {
|
||||
super(new DiffUtil.ItemCallback<I>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(I a, I b) {
|
||||
return a.equals(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(I a, I b) {
|
||||
return a.isHighlighted() == b.isHighlighted() &&
|
||||
a.isRead() && b.isRead();
|
||||
}
|
||||
});
|
||||
this.listener = listener;
|
||||
this.layoutManager = layoutManager;
|
||||
}
|
||||
@@ -51,28 +62,14 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull BaseThreadItemViewHolder<I> ui,
|
||||
int position) {
|
||||
I item = items.get(position);
|
||||
I item = getItem(position);
|
||||
ui.bind(item, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void incrementRevision() {
|
||||
revision++;
|
||||
}
|
||||
|
||||
void setItemWithIdVisible(MessageId messageId) {
|
||||
int pos = 0;
|
||||
for (I item : items) {
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
I item = getItem(i);
|
||||
if (item.getId().equals(messageId)) {
|
||||
layoutManager.scrollToPosition(pos);
|
||||
break;
|
||||
@@ -81,46 +78,16 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
}
|
||||
}
|
||||
|
||||
public void setItems(Collection<I> items) {
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void add(I item) {
|
||||
items.add(item);
|
||||
notifyItemInserted(findItemPosition(item));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public I getItemAt(int position) {
|
||||
if (position == NO_POSITION || position >= items.size()) {
|
||||
return null;
|
||||
}
|
||||
return items.get(position);
|
||||
}
|
||||
|
||||
protected int findItemPosition(@Nullable I item) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (items.get(i).equals(item)) return i;
|
||||
}
|
||||
return NO_POSITION; // Not found
|
||||
}
|
||||
|
||||
boolean contains(MessageId m) {
|
||||
return items.contains(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the item with the given {@link MessageId}
|
||||
* and disables the highlight for a previously highlighted item, if any.
|
||||
*
|
||||
* <p>
|
||||
* Only one item can be highlighted at a time.
|
||||
*/
|
||||
void setHighlightedItem(@Nullable MessageId id) {
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
I item = items.get(i);
|
||||
if (id != null && item.getId().equals(id)) {
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
I item = getItem(i);
|
||||
if (item.getId().equals(id)) {
|
||||
item.setHighlighted(true);
|
||||
notifyItemChanged(i, item);
|
||||
} else if (item.isHighlighted()) {
|
||||
@@ -132,8 +99,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
|
||||
@Nullable
|
||||
I getHighlightedItem() {
|
||||
for (I i : items) {
|
||||
if (i.isHighlighted()) return i;
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
I item = getItem(i);
|
||||
if (item.isHighlighted()) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -144,8 +112,8 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
int getVisibleUnreadPosBottom() {
|
||||
int positionBottom = layoutManager.findLastVisibleItemPosition();
|
||||
if (positionBottom == NO_POSITION) return NO_POSITION;
|
||||
for (int i = positionBottom + 1; i < items.size(); i++) {
|
||||
if (!items.get(i).isRead()) return i;
|
||||
for (int i = positionBottom + 1; i < getItemCount(); i++) {
|
||||
if (!getItem(i).isRead()) return i;
|
||||
}
|
||||
return NO_POSITION;
|
||||
}
|
||||
@@ -156,8 +124,8 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
int getVisibleUnreadPosTop() {
|
||||
int positionTop = layoutManager.findFirstVisibleItemPosition();
|
||||
int position = NO_POSITION;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
if (i < positionTop && !items.get(i).isRead()) {
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
if (i < positionTop && !getItem(i).isRead()) {
|
||||
position = i;
|
||||
} else if (i >= positionTop) {
|
||||
return position;
|
||||
@@ -166,6 +134,11 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
return NO_POSITION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public I getItemAt(int position) {
|
||||
return getItem(position);
|
||||
}
|
||||
|
||||
public interface ThreadItemListener<I> {
|
||||
void onReplyClick(I item);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.briarproject.briar.android.view.TextSendController;
|
||||
import org.briarproject.briar.android.view.TextSendController.SendListener;
|
||||
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
||||
import org.briarproject.briar.api.client.NamedGroup;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -48,7 +47,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadItem, A extends ThreadItemAdapter<I>>
|
||||
public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadItemAdapter<I>>
|
||||
extends BriarActivity
|
||||
implements ThreadListListener<I>, SendListener, SharingListener,
|
||||
ThreadItemListener<I>, ThreadListDataSource {
|
||||
@@ -59,6 +58,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
getLogger(ThreadListActivity.class.getName());
|
||||
|
||||
protected A adapter;
|
||||
|
||||
private ThreadScrollListener<I> scrollListener;
|
||||
protected BriarRecyclerView list;
|
||||
private LinearLayoutManager layoutManager;
|
||||
@@ -70,9 +70,9 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
@Nullable
|
||||
private MessageId replyId;
|
||||
|
||||
protected abstract ThreadListController<G, I> getController();
|
||||
protected abstract ThreadListController<I> getController();
|
||||
|
||||
protected abstract ThreadListViewModel<G, I> getViewModel();
|
||||
protected abstract ThreadListViewModel<I> getViewModel();
|
||||
|
||||
@Inject
|
||||
protected SharingController sharingController;
|
||||
@@ -127,6 +127,11 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
|
||||
}
|
||||
|
||||
getViewModel().getItems().observe(this, result -> result
|
||||
.onError(this::handleException)
|
||||
.onSuccess(this::displayItems)
|
||||
);
|
||||
|
||||
sharingController.setSharingListener(this);
|
||||
loadSharingContacts();
|
||||
}
|
||||
@@ -145,44 +150,22 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
|
||||
protected abstract A createAdapter(LinearLayoutManager layoutManager);
|
||||
|
||||
protected void loadItems() {
|
||||
int revision = adapter.getRevision();
|
||||
getController().loadItems(
|
||||
new UiResultExceptionHandler<ThreadItemList<I>, DbException>(
|
||||
this) {
|
||||
@Override
|
||||
public void onResultUi(ThreadItemList<I> items) {
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (items.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
displayItems(items);
|
||||
updateTextInput();
|
||||
}
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
loadItems();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
handleException(exception);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayItems(ThreadItemList<I> items) {
|
||||
adapter.setItems(items);
|
||||
MessageId messageId = items.getFirstVisibleItemId();
|
||||
if (messageId != null)
|
||||
adapter.setItemWithIdVisible(messageId);
|
||||
list.showData();
|
||||
if (layoutManagerState == null) {
|
||||
list.scrollToPosition(0); // Scroll to the top
|
||||
protected void displayItems(List<I> items) {
|
||||
if (items.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
layoutManager.onRestoreInstanceState(layoutManagerState);
|
||||
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);
|
||||
}
|
||||
updateTextInput();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +192,6 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
sharingController.onStart();
|
||||
loadItems();
|
||||
list.startPeriodicUpdate();
|
||||
}
|
||||
|
||||
@@ -296,7 +278,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
}
|
||||
|
||||
private void scrollToItemAtTop(I item) {
|
||||
int position = adapter.findItemPosition(item);
|
||||
int position = NO_POSITION;// adapter.findItemPosition(item);
|
||||
if (position != NO_POSITION) {
|
||||
layoutManager
|
||||
.scrollToPositionWithOffset(position, 0);
|
||||
@@ -357,15 +339,14 @@ public abstract class ThreadListActivity<G extends NamedGroup, I extends ThreadI
|
||||
}
|
||||
|
||||
private void addItem(I item, boolean isLocal) {
|
||||
adapter.incrementRevision();
|
||||
MessageId parent = item.getParentId();
|
||||
if (parent != null && !adapter.contains(parent)) {
|
||||
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;
|
||||
}
|
||||
adapter.add(item);
|
||||
// TODO submit new list
|
||||
|
||||
if (isLocal) {
|
||||
scrollToItemAtTop(item);
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.api.client.NamedGroup;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -16,7 +15,7 @@ import javax.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface ThreadListController<G extends NamedGroup, I extends ThreadItem>
|
||||
public interface ThreadListController<I extends ThreadItem>
|
||||
extends ActivityLifecycleController {
|
||||
|
||||
void setGroupId(GroupId groupId);
|
||||
@@ -24,9 +23,6 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
void loadSharingContacts(
|
||||
ResultExceptionHandler<Collection<ContactId>, DbException> handler);
|
||||
|
||||
void loadItems(
|
||||
ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
|
||||
|
||||
void markItemRead(I item);
|
||||
|
||||
void markItemsRead(Collection<I> items);
|
||||
@@ -48,7 +44,8 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
|
||||
interface ThreadListDataSource {
|
||||
|
||||
@UiThread @Nullable
|
||||
@UiThread
|
||||
@Nullable
|
||||
MessageId getFirstVisibleMessageId();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ 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.NamedGroup;
|
||||
import org.briarproject.briar.api.client.PostHeader;
|
||||
import org.briarproject.briar.api.client.ThreadedMessage;
|
||||
|
||||
@@ -34,7 +33,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
@@ -42,9 +40,9 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<I>>
|
||||
public abstract class ThreadListControllerImpl<I extends ThreadItem, H extends PostHeader, M extends ThreadedMessage, L extends ThreadListListener<I>>
|
||||
extends DbControllerImpl
|
||||
implements ThreadListController<G, I>, EventListener {
|
||||
implements ThreadListController<I>, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ThreadListControllerImpl.class.getName());
|
||||
@@ -129,42 +127,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadItems(
|
||||
ResultExceptionHandler<ThreadItemList<I>, DbException> handler) {
|
||||
checkGroupId();
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
// Load headers
|
||||
long start = now();
|
||||
Collection<H> headers = loadHeaders();
|
||||
logDuration(LOG, "Loading headers", start);
|
||||
|
||||
// Load bodies into cache
|
||||
start = now();
|
||||
for (H header : headers) {
|
||||
if (!textCache.containsKey(header.getId())) {
|
||||
textCache.put(header.getId(),
|
||||
loadMessageText(header));
|
||||
}
|
||||
}
|
||||
logDuration(LOG, "Loading bodies", start);
|
||||
|
||||
// Build and hand over items
|
||||
handler.onResult(buildItems(headers));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
handler.onException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract Collection<H> loadHeaders() throws DbException;
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract String loadMessageText(H header) throws DbException;
|
||||
|
||||
@Override
|
||||
public void markItemRead(I item) {
|
||||
markItemsRead(Collections.singletonList(item));
|
||||
@@ -207,19 +169,6 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
@DatabaseExecutor
|
||||
protected abstract H addLocalMessage(M message) throws DbException;
|
||||
|
||||
private ThreadItemList<I> buildItems(Collection<H> headers)
|
||||
throws DbException {
|
||||
ThreadItemList<I> items = new ThreadItemListImpl<>();
|
||||
for (H h : headers) {
|
||||
items.add(buildItem(h, textCache.get(h.getId())));
|
||||
}
|
||||
MessageId msgId = messageTracker.loadStoredMessageId(groupId);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded last top visible message id " + msgId);
|
||||
items.setFirstVisibleId(msgId);
|
||||
return items;
|
||||
}
|
||||
|
||||
protected abstract I buildItem(H header, String text);
|
||||
|
||||
protected GroupId getGroupId() {
|
||||
|
||||
@@ -4,6 +4,8 @@ import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
@@ -12,34 +14,51 @@ 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.viewmodel.DbViewModel;
|
||||
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.NamedGroup;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.PostHeader;
|
||||
|
||||
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.logging.Logger;
|
||||
|
||||
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.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public abstract class ThreadListViewModel<G extends NamedGroup, I extends ThreadItem>
|
||||
extends DbViewModel implements EventListener {
|
||||
public abstract class ThreadListViewModel<I extends ThreadItem> extends DbViewModel
|
||||
implements EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ThreadListViewModel.class.getName());
|
||||
|
||||
|
||||
protected final IdentityManager identityManager;
|
||||
protected final AndroidNotificationManager notificationManager;
|
||||
protected final Executor cryptoExecutor;
|
||||
protected final Clock clock;
|
||||
private final MessageTracker messageTracker;
|
||||
private final EventBus eventBus;
|
||||
|
||||
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||
private final MutableLiveData<LiveResult<List<I>>> items =
|
||||
new MutableLiveData<>();
|
||||
|
||||
protected volatile GroupId groupId;
|
||||
|
||||
public ThreadListViewModel(Application application,
|
||||
@@ -51,12 +70,14 @@ public abstract class ThreadListViewModel<G extends NamedGroup, I extends Thread
|
||||
AndroidNotificationManager notificationManager,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
Clock clock,
|
||||
MessageTracker messageTracker,
|
||||
EventBus eventBus) {
|
||||
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.identityManager = identityManager;
|
||||
this.notificationManager = notificationManager;
|
||||
this.cryptoExecutor = cryptoExecutor;
|
||||
this.clock = clock;
|
||||
this.messageTracker = messageTracker;
|
||||
this.eventBus = eventBus;
|
||||
this.eventBus.addListener(this);
|
||||
}
|
||||
@@ -74,6 +95,52 @@ public abstract class ThreadListViewModel<G extends NamedGroup, I extends Thread
|
||||
@CallSuper
|
||||
public void setGroupId(GroupId groupId) {
|
||||
this.groupId = groupId;
|
||||
loadItems();
|
||||
}
|
||||
|
||||
public abstract void loadItems();
|
||||
|
||||
@UiThread
|
||||
protected void setItems(LiveResult<List<I>> items) {
|
||||
this.items.setValue(items);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
protected <H extends PostHeader> List<I> buildItems(
|
||||
Transaction txn, Collection<H> headers, ItemGetter<H, I> itemGetter)
|
||||
throws DbException {
|
||||
long start = now();
|
||||
ThreadItemList<I> items = new ThreadItemListImpl<>();
|
||||
for (H header : headers) {
|
||||
MessageId id = header.getId();
|
||||
String text = textCache.get(header.getId());
|
||||
if (text == null) {
|
||||
text = loadMessageText(txn, header);
|
||||
textCache.put(id, text);
|
||||
}
|
||||
items.add(itemGetter.getItem(header, text));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract String loadMessageText(Transaction txn,
|
||||
PostHeader header) throws DbException;
|
||||
|
||||
LiveData<LiveResult<List<I>>> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public interface ItemGetter<H extends PostHeader, I> {
|
||||
I getItem(H header, String text);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,11 +20,11 @@ class ThreadScrollListener<I extends ThreadItem>
|
||||
private static final Logger LOG =
|
||||
getLogger(ThreadScrollListener.class.getName());
|
||||
|
||||
private final ThreadListController<?, I> controller;
|
||||
private final ThreadListController<I> controller;
|
||||
private final UnreadMessageButton upButton, downButton;
|
||||
|
||||
ThreadScrollListener(ThreadItemAdapter<I> adapter,
|
||||
ThreadListController<?, I> controller,
|
||||
ThreadListController<I> controller,
|
||||
UnreadMessageButton upButton,
|
||||
UnreadMessageButton downButton) {
|
||||
super(adapter);
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -13,14 +13,15 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
|
||||
|
||||
void add(Collection<T> nodes);
|
||||
|
||||
@Deprecated
|
||||
void add(T node);
|
||||
|
||||
void setComparator(Comparator<T> comparator);
|
||||
|
||||
@Deprecated
|
||||
void clear();
|
||||
|
||||
Collection<T> depthFirstOrder();
|
||||
List<T> depthFirstOrder();
|
||||
|
||||
@Deprecated
|
||||
boolean contains(MessageId m);
|
||||
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -30,9 +30,14 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
||||
private final List<List<T>> unsortedLists = new ArrayList<>();
|
||||
|
||||
@SuppressWarnings("UseCompareMethod")
|
||||
private Comparator<T> comparator = (o1, o2) ->
|
||||
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();
|
||||
@@ -79,6 +84,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
||||
@GuardedBy("this")
|
||||
private void sortUnsorted() {
|
||||
for (List<T> list : unsortedLists) {
|
||||
//noinspection Java8ListSort
|
||||
Collections.sort(list, comparator);
|
||||
}
|
||||
unsortedLists.clear();
|
||||
@@ -95,17 +101,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setComparator(Comparator<T> comparator) {
|
||||
this.comparator = comparator;
|
||||
// Sort all lists with the new comparator
|
||||
Collections.sort(roots, comparator);
|
||||
for (Map.Entry<MessageId, List<T>> entry : nodeMap.entrySet()) {
|
||||
Collections.sort(entry.getValue(), comparator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Collection<T> depthFirstOrder() {
|
||||
public synchronized List<T> depthFirstOrder() {
|
||||
List<T> orderedList = new ArrayList<>();
|
||||
for (T root : roots) {
|
||||
traverse(orderedList, root, 0);
|
||||
|
||||
Reference in New Issue
Block a user