mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
merge with master and fixes after comments
This commit is contained in:
@@ -19,6 +19,7 @@ import org.briarproject.R;
|
|||||||
import org.briarproject.android.ActivityComponent;
|
import org.briarproject.android.ActivityComponent;
|
||||||
import org.briarproject.android.BriarActivity;
|
import org.briarproject.android.BriarActivity;
|
||||||
import org.briarproject.android.api.AndroidNotificationManager;
|
import org.briarproject.android.api.AndroidNotificationManager;
|
||||||
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.android.controller.handler.UiResultHandler;
|
import org.briarproject.android.controller.handler.UiResultHandler;
|
||||||
import org.briarproject.android.forum.ForumController.ForumPostListener;
|
import org.briarproject.android.forum.ForumController.ForumPostListener;
|
||||||
import org.briarproject.android.forum.NestedForumAdapter.OnNestedForumListener;
|
import org.briarproject.android.forum.NestedForumAdapter.OnNestedForumListener;
|
||||||
@@ -27,8 +28,8 @@ import org.briarproject.android.sharing.SharingStatusForumActivity;
|
|||||||
import org.briarproject.android.view.BriarRecyclerView;
|
import org.briarproject.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.android.view.TextInputView;
|
import org.briarproject.android.view.TextInputView;
|
||||||
import org.briarproject.android.view.TextInputView.TextInputListener;
|
import org.briarproject.android.view.TextInputView.TextInputListener;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.forum.Forum;
|
import org.briarproject.api.forum.Forum;
|
||||||
import org.briarproject.api.forum.ForumPost;
|
|
||||||
import org.briarproject.api.forum.ForumPostHeader;
|
import org.briarproject.api.forum.ForumPostHeader;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
@@ -64,7 +65,6 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
|
|
||||||
private BriarRecyclerView recyclerView;
|
private BriarRecyclerView recyclerView;
|
||||||
private TextInputView textInput;
|
private TextInputView textInput;
|
||||||
private LinearLayoutManager linearLayoutManager;
|
|
||||||
|
|
||||||
private volatile GroupId groupId = null;
|
private volatile GroupId groupId = null;
|
||||||
|
|
||||||
@@ -93,29 +93,31 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
recyclerView.setEmptyText(R.string.no_forum_posts);
|
recyclerView.setEmptyText(R.string.no_forum_posts);
|
||||||
|
|
||||||
forumController.loadForum(groupId,
|
forumController.loadForum(groupId,
|
||||||
new UiResultHandler<List<ForumEntry>>(this) {
|
new UiResultExceptionHandler<List<ForumEntry>, DbException>(
|
||||||
|
this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(List<ForumEntry> result) {
|
public void onResultUi(List<ForumEntry> result) {
|
||||||
if (result != null) {
|
Forum forum = forumController.getForum();
|
||||||
Forum forum = forumController.getForum();
|
if (forum != null) setTitle(forum.getName());
|
||||||
if (forum != null) setTitle(forum.getName());
|
List<ForumEntry> entries = new ArrayList<>(result);
|
||||||
List<ForumEntry> entries = new ArrayList<>(result);
|
if (entries.isEmpty()) {
|
||||||
if (entries.isEmpty()) {
|
recyclerView.showData();
|
||||||
recyclerView.showData();
|
|
||||||
} else {
|
|
||||||
forumAdapter.setEntries(entries);
|
|
||||||
if (state != null) {
|
|
||||||
byte[] replyId =
|
|
||||||
state.getByteArray(KEY_REPLY_ID);
|
|
||||||
if (replyId != null)
|
|
||||||
forumAdapter.setReplyEntryById(replyId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// TODO Improve UX ?
|
forumAdapter.setEntries(entries);
|
||||||
finish();
|
if (state != null) {
|
||||||
|
byte[] replyId =
|
||||||
|
state.getByteArray(KEY_REPLY_ID);
|
||||||
|
if (replyId != null)
|
||||||
|
forumAdapter.setReplyEntryById(replyId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO Improve UX ?
|
||||||
|
finish();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,22 +250,21 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
public void onSendClick(String text) {
|
public void onSendClick(String text) {
|
||||||
if (text.trim().length() == 0)
|
if (text.trim().length() == 0)
|
||||||
return;
|
return;
|
||||||
if (forumController.getForum() == null) return;
|
|
||||||
ForumEntry replyEntry = forumAdapter.getReplyEntry();
|
ForumEntry replyEntry = forumAdapter.getReplyEntry();
|
||||||
UiResultHandler<ForumPost> resultHandler =
|
UiResultExceptionHandler<ForumEntry, DbException> resultHandler =
|
||||||
new UiResultHandler<ForumPost>(this) {
|
new UiResultExceptionHandler<ForumEntry, DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(ForumPost result) {
|
public void onResultUi(ForumEntry result) {
|
||||||
forumController.storePost(result,
|
onForumEntryAdded(result, true);
|
||||||
new UiResultHandler<ForumEntry>(
|
}
|
||||||
ForumActivity.this) {
|
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(ForumEntry result) {
|
public void onExceptionUi(DbException exception) {
|
||||||
onForumEntryAdded(result, true);
|
// TODO Improve UX ?
|
||||||
}
|
finish();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (replyEntry == null) {
|
if (replyEntry == null) {
|
||||||
// root post
|
// root post
|
||||||
forumController.createPost(StringUtils.toUtf8(text), resultHandler);
|
forumController.createPost(StringUtils.toUtf8(text), resultHandler);
|
||||||
@@ -322,11 +323,14 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
|
|
||||||
private void onForumEntryAdded(final ForumEntry entry, boolean isLocal) {
|
private void onForumEntryAdded(final ForumEntry entry, boolean isLocal) {
|
||||||
forumAdapter.addEntry(entry);
|
forumAdapter.addEntry(entry);
|
||||||
if (isLocal) {
|
if (isLocal && forumAdapter.isVisible(entry)) {
|
||||||
displaySnackbarShort(R.string.forum_new_entry_posted);
|
displaySnackbarShort(R.string.forum_new_entry_posted);
|
||||||
} else {
|
} else {
|
||||||
Snackbar snackbar = Snackbar.make(recyclerView,
|
Snackbar snackbar = Snackbar.make(recyclerView,
|
||||||
R.string.forum_new_entry_received, Snackbar.LENGTH_LONG);
|
isLocal ? R.string.forum_new_entry_posted :
|
||||||
|
R.string.forum_new_entry_received,
|
||||||
|
Snackbar.LENGTH_LONG);
|
||||||
|
snackbar.getView().setBackgroundResource(R.color.briar_primary);
|
||||||
snackbar.setActionTextColor(ContextCompat
|
snackbar.setActionTextColor(ContextCompat
|
||||||
.getColor(ForumActivity.this,
|
.getColor(ForumActivity.this,
|
||||||
R.color.briar_button_positive));
|
R.color.briar_button_positive));
|
||||||
@@ -343,12 +347,18 @@ public class ForumActivity extends BriarActivity implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExternalEntryAdded(ForumPostHeader header) {
|
public void onExternalEntryAdded(ForumPostHeader header) {
|
||||||
forumController.loadPost(header, new UiResultHandler<ForumEntry>(this) {
|
forumController.loadPost(header,
|
||||||
@Override
|
new UiResultExceptionHandler<ForumEntry, DbException>(this) {
|
||||||
public void onResultUi(final ForumEntry result) {
|
@Override
|
||||||
onForumEntryAdded(result, false);
|
public void onResultUi(final ForumEntry result) {
|
||||||
}
|
onForumEntryAdded(result, false);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO add proper exception handling
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import android.support.annotation.Nullable;
|
|||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.android.controller.ActivityLifecycleController;
|
import org.briarproject.android.controller.ActivityLifecycleController;
|
||||||
|
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.android.controller.handler.ResultHandler;
|
import org.briarproject.android.controller.handler.ResultHandler;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.forum.Forum;
|
import org.briarproject.api.forum.Forum;
|
||||||
import org.briarproject.api.forum.ForumPost;
|
|
||||||
import org.briarproject.api.forum.ForumPostHeader;
|
import org.briarproject.api.forum.ForumPostHeader;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
@@ -17,13 +18,13 @@ import java.util.List;
|
|||||||
public interface ForumController extends ActivityLifecycleController {
|
public interface ForumController extends ActivityLifecycleController {
|
||||||
|
|
||||||
void loadForum(GroupId groupId,
|
void loadForum(GroupId groupId,
|
||||||
ResultHandler<List<ForumEntry>> resultHandler);
|
ResultExceptionHandler<List<ForumEntry>, DbException> resultHandler);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
Forum getForum();
|
Forum getForum();
|
||||||
|
|
||||||
void loadPost(ForumPostHeader header,
|
void loadPost(ForumPostHeader header,
|
||||||
ResultHandler<ForumEntry> resultHandler);
|
ResultExceptionHandler<ForumEntry, DbException> resultHandler);
|
||||||
|
|
||||||
void unsubscribe(ResultHandler<Boolean> resultHandler);
|
void unsubscribe(ResultHandler<Boolean> resultHandler);
|
||||||
|
|
||||||
@@ -31,12 +32,11 @@ public interface ForumController extends ActivityLifecycleController {
|
|||||||
|
|
||||||
void entriesRead(Collection<ForumEntry> messageIds);
|
void entriesRead(Collection<ForumEntry> messageIds);
|
||||||
|
|
||||||
void createPost(byte[] body, ResultHandler<ForumPost> resultHandler);
|
void createPost(byte[] body,
|
||||||
|
ResultExceptionHandler<ForumEntry, DbException> resultHandler);
|
||||||
|
|
||||||
void createPost(byte[] body, MessageId parentId,
|
void createPost(byte[] body, MessageId parentId,
|
||||||
ResultHandler<ForumPost> resultHandler);
|
ResultExceptionHandler<ForumEntry, DbException> resultHandler);
|
||||||
|
|
||||||
void storePost(ForumPost post, ResultHandler<ForumEntry> resultHandler);
|
|
||||||
|
|
||||||
interface ForumPostListener {
|
interface ForumPostListener {
|
||||||
@UiThread
|
@UiThread
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Activity;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.android.controller.DbControllerImpl;
|
import org.briarproject.android.controller.DbControllerImpl;
|
||||||
|
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.android.controller.handler.ResultHandler;
|
import org.briarproject.android.controller.handler.ResultHandler;
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
@@ -42,7 +43,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
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.Level.WARNING;
|
||||||
import static org.briarproject.api.identity.Author.Status.VERIFIED;
|
import static org.briarproject.api.identity.Author.Status.OURSELVES;
|
||||||
|
|
||||||
public class ForumControllerImpl extends DbControllerImpl
|
public class ForumControllerImpl extends DbControllerImpl
|
||||||
implements ForumController, EventListener {
|
implements ForumController, EventListener {
|
||||||
@@ -149,9 +150,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
// Get First Identity
|
// Get First Identity
|
||||||
now = System.currentTimeMillis();
|
now = System.currentTimeMillis();
|
||||||
localAuthor =
|
localAuthor = identityManager.getLocalAuthor();
|
||||||
identityManager.getLocalAuthors().iterator()
|
|
||||||
.next();
|
|
||||||
duration = System.currentTimeMillis() - now;
|
duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Loading author took " + duration + " ms");
|
LOG.info("Loading author took " + duration + " ms");
|
||||||
@@ -214,7 +213,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadForum(final GroupId groupId,
|
public void loadForum(final GroupId groupId,
|
||||||
final ResultHandler<List<ForumEntry>> resultHandler) {
|
final ResultExceptionHandler<List<ForumEntry>, DbException> resultHandler) {
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -231,7 +230,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
resultHandler.onResult(null);
|
resultHandler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -245,7 +244,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadPost(final ForumPostHeader header,
|
public void loadPost(final ForumPostHeader header,
|
||||||
final ResultHandler<ForumEntry> resultHandler) {
|
final ResultExceptionHandler<ForumEntry, DbException> resultHandler) {
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -255,7 +254,9 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
resultHandler.onResult(new ForumEntry(header, StringUtils
|
resultHandler.onResult(new ForumEntry(header, StringUtils
|
||||||
.fromUtf8(bodyCache.get(header.getId()))));
|
.fromUtf8(bodyCache.get(header.getId()))));
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
e.printStackTrace();
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
resultHandler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -311,13 +312,13 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPost(byte[] body,
|
public void createPost(byte[] body,
|
||||||
ResultHandler<ForumPost> resultHandler) {
|
ResultExceptionHandler<ForumEntry, DbException> resultHandler) {
|
||||||
createPost(body, null, resultHandler);
|
createPost(body, null, resultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createPost(final byte[] body, final MessageId parentId,
|
public void createPost(final byte[] body, final MessageId parentId,
|
||||||
final ResultHandler<ForumPost> resultHandler) {
|
final ResultExceptionHandler<ForumEntry, DbException> resultHandler) {
|
||||||
cryptoExecutor.execute(new Runnable() {
|
cryptoExecutor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -336,14 +337,13 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
bodyCache.put(p.getMessage().getId(), body);
|
bodyCache.put(p.getMessage().getId(), body);
|
||||||
resultHandler.onResult(p);
|
storePost(p, resultHandler);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void storePost(final ForumPost p,
|
||||||
public void storePost(final ForumPost p,
|
final ResultExceptionHandler<ForumEntry, DbException> resultHandler) {
|
||||||
final ResultHandler<ForumEntry> resultHandler) {
|
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -359,8 +359,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
new ForumPostHeader(p.getMessage().getId(),
|
new ForumPostHeader(p.getMessage().getId(),
|
||||||
p.getParent(),
|
p.getParent(),
|
||||||
p.getMessage().getTimestamp(),
|
p.getMessage().getTimestamp(),
|
||||||
p.getAuthor(), VERIFIED,
|
p.getAuthor(), OURSELVES, true);
|
||||||
true);
|
|
||||||
|
|
||||||
resultHandler.onResult(new ForumEntry(h, StringUtils
|
resultHandler.onResult(new ForumEntry(h, StringUtils
|
||||||
.fromUtf8(bodyCache.get(p.getMessage().getId()))));
|
.fromUtf8(bodyCache.get(p.getMessage().getId()))));
|
||||||
@@ -368,6 +367,7 @@ public class ForumControllerImpl extends DbControllerImpl
|
|||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
resultHandler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.api.identity.Author;
|
|||||||
import org.briarproject.api.identity.Author.Status;
|
import org.briarproject.api.identity.Author.Status;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
|
||||||
|
/* This class is not thread safe */
|
||||||
public class ForumEntry implements MessageTree.MessageNode {
|
public class ForumEntry implements MessageTree.MessageNode {
|
||||||
|
|
||||||
public final static int LEVEL_UNDEFINED = -1;
|
public final static int LEVEL_UNDEFINED = -1;
|
||||||
@@ -18,6 +19,7 @@ public class ForumEntry implements MessageTree.MessageNode {
|
|||||||
private Status status;
|
private Status status;
|
||||||
private int level = LEVEL_UNDEFINED;
|
private int level = LEVEL_UNDEFINED;
|
||||||
private boolean isShowingDescendants = true;
|
private boolean isShowingDescendants = true;
|
||||||
|
private int descendantCount = 0;
|
||||||
private boolean isRead = true;
|
private boolean isRead = true;
|
||||||
|
|
||||||
ForumEntry(ForumPostHeader h, String text) {
|
ForumEntry(ForumPostHeader h, String text) {
|
||||||
@@ -70,7 +72,7 @@ public class ForumEntry implements MessageTree.MessageNode {
|
|||||||
return isShowingDescendants;
|
return isShowingDescendants;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLevel(int level) {
|
public void setLevel(int level) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,4 +91,12 @@ public class ForumEntry implements MessageTree.MessageNode {
|
|||||||
void setRead(boolean read) {
|
void setRead(boolean read) {
|
||||||
isRead = read;
|
isRead = read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasDescendants() {
|
||||||
|
return descendantCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescendantCount(int descendantCount) {
|
||||||
|
this.descendantCount = descendantCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ package org.briarproject.android.forum;
|
|||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.ArgbEvaluator;
|
import android.animation.ArgbEvaluator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Build;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
@@ -26,7 +24,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
@@ -61,90 +58,80 @@ public class NestedForumAdapter
|
|||||||
return replyEntry;
|
return replyEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setForumEntryLevels() {
|
|
||||||
Stack<MessageId> idStack = new Stack<>();
|
|
||||||
for (ForumEntry forumEntry : forumEntries) {
|
|
||||||
if (forumEntry.getParentId() == null) {
|
|
||||||
idStack.clear();
|
|
||||||
} else if (idStack.isEmpty() ||
|
|
||||||
!idStack.contains(forumEntry.getParentId())) {
|
|
||||||
idStack.push(forumEntry.getParentId());
|
|
||||||
} else if (!forumEntry.getParentId().equals(idStack.peek())) {
|
|
||||||
do {
|
|
||||||
idStack.pop();
|
|
||||||
} while (!forumEntry.getParentId().equals(idStack.peek()));
|
|
||||||
}
|
|
||||||
forumEntry.setLevel(idStack.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEntries(List<ForumEntry> entries) {
|
void setEntries(List<ForumEntry> entries) {
|
||||||
forumEntries.clear();
|
forumEntries.clear();
|
||||||
forumEntries.addAll(entries);
|
forumEntries.addAll(entries);
|
||||||
setForumEntryLevels();
|
|
||||||
notifyItemRangeInserted(0, entries.size());
|
notifyItemRangeInserted(0, entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void addEntry(ForumEntry entry) {
|
void addEntry(ForumEntry entry) {
|
||||||
boolean isShowingDescendants = false;
|
|
||||||
forumEntries.add(entry);
|
forumEntries.add(entry);
|
||||||
setForumEntryLevels();
|
addedEntry = entry;
|
||||||
if (entry.getLevel() > 0) {
|
if (entry.getParentId() == null) {
|
||||||
// update parent and make sure descendants are visible
|
notifyItemInserted(getVisiblePos(entry));
|
||||||
// Note that the parent's visibility is guaranteed (otherwise
|
} else {
|
||||||
// the reply button would not be visible)
|
// Try to find the entry's parent and perform the proper ui update if
|
||||||
|
// it's present and visible.
|
||||||
for (int i = forumEntries.indexOf(entry) - 1; i >= 0; i--) {
|
for (int i = forumEntries.indexOf(entry) - 1; i >= 0; i--) {
|
||||||
ForumEntry higherEntry = forumEntries.get(i);
|
ForumEntry higherEntry = forumEntries.get(i);
|
||||||
if (higherEntry.getLevel() < entry.getLevel()) {
|
if (higherEntry.getLevel() < entry.getLevel()) {
|
||||||
// parent found
|
// parent found
|
||||||
if (!higherEntry.isShowingDescendants()) {
|
if (higherEntry.isShowingDescendants()) {
|
||||||
isShowingDescendants = true;
|
int parentVisiblePos = getVisiblePos(higherEntry);
|
||||||
showDescendants(higherEntry);
|
if (parentVisiblePos != NO_POSITION) {
|
||||||
|
// parent is visible, we need to update its ui
|
||||||
|
notifyItemChanged(parentVisiblePos);
|
||||||
|
// new entry insert ui
|
||||||
|
int visiblePos = getVisiblePos(entry);
|
||||||
|
notifyItemInserted(visiblePos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// do not show the new entry if its parent is not showing
|
||||||
|
// descendants (this can be overridden by the user by
|
||||||
|
// pressing the snack bar)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
notifyItemChanged(getVisiblePos(higherEntry));
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isShowingDescendants) {
|
|
||||||
int visiblePos = getVisiblePos(entry);
|
|
||||||
notifyItemInserted(visiblePos);
|
|
||||||
}
|
|
||||||
addedEntry = entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scrollToEntry(ForumEntry entry) {
|
void scrollToEntry(ForumEntry entry) {
|
||||||
layoutManager
|
int visiblePos = getVisiblePos(entry);
|
||||||
.scrollToPositionWithOffset(getVisiblePos(entry), 0);
|
if (visiblePos == NO_POSITION && entry.getParentId() != null) {
|
||||||
}
|
// The entry is not visible due to being hidden by its parent entry.
|
||||||
|
// Find the parent and make it visible and traverse up the parent
|
||||||
private boolean hasDescendants(ForumEntry forumEntry) {
|
// chain if necessary to make the entry visible
|
||||||
int i = forumEntries.indexOf(forumEntry);
|
MessageId parentId = entry.getParentId();
|
||||||
if (i >= 0 && i < forumEntries.size() - 1) {
|
for (int i = forumEntries.indexOf(entry) - 1; i >= 0; i--) {
|
||||||
if (forumEntries.get(i + 1).getLevel() >
|
ForumEntry higherEntry = forumEntries.get(i);
|
||||||
forumEntry.getLevel()) {
|
if (higherEntry.getId().equals(parentId)) {
|
||||||
return true;
|
// parent found
|
||||||
|
showDescendants(higherEntry);
|
||||||
|
int parentPos = getVisiblePos(higherEntry);
|
||||||
|
if (parentPos != NO_POSITION) {
|
||||||
|
// parent or ancestor is visible, entry's visibility
|
||||||
|
// is ensured
|
||||||
|
notifyItemChanged(parentPos);
|
||||||
|
visiblePos = parentPos;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// parent or ancestor is hidden, we need to continue up the
|
||||||
|
// dependency chain
|
||||||
|
parentId = higherEntry.getParentId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
if (visiblePos != NO_POSITION)
|
||||||
}
|
layoutManager.scrollToPositionWithOffset(visiblePos, 0);
|
||||||
|
|
||||||
private boolean hasVisibleDescendants(ForumEntry forumEntry) {
|
|
||||||
int visiblePos = getVisiblePos(forumEntry);
|
|
||||||
int levelLimit = forumEntry.getLevel();
|
|
||||||
if (visiblePos + 1 < getItemCount()) {
|
|
||||||
ForumEntry entry = getVisibleEntry(visiblePos + 1);
|
|
||||||
if (entry == null || entry.getLevel() > levelLimit)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getReplyCount(ForumEntry entry) {
|
private int getReplyCount(ForumEntry entry) {
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
int pos = forumEntries.indexOf(entry);
|
int pos = forumEntries.indexOf(entry);
|
||||||
if (pos >= 0) {
|
if (pos >= 0) {
|
||||||
int ancestorLvl = forumEntries.get(pos).getLevel();
|
int ancestorLvl = entry.getLevel();
|
||||||
for (int i = pos + 1; i < forumEntries.size(); i++) {
|
for (int i = pos + 1; i < forumEntries.size(); i++) {
|
||||||
int descendantLvl = forumEntries.get(i).getLevel();
|
int descendantLvl = forumEntries.get(i).getLevel();
|
||||||
if (descendantLvl <= ancestorLvl)
|
if (descendantLvl <= ancestorLvl)
|
||||||
@@ -210,14 +197,12 @@ public class NestedForumAdapter
|
|||||||
List<Integer> indexList =
|
List<Integer> indexList =
|
||||||
getSubTreeIndexes(visiblePos, forumEntry.getLevel());
|
getSubTreeIndexes(visiblePos, forumEntry.getLevel());
|
||||||
if (!indexList.isEmpty()) {
|
if (!indexList.isEmpty()) {
|
||||||
if (Build.VERSION.SDK_INT >= 11) {
|
// stop animating children
|
||||||
// stop animating children
|
for (int index : indexList) {
|
||||||
for (int index : indexList) {
|
ValueAnimator anim =
|
||||||
ValueAnimator anim =
|
animatingEntries.get(forumEntries.get(index));
|
||||||
animatingEntries.get(forumEntries.get(index));
|
if (anim != null && anim.isRunning()) {
|
||||||
if (anim != null && anim.isRunning()) {
|
anim.cancel();
|
||||||
anim.cancel();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (indexList.size() == 1) {
|
if (indexList.size() == 1) {
|
||||||
@@ -231,11 +216,18 @@ public class NestedForumAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param position is visible entry index
|
||||||
|
* @return the visible entry at index position from an ordered list of visible
|
||||||
|
* entries, or null if position is larger then the number of visible entries.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ForumEntry getVisibleEntry(int position) {
|
ForumEntry getVisibleEntry(int position) {
|
||||||
int levelLimit = UNDEFINED;
|
int levelLimit = UNDEFINED;
|
||||||
for (ForumEntry forumEntry : forumEntries) {
|
for (ForumEntry forumEntry : forumEntries) {
|
||||||
if (levelLimit >= 0) {
|
if (levelLimit >= 0) {
|
||||||
|
// skip hidden entries that their parent is hiding
|
||||||
if (forumEntry.getLevel() > levelLimit) {
|
if (forumEntry.getLevel() > levelLimit) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -251,7 +243,6 @@ public class NestedForumAdapter
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(11)
|
|
||||||
private void animateFadeOut(final NestedForumHolder ui,
|
private void animateFadeOut(final NestedForumHolder ui,
|
||||||
final ForumEntry addedEntry) {
|
final ForumEntry addedEntry) {
|
||||||
ui.setIsRecyclable(false);
|
ui.setIsRecyclable(false);
|
||||||
@@ -341,9 +332,9 @@ public class NestedForumAdapter
|
|||||||
replies, replies));
|
replies, replies));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDescendants(entry)) {
|
if (entry.hasDescendants()) {
|
||||||
ui.chevron.setVisibility(VISIBLE);
|
ui.chevron.setVisibility(VISIBLE);
|
||||||
if (hasVisibleDescendants(entry)) {
|
if (entry.isShowingDescendants()) {
|
||||||
ui.chevron.setSelected(false);
|
ui.chevron.setSelected(false);
|
||||||
} else {
|
} else {
|
||||||
ui.chevron.setSelected(true);
|
ui.chevron.setSelected(true);
|
||||||
@@ -369,9 +360,7 @@ public class NestedForumAdapter
|
|||||||
|
|
||||||
ui.cell.setBackgroundColor(ContextCompat
|
ui.cell.setBackgroundColor(ContextCompat
|
||||||
.getColor(ctx, R.color.forum_cell_highlight));
|
.getColor(ctx, R.color.forum_cell_highlight));
|
||||||
if (Build.VERSION.SDK_INT >= 11) {
|
animateFadeOut(ui, addedEntry);
|
||||||
animateFadeOut(ui, addedEntry);
|
|
||||||
}
|
|
||||||
addedEntry = null;
|
addedEntry = null;
|
||||||
} else {
|
} else {
|
||||||
ui.cell.setBackgroundColor(ContextCompat
|
ui.cell.setBackgroundColor(ContextCompat
|
||||||
@@ -386,12 +375,24 @@ public class NestedForumAdapter
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVisible(ForumEntry entry) {
|
||||||
|
return getVisiblePos(entry) != NO_POSITION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param sEntry the ForumEntry to find the visible positoin of, or null to
|
||||||
|
* return the total cound of visible elements
|
||||||
|
* @return the visible position of sEntry, or the total number of visible
|
||||||
|
* elements if sEntry is null. If sEntry is not visible a NO_POSITION is
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
private int getVisiblePos(ForumEntry sEntry) {
|
private int getVisiblePos(ForumEntry sEntry) {
|
||||||
int visibleCounter = 0;
|
int visibleCounter = 0;
|
||||||
int levelLimit = UNDEFINED;
|
int levelLimit = UNDEFINED;
|
||||||
for (ForumEntry fEntry : forumEntries) {
|
for (ForumEntry fEntry : forumEntries) {
|
||||||
if (levelLimit >= 0) {
|
if (levelLimit >= 0) {
|
||||||
if (fEntry.getLevel() > levelLimit) {
|
if (fEntry.getLevel() > levelLimit) {
|
||||||
|
// skip all the entries below a non visible branch
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
levelLimit = UNDEFINED;
|
levelLimit = UNDEFINED;
|
||||||
@@ -440,6 +441,7 @@ public class NestedForumAdapter
|
|||||||
cell = (ViewGroup) v.findViewById(R.id.forum_cell);
|
cell = (ViewGroup) v.findViewById(R.id.forum_cell);
|
||||||
topDivider = v.findViewById(R.id.top_divider);
|
topDivider = v.findViewById(R.id.top_divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnNestedForumListener {
|
interface OnNestedForumListener {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.android.util;
|
package org.briarproject.android.util;
|
||||||
|
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.api.clients.MessageTree;
|
import org.briarproject.api.clients.MessageTree;
|
||||||
import org.briarproject.clients.MessageTreeImpl;
|
import org.briarproject.clients.MessageTreeImpl;
|
||||||
|
|
||||||
@@ -8,7 +10,7 @@ import java.util.Collection;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/* This class is not thread safe */
|
@UiThread
|
||||||
public class NestedTreeList<T extends MessageTree.MessageNode>
|
public class NestedTreeList<T extends MessageTree.MessageNode>
|
||||||
implements Iterable<T> {
|
implements Iterable<T> {
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import junit.framework.Assert;
|
|||||||
import org.briarproject.BuildConfig;
|
import org.briarproject.BuildConfig;
|
||||||
import org.briarproject.TestUtils;
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.android.TestBriarApplication;
|
import org.briarproject.android.TestBriarApplication;
|
||||||
import org.briarproject.android.controller.handler.UiResultHandler;
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
@@ -80,7 +81,8 @@ public class ForumActivityTest {
|
|||||||
|
|
||||||
private TestForumActivity forumActivity;
|
private TestForumActivity forumActivity;
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<UiResultHandler<List<ForumEntry>>> rc;
|
private ArgumentCaptor<UiResultExceptionHandler<List<ForumEntry>, DbException>>
|
||||||
|
rc;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -91,7 +93,6 @@ public class ForumActivityTest {
|
|||||||
.withIntent(intent).create().resume().get();
|
.withIntent(intent).create().resume().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private List<ForumEntry> getDummyData() {
|
private List<ForumEntry> getDummyData() {
|
||||||
ForumEntry[] forumEntries = new ForumEntry[6];
|
ForumEntry[] forumEntries = new ForumEntry[6];
|
||||||
for (int i = 0; i < forumEntries.length; i++) {
|
for (int i = 0; i < forumEntries.length; i++) {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
|
|||||||
interface MessageNode {
|
interface MessageNode {
|
||||||
MessageId getId();
|
MessageId getId();
|
||||||
MessageId getParentId();
|
MessageId getParentId();
|
||||||
|
void setLevel(int level);
|
||||||
|
void setDescendantCount(int descendantCount);
|
||||||
long getTimestamp();
|
long getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,10 +77,13 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void traverse(List<T> list, T node) {
|
private void traverse(List<T> list, T node, int level) {
|
||||||
list.add(node);
|
list.add(node);
|
||||||
for (T child : nodeMap.get(node.getId())) {
|
List<T> children = nodeMap.get(node.getId());
|
||||||
traverse(list, child);
|
node.setLevel(level);
|
||||||
|
node.setDescendantCount(children.size());
|
||||||
|
for (T child : children) {
|
||||||
|
traverse(list, child, level+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +101,7 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
public synchronized Collection<T> depthFirstOrder() {
|
public synchronized Collection<T> depthFirstOrder() {
|
||||||
List<T> orderedList = new ArrayList<T>();
|
List<T> orderedList = new ArrayList<T>();
|
||||||
for (T root : roots) {
|
for (T root : roots) {
|
||||||
traverse(orderedList, root);
|
traverse(orderedList, root, 0);
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableList(orderedList);
|
return Collections.unmodifiableList(orderedList);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,16 @@ public class MessageTreeTest {
|
|||||||
return parentId;
|
return parentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLevel(int level) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDescendantCount(int descendantCount) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getTimestamp() {
|
public long getTimestamp() {
|
||||||
return timestamp;
|
return timestamp;
|
||||||
|
|||||||
Reference in New Issue
Block a user