mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +01:00
Merge branch '879-remove-thread-collapsing-unread-count' into 'master'
Remove code for collapsing threads and for reply count Besides removing lots of code, this MR also improves the encapsulation between adapter and view holders. Closes #478, #502, #526, #682, #683, #835, #836 See merge request !477
This commit is contained in:
@@ -100,7 +100,7 @@ public class ForumActivity extends
|
|||||||
super.onActivityResult(request, result, data);
|
super.onActivityResult(request, result, data);
|
||||||
|
|
||||||
if (request == REQUEST_SHARE_FORUM && result == RESULT_OK) {
|
if (request == REQUEST_SHARE_FORUM && result == RESULT_OK) {
|
||||||
displaySnackbarShort(R.string.forum_shared_snackbar);
|
displaySnackbar(R.string.forum_shared_snackbar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ public class GroupActivity extends
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int request, int result, Intent data) {
|
protected void onActivityResult(int request, int result, Intent data) {
|
||||||
if (request == REQUEST_GROUP_INVITE && result == RESULT_OK) {
|
if (request == REQUEST_GROUP_INVITE && result == RESULT_OK) {
|
||||||
displaySnackbarShort(R.string.groups_invitation_sent);
|
displaySnackbar(R.string.groups_invitation_sent);
|
||||||
} else super.onActivityResult(request, result, data);
|
} else super.onActivityResult(request, result, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,9 +31,8 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
|||||||
@LayoutRes
|
@LayoutRes
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
GroupMessageItem item = getVisibleItem(position);
|
GroupMessageItem item = items.get(position);
|
||||||
if (item != null) return item.getLayout();
|
return item.getLayout();
|
||||||
return R.layout.list_item_thread;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +57,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
|||||||
GroupMessageItem item = items.get(position);
|
GroupMessageItem item = items.get(position);
|
||||||
if (item instanceof JoinMessageItem) {
|
if (item instanceof JoinMessageItem) {
|
||||||
((JoinMessageItem) item).setVisibility(v);
|
((JoinMessageItem) item).setVisibility(v);
|
||||||
notifyItemChanged(getVisiblePos(item), item);
|
notifyItemChanged(findItemPosition(item), item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.privategroup.conversation;
|
package org.briarproject.briar.android.privategroup.conversation;
|
||||||
|
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
@@ -20,9 +21,8 @@ class GroupMessageItem extends ThreadItem {
|
|||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
|
|
||||||
private GroupMessageItem(MessageId messageId, GroupId groupId,
|
private GroupMessageItem(MessageId messageId, GroupId groupId,
|
||||||
MessageId parentId,
|
@Nullable MessageId parentId, String text, long timestamp,
|
||||||
String text, long timestamp, Author author, Status status,
|
Author author, Status status, boolean isRead) {
|
||||||
boolean isRead) {
|
|
||||||
super(messageId, parentId, text, timestamp, author, status, isRead);
|
super(messageId, parentId, text, timestamp, author, status, isRead);
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ class JoinMessageItem extends GroupMessageItem {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasDescendants() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@LayoutRes
|
@LayoutRes
|
||||||
public int getLayout() {
|
public int getLayout() {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
||||||
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter;
|
|
||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||||
@@ -41,10 +40,9 @@ class JoinMessageItemViewHolder
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(ThreadItemAdapter<GroupMessageItem> adapter,
|
public void bind(GroupMessageItem item,
|
||||||
ThreadItemListener<GroupMessageItem> listener,
|
ThreadItemListener<GroupMessageItem> listener) {
|
||||||
GroupMessageItem item, int pos) {
|
super.bind(item, listener);
|
||||||
super.bind(adapter, listener, item, pos);
|
|
||||||
|
|
||||||
if (isCreator) bindForCreator((JoinMessageItem) item);
|
if (isCreator) bindForCreator((JoinMessageItem) item);
|
||||||
else bind((JoinMessageItem) item);
|
else bind((JoinMessageItem) item);
|
||||||
|
|||||||
@@ -20,9 +20,6 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
import org.briarproject.briar.android.view.AuthorView;
|
import org.briarproject.briar.android.view.AuthorView;
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
||||||
@@ -33,7 +30,6 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
|||||||
protected final TextView textView;
|
protected final TextView textView;
|
||||||
private final ViewGroup layout;
|
private final ViewGroup layout;
|
||||||
private final AuthorView author;
|
private final AuthorView author;
|
||||||
private final View topDivider;
|
|
||||||
|
|
||||||
public BaseThreadItemViewHolder(View v) {
|
public BaseThreadItemViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
@@ -41,43 +37,30 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
|||||||
layout = (ViewGroup) v.findViewById(R.id.layout);
|
layout = (ViewGroup) v.findViewById(R.id.layout);
|
||||||
textView = (TextView) v.findViewById(R.id.text);
|
textView = (TextView) v.findViewById(R.id.text);
|
||||||
author = (AuthorView) v.findViewById(R.id.author);
|
author = (AuthorView) v.findViewById(R.id.author);
|
||||||
topDivider = v.findViewById(R.id.top_divider);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO improve encapsulation, so we don't need to pass the adapter here
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void bind(final ThreadItemAdapter<I> adapter,
|
public void bind(final I item, final ThreadItemListener<I> listener) {
|
||||||
final ThreadItemListener<I> listener, final I item, int pos) {
|
|
||||||
|
|
||||||
textView.setText(StringUtils.trim(item.getText()));
|
textView.setText(StringUtils.trim(item.getText()));
|
||||||
|
|
||||||
if (pos == 0) {
|
|
||||||
topDivider.setVisibility(INVISIBLE);
|
|
||||||
} else {
|
|
||||||
topDivider.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
author.setAuthor(item.getAuthor());
|
author.setAuthor(item.getAuthor());
|
||||||
author.setDate(item.getTimestamp());
|
author.setDate(item.getTimestamp());
|
||||||
author.setAuthorStatus(item.getStatus());
|
author.setAuthorStatus(item.getStatus());
|
||||||
|
|
||||||
if (item.equals(adapter.getReplyItem())) {
|
if (item.isHighlighted()) {
|
||||||
layout.setActivated(true);
|
layout.setActivated(true);
|
||||||
} else if (!item.isRead()) {
|
} else if (!item.isRead()) {
|
||||||
layout.setActivated(true);
|
layout.setActivated(true);
|
||||||
animateFadeOut(adapter, item);
|
animateFadeOut();
|
||||||
listener.onUnreadItemVisible(item);
|
listener.onUnreadItemVisible(item);
|
||||||
} else {
|
} else {
|
||||||
layout.setActivated(false);
|
layout.setActivated(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateFadeOut(final ThreadItemAdapter<I> adapter,
|
private void animateFadeOut() {
|
||||||
final I addedItem) {
|
|
||||||
|
|
||||||
setIsRecyclable(false);
|
setIsRecyclable(false);
|
||||||
ValueAnimator anim = new ValueAnimator();
|
ValueAnimator anim = new ValueAnimator();
|
||||||
adapter.addAnimatingItem(addedItem, anim);
|
|
||||||
ColorDrawable viewColor = new ColorDrawable(ContextCompat
|
ColorDrawable viewColor = new ColorDrawable(ContextCompat
|
||||||
.getColor(getContext(), R.color.forum_cell_highlight));
|
.getColor(getContext(), R.color.forum_cell_highlight));
|
||||||
anim.setIntValues(viewColor.getColor(), ContextCompat
|
anim.setIntValues(viewColor.getColor(), ContextCompat
|
||||||
@@ -94,7 +77,6 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
|||||||
R.drawable.list_item_thread_background);
|
R.drawable.list_item_thread_background);
|
||||||
layout.setActivated(false);
|
layout.setActivated(false);
|
||||||
setIsRecyclable(true);
|
setIsRecyclable(true);
|
||||||
adapter.removeAnimatingItem(addedItem);
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationCancel(Animator animation) {
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ public abstract class ThreadItem implements MessageNode {
|
|||||||
private final Author author;
|
private final Author author;
|
||||||
private final Status status;
|
private final Status status;
|
||||||
private int level = UNDEFINED;
|
private int level = UNDEFINED;
|
||||||
private boolean isShowingDescendants = true;
|
private boolean isRead, highlighted;
|
||||||
private int descendantCount = 0;
|
|
||||||
private boolean isRead;
|
|
||||||
|
|
||||||
public ThreadItem(MessageId messageId, @Nullable MessageId parentId,
|
public ThreadItem(MessageId messageId, @Nullable MessageId parentId,
|
||||||
String text, long timestamp, Author author, Status status,
|
String text, long timestamp, Author author, Status status,
|
||||||
@@ -37,6 +35,7 @@ public abstract class ThreadItem implements MessageNode {
|
|||||||
this.author = author;
|
this.author = author;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.isRead = isRead;
|
this.isRead = isRead;
|
||||||
|
this.highlighted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getText() {
|
public String getText() {
|
||||||
@@ -71,19 +70,11 @@ public abstract class ThreadItem implements MessageNode {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShowingDescendants() {
|
|
||||||
return isShowingDescendants;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLevel(int level) {
|
public void setLevel(int level) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShowingDescendants(boolean showingDescendants) {
|
|
||||||
this.isShowingDescendants = showingDescendants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRead() {
|
public boolean isRead() {
|
||||||
return isRead;
|
return isRead;
|
||||||
}
|
}
|
||||||
@@ -92,13 +83,12 @@ public abstract class ThreadItem implements MessageNode {
|
|||||||
isRead = read;
|
isRead = read;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasDescendants() {
|
public void setHighlighted(boolean highlighted) {
|
||||||
return descendantCount > 0;
|
this.highlighted = highlighted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isHighlighted() {
|
||||||
public void setDescendantCount(int descendantCount) {
|
return highlighted;
|
||||||
this.descendantCount = descendantCount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.threaded;
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
import android.animation.ValueAnimator;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
@@ -13,14 +12,11 @@ import org.briarproject.bramble.api.sync.MessageId;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.util.VersionedAdapter;
|
import org.briarproject.briar.android.util.VersionedAdapter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public class ThreadItemAdapter<I extends ThreadItem>
|
public class ThreadItemAdapter<I extends ThreadItem>
|
||||||
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
|
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
|
||||||
implements VersionedAdapter {
|
implements VersionedAdapter {
|
||||||
@@ -28,13 +24,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
static final int UNDEFINED = -1;
|
static final int UNDEFINED = -1;
|
||||||
|
|
||||||
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
||||||
private final Map<I, ValueAnimator> animatingItems = new HashMap<>();
|
|
||||||
private final ThreadItemListener<I> listener;
|
private final ThreadItemListener<I> listener;
|
||||||
private final LinearLayoutManager layoutManager;
|
private final LinearLayoutManager layoutManager;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private I replyItem;
|
|
||||||
|
|
||||||
private volatile int revision = 0;
|
private volatile int revision = 0;
|
||||||
|
|
||||||
public ThreadItemAdapter(ThreadItemListener<I> listener,
|
public ThreadItemAdapter(ThreadItemListener<I> listener,
|
||||||
@@ -53,24 +45,23 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
|
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
|
||||||
I item = getVisibleItem(position);
|
I item = items.get(position);
|
||||||
if (item == null) return;
|
ui.bind(item, listener);
|
||||||
ui.bind(this, listener, item, position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Contrary to the super class adapter,
|
|
||||||
* this returns the number of <b>visible</b> items,
|
|
||||||
* not all items in the dataset.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return getVisiblePos(null);
|
return items.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Override
|
||||||
I getReplyItem() {
|
public int getRevision() {
|
||||||
return replyItem;
|
return revision;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void incrementRevision() {
|
||||||
|
revision++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setItems(Collection<I> items) {
|
public void setItems(Collection<I> items) {
|
||||||
@@ -81,233 +72,51 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
|
|
||||||
public void add(I item) {
|
public void add(I item) {
|
||||||
items.add(item);
|
items.add(item);
|
||||||
if (item.getParentId() == null) {
|
notifyItemInserted(findItemPosition(item));
|
||||||
notifyItemInserted(getVisiblePos(item));
|
|
||||||
} else {
|
|
||||||
// Try to find the item's parent and perform the proper ui update if
|
|
||||||
// it's present and visible.
|
|
||||||
for (int i = items.indexOf(item) - 1; i >= 0; i--) {
|
|
||||||
I higherItem = items.get(i);
|
|
||||||
if (higherItem.getLevel() < item.getLevel()) {
|
|
||||||
// parent found
|
|
||||||
if (higherItem.isShowingDescendants()) {
|
|
||||||
int parentVisiblePos = getVisiblePos(higherItem);
|
|
||||||
if (parentVisiblePos != NO_POSITION) {
|
|
||||||
// parent is visible, we need to update its ui
|
|
||||||
notifyItemChanged(parentVisiblePos);
|
|
||||||
// new item insert ui
|
|
||||||
int visiblePos = getVisiblePos(item);
|
|
||||||
notifyItemInserted(visiblePos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// do not show the new item if its parent is not showing
|
|
||||||
// descendants (this can be overridden by the user by
|
|
||||||
// pressing the snack bar)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scrollTo(I item) {
|
@Nullable
|
||||||
int visiblePos = getVisiblePos(item);
|
public I getItemAt(int position) {
|
||||||
MessageId parentId = item.getParentId();
|
if (position == NO_POSITION || position >= items.size()) {
|
||||||
if (visiblePos == NO_POSITION && parentId != null) {
|
return null;
|
||||||
// The item is not visible due to being hidden by its parent item.
|
|
||||||
// Find the parent and make it visible and traverse up the parent
|
|
||||||
// chain if necessary to make the item visible
|
|
||||||
for (int i = items.indexOf(item) - 1; i >= 0; i--) {
|
|
||||||
I higherItem = items.get(i);
|
|
||||||
if (higherItem.getId().equals(parentId)) {
|
|
||||||
// parent found
|
|
||||||
showDescendants(higherItem);
|
|
||||||
int parentPos = getVisiblePos(higherItem);
|
|
||||||
if (parentPos != NO_POSITION) {
|
|
||||||
// parent or ancestor is visible, item's visibility
|
|
||||||
// is ensured
|
|
||||||
notifyItemChanged(parentPos);
|
|
||||||
visiblePos = parentPos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// parent or ancestor is hidden, we need to continue up the
|
|
||||||
// dependency chain
|
|
||||||
parentId = higherItem.getParentId();
|
|
||||||
if (parentId == null) throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (visiblePos != NO_POSITION)
|
return items.get(position);
|
||||||
layoutManager.scrollToPositionWithOffset(visiblePos, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int getReplyCount(I item) {
|
protected int findItemPosition(@Nullable I item) {
|
||||||
int counter = 0;
|
for (int i = 0; i < items.size(); i++) {
|
||||||
int pos = items.indexOf(item);
|
if (items.get(i).equals(item)) return i;
|
||||||
if (pos >= 0) {
|
|
||||||
int ancestorLvl = item.getLevel();
|
|
||||||
for (int i = pos + 1; i < items.size(); i++) {
|
|
||||||
int descendantLvl = items.get(i).getLevel();
|
|
||||||
if (descendantLvl <= ancestorLvl)
|
|
||||||
break;
|
|
||||||
if (descendantLvl == ancestorLvl + 1)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return counter;
|
return NO_POSITION; // Not found
|
||||||
}
|
}
|
||||||
|
|
||||||
void setReplyItem(@Nullable I item) {
|
|
||||||
if (replyItem != null) {
|
|
||||||
notifyItemChanged(getVisiblePos(replyItem));
|
|
||||||
}
|
|
||||||
replyItem = item;
|
|
||||||
if (replyItem != null) {
|
|
||||||
notifyItemChanged(getVisiblePos(replyItem));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setReplyItemById(MessageId id) {
|
|
||||||
for (I item : items) {
|
|
||||||
if (item.getId().equals(id)) {
|
|
||||||
setReplyItem(item);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Integer> getSubTreeIndexes(int pos, int levelLimit) {
|
|
||||||
List<Integer> indexList = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int i = pos + 1; i < getItemCount(); i++) {
|
|
||||||
I item = getVisibleItem(i);
|
|
||||||
if (item != null && item.getLevel() > levelLimit) {
|
|
||||||
indexList.add(i);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return indexList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showDescendants(I item) {
|
|
||||||
item.setShowingDescendants(true);
|
|
||||||
int visiblePos = getVisiblePos(item);
|
|
||||||
List<Integer> indexList =
|
|
||||||
getSubTreeIndexes(visiblePos, item.getLevel());
|
|
||||||
if (!indexList.isEmpty()) {
|
|
||||||
if (indexList.size() == 1) {
|
|
||||||
notifyItemInserted(indexList.get(0));
|
|
||||||
} else {
|
|
||||||
notifyItemRangeInserted(indexList.get(0),
|
|
||||||
indexList.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hideDescendants(I item) {
|
|
||||||
int visiblePos = getVisiblePos(item);
|
|
||||||
List<Integer> indexList =
|
|
||||||
getSubTreeIndexes(visiblePos, item.getLevel());
|
|
||||||
if (!indexList.isEmpty()) {
|
|
||||||
// stop animating children
|
|
||||||
for (int index : indexList) {
|
|
||||||
ValueAnimator anim = animatingItems.get(items.get(index));
|
|
||||||
if (anim != null && anim.isRunning()) {
|
|
||||||
anim.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (indexList.size() == 1) {
|
|
||||||
notifyItemRemoved(indexList.get(0));
|
|
||||||
} else {
|
|
||||||
notifyItemRangeRemoved(indexList.get(0),
|
|
||||||
indexList.size());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.setShowingDescendants(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the visible item at the given position
|
* Highlights the item with the given {@link MessageId}
|
||||||
|
* and disables the highlight for a previously highlighted item, if any.
|
||||||
*
|
*
|
||||||
* @param position is visible item index
|
* Only one item can be highlighted at a time.
|
||||||
* @return the visible item at index 'position' from an ordered list of
|
|
||||||
* visible items, or null if 'position' is larger than the number of
|
|
||||||
* visible items.
|
|
||||||
*/
|
*/
|
||||||
|
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)) {
|
||||||
|
item.setHighlighted(true);
|
||||||
|
notifyItemChanged(i, item);
|
||||||
|
} else if (item.isHighlighted()) {
|
||||||
|
item.setHighlighted(false);
|
||||||
|
notifyItemChanged(i, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public I getVisibleItem(int position) {
|
I getHighlightedItem() {
|
||||||
int levelLimit = UNDEFINED;
|
for (I i : items) {
|
||||||
for (I item : items) {
|
if (i.isHighlighted()) return i;
|
||||||
if (levelLimit >= 0) {
|
|
||||||
// skip hidden items that their parent is hiding
|
|
||||||
if (item.getLevel() > levelLimit) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
levelLimit = UNDEFINED;
|
|
||||||
}
|
|
||||||
if (!item.isShowingDescendants()) {
|
|
||||||
levelLimit = item.getLevel();
|
|
||||||
}
|
|
||||||
if (position-- == 0) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the visible position of the given item.
|
|
||||||
*
|
|
||||||
* @param item the item to find the visible position of, or null to
|
|
||||||
* return the total count of visible items.
|
|
||||||
* @return the visible position of 'item', or the total number of visible
|
|
||||||
* items if 'item' is null. If 'item' is not visible, NO_POSITION is
|
|
||||||
* returned.
|
|
||||||
*/
|
|
||||||
protected int getVisiblePos(@Nullable I item) {
|
|
||||||
int visibleCounter = 0;
|
|
||||||
int levelLimit = UNDEFINED;
|
|
||||||
for (I i : items) {
|
|
||||||
if (levelLimit >= 0) {
|
|
||||||
if (i.getLevel() > levelLimit) {
|
|
||||||
// skip all the items below a non visible branch
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
levelLimit = UNDEFINED;
|
|
||||||
}
|
|
||||||
if (item != null && item.equals(i)) {
|
|
||||||
return visibleCounter;
|
|
||||||
} else if (!i.isShowingDescendants()) {
|
|
||||||
levelLimit = i.getLevel();
|
|
||||||
}
|
|
||||||
visibleCounter++;
|
|
||||||
}
|
|
||||||
return item == null ? visibleCounter : NO_POSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addAnimatingItem(I item, ValueAnimator anim) {
|
|
||||||
animatingItems.put(item, anim);
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeAnimatingItem(I item) {
|
|
||||||
animatingItems.remove(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getRevision() {
|
|
||||||
return revision;
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
@Override
|
|
||||||
public void incrementRevision() {
|
|
||||||
revision++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of unread items above and below the current view port.
|
* Gets the number of unread items above and below the current view port.
|
||||||
*
|
*
|
||||||
@@ -321,24 +130,13 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
return new UnreadCount(0, 0);
|
return new UnreadCount(0, 0);
|
||||||
|
|
||||||
int unreadCounterTop = 0, unreadCounterBottom = 0;
|
int unreadCounterTop = 0, unreadCounterBottom = 0;
|
||||||
int visibleCounter = 0;
|
for (int i = 0; i < items.size(); i++) {
|
||||||
int levelLimit = UNDEFINED;
|
I item = items.get(i);
|
||||||
for (I i : items) {
|
if (i < positionTop && !item.isRead()) {
|
||||||
if (levelLimit >= 0) {
|
|
||||||
if (i.getLevel() > levelLimit) {
|
|
||||||
// skip all the items below a non visible branch
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
levelLimit = UNDEFINED;
|
|
||||||
}
|
|
||||||
if (visibleCounter > positionBottom && !i.isRead()) {
|
|
||||||
unreadCounterBottom++;
|
|
||||||
} else if (visibleCounter < positionTop && !i.isRead()) {
|
|
||||||
unreadCounterTop++;
|
unreadCounterTop++;
|
||||||
} else if (!i.isShowingDescendants()) {
|
} else if (i > positionBottom && !item.isRead()) {
|
||||||
levelLimit = i.getLevel();
|
unreadCounterBottom++;
|
||||||
}
|
}
|
||||||
visibleCounter++;
|
|
||||||
}
|
}
|
||||||
return new UnreadCount(unreadCounterTop, unreadCounterBottom);
|
return new UnreadCount(unreadCounterTop, unreadCounterBottom);
|
||||||
}
|
}
|
||||||
@@ -348,22 +146,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
*/
|
*/
|
||||||
public int getVisibleUnreadPosBottom() {
|
public int getVisibleUnreadPosBottom() {
|
||||||
final int positionBottom = layoutManager.findLastVisibleItemPosition();
|
final int positionBottom = layoutManager.findLastVisibleItemPosition();
|
||||||
int visibleCounter = 0;
|
if (positionBottom == NO_POSITION) return NO_POSITION;
|
||||||
int levelLimit = UNDEFINED;
|
for (int i = positionBottom + 1; i < items.size(); i++) {
|
||||||
for (I i : items) {
|
if (!items.get(i).isRead()) return i;
|
||||||
if (levelLimit >= 0) {
|
|
||||||
if (i.getLevel() > levelLimit) {
|
|
||||||
// skip all the items below a non visible branch
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
levelLimit = UNDEFINED;
|
|
||||||
}
|
|
||||||
if (visibleCounter > positionBottom && !i.isRead()) {
|
|
||||||
return visibleCounter;
|
|
||||||
} else if (!i.isShowingDescendants()) {
|
|
||||||
levelLimit = i.getLevel();
|
|
||||||
}
|
|
||||||
visibleCounter++;
|
|
||||||
}
|
}
|
||||||
return NO_POSITION;
|
return NO_POSITION;
|
||||||
}
|
}
|
||||||
@@ -374,24 +159,12 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
public int getVisibleUnreadPosTop() {
|
public int getVisibleUnreadPosTop() {
|
||||||
final int positionTop = layoutManager.findFirstVisibleItemPosition();
|
final int positionTop = layoutManager.findFirstVisibleItemPosition();
|
||||||
int position = NO_POSITION;
|
int position = NO_POSITION;
|
||||||
int visibleCounter = 0;
|
for (int i = 0; i < items.size(); i++) {
|
||||||
int levelLimit = UNDEFINED;
|
if (i < positionTop && !items.get(i).isRead()) {
|
||||||
for (I i : items) {
|
position = i;
|
||||||
if (levelLimit >= 0) {
|
} else if (i >= positionTop) {
|
||||||
if (i.getLevel() > levelLimit) {
|
|
||||||
// skip all the items below a non visible branch
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
levelLimit = UNDEFINED;
|
|
||||||
}
|
|
||||||
if (visibleCounter < positionTop && !i.isRead()) {
|
|
||||||
position = visibleCounter;
|
|
||||||
} if (visibleCounter >= positionTop) {
|
|
||||||
return position;
|
return position;
|
||||||
} else if (!i.isShowingDescendants()) {
|
|
||||||
levelLimit = i.getLevel();
|
|
||||||
}
|
}
|
||||||
visibleCounter++;
|
|
||||||
}
|
}
|
||||||
return NO_POSITION;
|
return NO_POSITION;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.threaded;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
@@ -32,11 +33,11 @@ import org.briarproject.briar.android.view.TextInputView.TextInputListener;
|
|||||||
import org.briarproject.briar.android.view.UnreadMessageButton;
|
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
import org.briarproject.briar.api.client.PostHeader;
|
import org.briarproject.briar.api.client.PostHeader;
|
||||||
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.support.design.widget.Snackbar.make;
|
import static android.support.design.widget.Snackbar.make;
|
||||||
@@ -62,9 +63,11 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
|
|
||||||
protected A adapter;
|
protected A adapter;
|
||||||
protected BriarRecyclerView list;
|
protected BriarRecyclerView list;
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
protected TextInputView textInput;
|
protected TextInputView textInput;
|
||||||
protected GroupId groupId;
|
protected GroupId groupId;
|
||||||
private UnreadMessageButton upButton, downButton;
|
private UnreadMessageButton upButton, downButton;
|
||||||
|
@Nullable
|
||||||
private MessageId replyId;
|
private MessageId replyId;
|
||||||
|
|
||||||
protected abstract ThreadListController<G, I, H> getController();
|
protected abstract ThreadListController<G, I, H> getController();
|
||||||
@@ -89,9 +92,9 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
textInput.setVisibility(GONE);
|
textInput.setVisibility(GONE);
|
||||||
textInput.setListener(this);
|
textInput.setListener(this);
|
||||||
list = (BriarRecyclerView) findViewById(R.id.list);
|
list = (BriarRecyclerView) findViewById(R.id.list);
|
||||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
|
layoutManager = new LinearLayoutManager(this);
|
||||||
list.setLayoutManager(linearLayoutManager);
|
list.setLayoutManager(layoutManager);
|
||||||
adapter = createAdapter(linearLayoutManager);
|
adapter = createAdapter(layoutManager);
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
|
|
||||||
list.getRecyclerView().addOnScrollListener(
|
list.getRecyclerView().addOnScrollListener(
|
||||||
@@ -179,7 +182,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
adapter.setItems(items);
|
adapter.setItems(items);
|
||||||
list.showData();
|
list.showData();
|
||||||
if (replyId != null)
|
if (replyId != null)
|
||||||
adapter.setReplyItemById(replyId);
|
adapter.setHighlightedItem(replyId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Concurrent update, reloading");
|
LOG.info("Concurrent update, reloading");
|
||||||
@@ -240,7 +243,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
boolean visible = textInput.getVisibility() == VISIBLE;
|
boolean visible = textInput.getVisibility() == VISIBLE;
|
||||||
outState.putBoolean(KEY_INPUT_VISIBILITY, visible);
|
outState.putBoolean(KEY_INPUT_VISIBILITY, visible);
|
||||||
ThreadItem replyItem = adapter.getReplyItem();
|
ThreadItem replyItem = adapter.getHighlightedItem();
|
||||||
if (replyItem != null) {
|
if (replyItem != null) {
|
||||||
outState.putByteArray(KEY_REPLY_ID, replyItem.getId().getBytes());
|
outState.putByteArray(KEY_REPLY_ID, replyItem.getId().getBytes());
|
||||||
}
|
}
|
||||||
@@ -261,7 +264,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (textInput.getVisibility() == VISIBLE) {
|
if (textInput.getVisibility() == VISIBLE) {
|
||||||
textInput.setVisibility(GONE);
|
textInput.setVisibility(GONE);
|
||||||
adapter.setReplyItem(null);
|
adapter.setHighlightedItem(null);
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
@@ -276,8 +279,21 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReplyClick(I item) {
|
public void onReplyClick(final I item) {
|
||||||
showTextInput(item);
|
showTextInput(item);
|
||||||
|
if (textInput.isKeyboardOpen()) {
|
||||||
|
scrollToItemAtTop(item);
|
||||||
|
} else {
|
||||||
|
// wait with scrolling until keyboard opened
|
||||||
|
textInput.addOnKeyboardShownListener(
|
||||||
|
new KeyboardAwareLinearLayout.OnKeyboardShownListener() {
|
||||||
|
@Override
|
||||||
|
public void onKeyboardShown() {
|
||||||
|
scrollToItemAtTop(item);
|
||||||
|
textInput.removeOnKeyboardShownListener(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -300,7 +316,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void displaySnackbarShort(@StringRes int stringId) {
|
private void scrollToItemAtTop(I item) {
|
||||||
|
int position = adapter.findItemPosition(item);
|
||||||
|
if (position != NO_POSITION) {
|
||||||
|
layoutManager
|
||||||
|
.scrollToPositionWithOffset(position, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void displaySnackbar(@StringRes int stringId) {
|
||||||
Snackbar snackbar = make(list, stringId, Snackbar.LENGTH_SHORT);
|
Snackbar snackbar = make(list, stringId, Snackbar.LENGTH_SHORT);
|
||||||
snackbar.getView().setBackgroundResource(R.color.briar_primary);
|
snackbar.getView().setBackgroundResource(R.color.briar_primary);
|
||||||
snackbar.show();
|
snackbar.show();
|
||||||
@@ -318,7 +342,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
textInput.showSoftKeyboard();
|
textInput.showSoftKeyboard();
|
||||||
textInput.setHint(replyItem == null ? R.string.forum_new_message_hint :
|
textInput.setHint(replyItem == null ? R.string.forum_new_message_hint :
|
||||||
R.string.forum_message_reply_hint);
|
R.string.forum_message_reply_hint);
|
||||||
adapter.setReplyItem(replyItem);
|
adapter.setHighlightedItem(
|
||||||
|
replyItem == null ? null : replyItem.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -326,10 +351,10 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
if (text.trim().length() == 0)
|
if (text.trim().length() == 0)
|
||||||
return;
|
return;
|
||||||
if (StringUtils.utf8IsTooLong(text, getMaxBodyLength())) {
|
if (StringUtils.utf8IsTooLong(text, getMaxBodyLength())) {
|
||||||
displaySnackbarShort(R.string.text_too_long);
|
displaySnackbar(R.string.text_too_long);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
I replyItem = adapter.getReplyItem();
|
I replyItem = adapter.getHighlightedItem();
|
||||||
UiResultExceptionHandler<I, DbException> handler =
|
UiResultExceptionHandler<I, DbException> handler =
|
||||||
new UiResultExceptionHandler<I, DbException>(this) {
|
new UiResultExceptionHandler<I, DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
@@ -346,7 +371,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
textInput.hideSoftKeyboard();
|
textInput.hideSoftKeyboard();
|
||||||
textInput.setVisibility(GONE);
|
textInput.setVisibility(GONE);
|
||||||
textInput.setText("");
|
textInput.setText("");
|
||||||
adapter.setReplyItem(null);
|
adapter.setHighlightedItem(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract int getMaxBodyLength();
|
protected abstract int getMaxBodyLength();
|
||||||
@@ -377,7 +402,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
adapter.add(item);
|
adapter.add(item);
|
||||||
|
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
displaySnackbarShort(getItemPostedString());
|
displaySnackbar(getItemPostedString());
|
||||||
|
scrollToItemAtTop(item);
|
||||||
} else {
|
} else {
|
||||||
updateUnreadCount();
|
updateUnreadCount();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,32 +16,29 @@ import static android.view.View.VISIBLE;
|
|||||||
public class ThreadPostViewHolder<I extends ThreadItem>
|
public class ThreadPostViewHolder<I extends ThreadItem>
|
||||||
extends BaseThreadItemViewHolder<I> {
|
extends BaseThreadItemViewHolder<I> {
|
||||||
|
|
||||||
private final TextView lvlText, repliesText;
|
private final TextView lvlText;
|
||||||
private final View[] lvls;
|
private final View[] lvls;
|
||||||
private final View chevron, replyButton;
|
private final View replyButton;
|
||||||
|
|
||||||
|
private final static int[] nestedLineIds = {
|
||||||
|
R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
|
||||||
|
R.id.nested_line_4, R.id.nested_line_5
|
||||||
|
};
|
||||||
|
|
||||||
public ThreadPostViewHolder(View v) {
|
public ThreadPostViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|
||||||
lvlText = (TextView) v.findViewById(R.id.nested_line_text);
|
lvlText = (TextView) v.findViewById(R.id.nested_line_text);
|
||||||
repliesText = (TextView) v.findViewById(R.id.replies);
|
|
||||||
int[] nestedLineIds = {
|
|
||||||
R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
|
|
||||||
R.id.nested_line_4, R.id.nested_line_5
|
|
||||||
};
|
|
||||||
lvls = new View[nestedLineIds.length];
|
lvls = new View[nestedLineIds.length];
|
||||||
for (int i = 0; i < lvls.length; i++) {
|
for (int i = 0; i < lvls.length; i++) {
|
||||||
lvls[i] = v.findViewById(nestedLineIds[i]);
|
lvls[i] = v.findViewById(nestedLineIds[i]);
|
||||||
}
|
}
|
||||||
chevron = v.findViewById(R.id.chevron);
|
|
||||||
replyButton = v.findViewById(R.id.btn_reply);
|
replyButton = v.findViewById(R.id.btn_reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO improve encapsulation, so we don't need to pass the adapter here
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(final ThreadItemAdapter<I> adapter,
|
public void bind(final I item, final ThreadItemListener<I> listener) {
|
||||||
final ThreadItemListener<I> listener, final I item, int pos) {
|
super.bind(item, listener);
|
||||||
super.bind(adapter, listener, item, pos);
|
|
||||||
|
|
||||||
for (int i = 0; i < lvls.length; i++) {
|
for (int i = 0; i < lvls.length; i++) {
|
||||||
lvls[i].setVisibility(i < item.getLevel() ? VISIBLE : GONE);
|
lvls[i].setVisibility(i < item.getLevel() ? VISIBLE : GONE);
|
||||||
@@ -53,41 +50,10 @@ public class ThreadPostViewHolder<I extends ThreadItem>
|
|||||||
lvlText.setVisibility(GONE);
|
lvlText.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int replies = adapter.getReplyCount(item);
|
|
||||||
if (replies == 0) {
|
|
||||||
repliesText.setText("");
|
|
||||||
} else {
|
|
||||||
repliesText.setText(getContext().getResources()
|
|
||||||
.getQuantityString(R.plurals.message_replies, replies,
|
|
||||||
replies));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.hasDescendants()) {
|
|
||||||
// chevron.setVisibility(VISIBLE);
|
|
||||||
if (item.isShowingDescendants()) {
|
|
||||||
chevron.setSelected(false);
|
|
||||||
} else {
|
|
||||||
chevron.setSelected(true);
|
|
||||||
}
|
|
||||||
chevron.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
chevron.setSelected(!chevron.isSelected());
|
|
||||||
if (chevron.isSelected()) {
|
|
||||||
adapter.hideDescendants(item);
|
|
||||||
} else {
|
|
||||||
adapter.showDescendants(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// chevron.setVisibility(INVISIBLE);
|
|
||||||
}
|
|
||||||
replyButton.setOnClickListener(new View.OnClickListener() {
|
replyButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.onReplyClick(item);
|
listener.onReplyClick(item);
|
||||||
adapter.scrollTo(item);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
tools:text="Dec 24"/>
|
tools:text="Dec 24"/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style="@style/Divider.ForumList"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/postCountView"/>
|
android:layout_below="@+id/postCountView"/>
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
style="@style/Divider.ForumList"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/statusView"
|
android:layout_below="@+id/statusView"
|
||||||
|
|||||||
@@ -6,23 +6,14 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="@dimen/margin_medium"
|
|
||||||
android:baselineAligned="false"
|
android:baselineAligned="false"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/top_divider"
|
|
||||||
style="@style/Divider.ForumList"
|
|
||||||
android:layout_alignParentTop="true"/>
|
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@+id/top_divider"
|
android:layout_margin="@dimen/margin_medium"
|
||||||
android:layout_marginBottom="@dimen/margin_small"
|
|
||||||
android:layout_marginRight="@dimen/margin_medium"
|
|
||||||
android:layout_marginTop="@dimen/margin_medium"
|
|
||||||
android:textColor="@color/briar_text_secondary"
|
android:textColor="@color/briar_text_secondary"
|
||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
android:textStyle="italic"
|
android:textStyle="italic"
|
||||||
@@ -32,9 +23,12 @@
|
|||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/info"
|
||||||
android:layout_alignLeft="@+id/text"
|
android:layout_alignLeft="@+id/text"
|
||||||
|
android:layout_alignTop="@+id/info"
|
||||||
android:layout_below="@+id/text"
|
android:layout_below="@+id/text"
|
||||||
android:layout_marginRight="@dimen/margin_small"
|
android:layout_marginRight="@dimen/margin_medium"
|
||||||
|
android:scaleType="center"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/ic_visibility"/>
|
tools:src="@drawable/ic_visibility"/>
|
||||||
|
|
||||||
@@ -45,6 +39,7 @@
|
|||||||
android:layout_alignEnd="@+id/text"
|
android:layout_alignEnd="@+id/text"
|
||||||
android:layout_alignRight="@+id/text"
|
android:layout_alignRight="@+id/text"
|
||||||
android:layout_below="@+id/text"
|
android:layout_below="@+id/text"
|
||||||
|
android:layout_marginBottom="@dimen/margin_medium"
|
||||||
android:layout_toRightOf="@+id/icon"
|
android:layout_toRightOf="@+id/icon"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:minHeight="24dp"
|
android:minHeight="24dp"
|
||||||
@@ -57,10 +52,10 @@
|
|||||||
<org.briarproject.briar.android.view.AuthorView
|
<org.briarproject.briar.android.view.AuthorView
|
||||||
android:id="@+id/author"
|
android:id="@+id/author"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/button_size"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignLeft="@+id/text"
|
android:layout_alignLeft="@+id/text"
|
||||||
android:layout_below="@+id/info"
|
android:layout_below="@+id/info"
|
||||||
android:gravity="center"
|
android:layout_toLeftOf="@+id/optionsButton"
|
||||||
app:persona="commenter"/>
|
app:persona="commenter"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -68,11 +63,16 @@
|
|||||||
style="@style/BriarButtonFlat.Positive.Tiny"
|
style="@style/BriarButtonFlat.Positive.Tiny"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@+id/author"
|
||||||
android:layout_alignEnd="@+id/text"
|
android:layout_alignEnd="@+id/text"
|
||||||
android:layout_alignRight="@+id/text"
|
android:layout_alignRight="@+id/text"
|
||||||
android:layout_alignTop="@+id/author"
|
android:layout_below="@+id/info"
|
||||||
android:layout_toRightOf="@+id/author"
|
|
||||||
android:gravity="right|center_vertical"
|
android:gravity="right|center_vertical"
|
||||||
android:text="@string/options"/>
|
android:text="@string/options"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
style="@style/Divider.ThreadItem"
|
||||||
|
android:layout_below="@+id/author"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@
|
|||||||
android:text="@string/decline"/>
|
android:text="@string/decline"/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style="@style/Divider.ForumList"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/acceptButton"/>
|
android:layout_below="@+id/acceptButton"/>
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
tools:text="This is a description of the RSS feed. It can be several lines long, but it can also not exist at all if it is not present in the feed itself."/>
|
tools:text="This is a description of the RSS feed. It can be several lines long, but it can also not exist at all if it is not present in the feed itself."/>
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style="@style/Divider.ForumList"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/descriptionView"
|
android:layout_below="@+id/descriptionView"
|
||||||
|
|||||||
@@ -62,23 +62,20 @@
|
|||||||
android:background="@drawable/level_indicator_circle"
|
android:background="@drawable/level_indicator_circle"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textSize="@dimen/text_size_small"
|
android:textSize="@dimen/text_size_small"
|
||||||
android:visibility="gone"
|
android:visibility="gone"/>
|
||||||
/>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="@dimen/margin_medium"
|
|
||||||
android:layout_weight="1">
|
android:layout_weight="1">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingRight="@dimen/margin_medium"
|
android:padding="@dimen/margin_medium"
|
||||||
android:paddingTop="@dimen/margin_medium"
|
|
||||||
android:textColor="@color/briar_text_primary"
|
android:textColor="@color/briar_text_primary"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
@@ -87,26 +84,12 @@
|
|||||||
<org.briarproject.briar.android.view.AuthorView
|
<org.briarproject.briar.android.view.AuthorView
|
||||||
android:id="@+id/author"
|
android:id="@+id/author"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/button_size"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignLeft="@id/text"
|
android:layout_alignLeft="@id/text"
|
||||||
android:layout_below="@id/text"
|
android:layout_below="@id/text"
|
||||||
android:gravity="center"
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
app:persona="commenter"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/replies"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignBottom="@+id/author"
|
|
||||||
android:layout_alignTop="@+id/author"
|
|
||||||
android:layout_toLeftOf="@+id/btn_reply"
|
android:layout_toLeftOf="@+id/btn_reply"
|
||||||
android:layout_toRightOf="@+id/author"
|
app:persona="commenter"/>
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="right|end|center_vertical"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:padding="@dimen/margin_medium"
|
|
||||||
android:textSize="@dimen/text_size_tiny"
|
|
||||||
tools:text="2 replies"/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btn_reply"
|
android:id="@+id/btn_reply"
|
||||||
@@ -115,32 +98,17 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/author"
|
android:layout_alignBottom="@+id/author"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_toLeftOf="@+id/chevron"
|
android:layout_below="@+id/text"
|
||||||
|
android:layout_marginRight="@dimen/margin_medium"
|
||||||
android:text="@string/btn_reply"
|
android:text="@string/btn_reply"
|
||||||
android:textSize="@dimen/text_size_tiny"/>
|
android:textSize="@dimen/text_size_tiny"/>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/chevron"
|
|
||||||
android:layout_width="@dimen/button_size"
|
|
||||||
android:layout_height="@dimen/button_size"
|
|
||||||
android:layout_alignBottom="@+id/author"
|
|
||||||
android:layout_alignParentRight="true"
|
|
||||||
android:background="?attr/selectableItemBackground"
|
|
||||||
android:clickable="true"
|
|
||||||
android:padding="@dimen/margin_medium"
|
|
||||||
android:scaleType="center"
|
|
||||||
android:src="@drawable/selector_chevron"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/top_divider"
|
style="@style/Divider.ThreadItem"
|
||||||
style="@style/Divider.ForumList"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/margin_separator"
|
|
||||||
android:layout_alignLeft="@id/text"
|
android:layout_alignLeft="@id/text"
|
||||||
android:layout_alignParentTop="true"/>
|
android:layout_below="@+id/author"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"/>
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<item name="android:layout_marginLeft">@dimen/margin_large</item>
|
<item name="android:layout_marginLeft">@dimen/margin_large</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Divider.ForumList" parent="Divider">
|
<style name="Divider.ThreadItem" parent="Divider">
|
||||||
<item name="android:layout_width">match_parent</item>
|
<item name="android:layout_width">match_parent</item>
|
||||||
<item name="android:layout_height">1dp</item>
|
<item name="android:layout_height">1dp</item>
|
||||||
</style>
|
</style>
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
|
|
||||||
<style name="DiscussionLevelIndicator">
|
<style name="DiscussionLevelIndicator">
|
||||||
<item name="android:layout_marginLeft">4dp</item>
|
<item name="android:layout_marginLeft">4dp</item>
|
||||||
<item name="android:background">?android:attr/listDivider</item>
|
<item name="android:background">@color/divider</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="BriarTabLayout" parent="Widget.Design.TabLayout">
|
<style name="BriarTabLayout" parent="Widget.Design.TabLayout">
|
||||||
|
|||||||
@@ -114,28 +114,15 @@ public class ForumActivityTest {
|
|||||||
rc.getValue().onResult(dummyData);
|
rc.getValue().onResult(dummyData);
|
||||||
ThreadItemAdapter<ForumItem> adapter = forumActivity.getAdapter();
|
ThreadItemAdapter<ForumItem> adapter = forumActivity.getAdapter();
|
||||||
Assert.assertNotNull(adapter);
|
Assert.assertNotNull(adapter);
|
||||||
// Cascade close
|
|
||||||
assertEquals(6, adapter.getItemCount());
|
assertEquals(6, adapter.getItemCount());
|
||||||
adapter.hideDescendants(dummyData.get(2));
|
|
||||||
assertEquals(5, adapter.getItemCount());
|
|
||||||
adapter.hideDescendants(dummyData.get(1));
|
|
||||||
assertEquals(4, adapter.getItemCount());
|
|
||||||
adapter.hideDescendants(dummyData.get(0));
|
|
||||||
assertEquals(2, adapter.getItemCount());
|
|
||||||
assertTrue(dummyData.get(0).getText()
|
assertTrue(dummyData.get(0).getText()
|
||||||
.equals(adapter.getVisibleItem(0).getText()));
|
.equals(adapter.getItemAt(0).getText()));
|
||||||
assertTrue(dummyData.get(5).getText()
|
assertTrue(dummyData.get(5).getText()
|
||||||
.equals(adapter.getVisibleItem(1).getText()));
|
.equals(adapter.getItemAt(1).getText()));
|
||||||
// Cascade re-open
|
|
||||||
adapter.showDescendants(dummyData.get(0));
|
|
||||||
assertEquals(4, adapter.getItemCount());
|
|
||||||
adapter.showDescendants(dummyData.get(1));
|
|
||||||
assertEquals(5, adapter.getItemCount());
|
|
||||||
adapter.showDescendants(dummyData.get(2));
|
|
||||||
assertEquals(6, adapter.getItemCount());
|
|
||||||
assertTrue(dummyData.get(2).getText()
|
assertTrue(dummyData.get(2).getText()
|
||||||
.equals(adapter.getVisibleItem(2).getText()));
|
.equals(adapter.getItemAt(2).getText()));
|
||||||
assertTrue(dummyData.get(4).getText()
|
assertTrue(dummyData.get(4).getText()
|
||||||
.equals(adapter.getVisibleItem(4).getText()));
|
.equals(adapter.getItemAt(4).getText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
|
|||||||
|
|
||||||
void setLevel(int level);
|
void setLevel(int level);
|
||||||
|
|
||||||
void setDescendantCount(int descendantCount);
|
|
||||||
|
|
||||||
long getTimestamp();
|
long getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
|
|||||||
list.add(node);
|
list.add(node);
|
||||||
List<T> children = nodeMap.get(node.getId());
|
List<T> children = nodeMap.get(node.getId());
|
||||||
node.setLevel(level);
|
node.setLevel(level);
|
||||||
node.setDescendantCount(children.size());
|
|
||||||
for (T child : children) {
|
for (T child : children) {
|
||||||
traverse(list, child, level + 1);
|
traverse(list, child, level + 1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user