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:
Torsten Grote
2017-01-04 13:00:16 +00:00
21 changed files with 160 additions and 479 deletions

View File

@@ -100,7 +100,7 @@ public class ForumActivity extends
super.onActivityResult(request, result, data);
if (request == REQUEST_SHARE_FORUM && result == RESULT_OK) {
displaySnackbarShort(R.string.forum_shared_snackbar);
displaySnackbar(R.string.forum_shared_snackbar);
}
}

View File

@@ -183,7 +183,7 @@ public class GroupActivity extends
@Override
protected void onActivityResult(int request, int result, Intent data) {
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);
}

View File

@@ -31,9 +31,8 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
@LayoutRes
@Override
public int getItemViewType(int position) {
GroupMessageItem item = getVisibleItem(position);
if (item != null) return item.getLayout();
return R.layout.list_item_thread;
GroupMessageItem item = items.get(position);
return item.getLayout();
}
@Override
@@ -58,7 +57,7 @@ class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
GroupMessageItem item = items.get(position);
if (item instanceof JoinMessageItem) {
((JoinMessageItem) item).setVisibility(v);
notifyItemChanged(getVisiblePos(item), item);
notifyItemChanged(findItemPosition(item), item);
}
}
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.privategroup.conversation;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.identity.Author;
@@ -20,9 +21,8 @@ class GroupMessageItem extends ThreadItem {
private final GroupId groupId;
private GroupMessageItem(MessageId messageId, GroupId groupId,
MessageId parentId,
String text, long timestamp, Author author, Status status,
boolean isRead) {
@Nullable MessageId parentId, String text, long timestamp,
Author author, Status status, boolean isRead) {
super(messageId, parentId, text, timestamp, author, status, isRead);
this.groupId = groupId;
}

View File

@@ -27,11 +27,6 @@ class JoinMessageItem extends GroupMessageItem {
return 0;
}
@Override
public boolean hasDescendants() {
return false;
}
@Override
@LayoutRes
public int getLayout() {

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
import org.briarproject.briar.android.threaded.ThreadItemAdapter;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
@@ -41,10 +40,9 @@ class JoinMessageItemViewHolder
}
@Override
public void bind(ThreadItemAdapter<GroupMessageItem> adapter,
ThreadItemListener<GroupMessageItem> listener,
GroupMessageItem item, int pos) {
super.bind(adapter, listener, item, pos);
public void bind(GroupMessageItem item,
ThreadItemListener<GroupMessageItem> listener) {
super.bind(item, listener);
if (isCreator) bindForCreator((JoinMessageItem) item);
else bind((JoinMessageItem) item);

View File

@@ -20,9 +20,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import org.briarproject.briar.android.view.AuthorView;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
@UiThread
@NotNullByDefault
public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
@@ -33,7 +30,6 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
protected final TextView textView;
private final ViewGroup layout;
private final AuthorView author;
private final View topDivider;
public BaseThreadItemViewHolder(View v) {
super(v);
@@ -41,43 +37,30 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
layout = (ViewGroup) v.findViewById(R.id.layout);
textView = (TextView) v.findViewById(R.id.text);
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
public void bind(final ThreadItemAdapter<I> adapter,
final ThreadItemListener<I> listener, final I item, int pos) {
public void bind(final I item, final ThreadItemListener<I> listener) {
textView.setText(StringUtils.trim(item.getText()));
if (pos == 0) {
topDivider.setVisibility(INVISIBLE);
} else {
topDivider.setVisibility(VISIBLE);
}
author.setAuthor(item.getAuthor());
author.setDate(item.getTimestamp());
author.setAuthorStatus(item.getStatus());
if (item.equals(adapter.getReplyItem())) {
if (item.isHighlighted()) {
layout.setActivated(true);
} else if (!item.isRead()) {
layout.setActivated(true);
animateFadeOut(adapter, item);
animateFadeOut();
listener.onUnreadItemVisible(item);
} else {
layout.setActivated(false);
}
}
private void animateFadeOut(final ThreadItemAdapter<I> adapter,
final I addedItem) {
private void animateFadeOut() {
setIsRecyclable(false);
ValueAnimator anim = new ValueAnimator();
adapter.addAnimatingItem(addedItem, anim);
ColorDrawable viewColor = new ColorDrawable(ContextCompat
.getColor(getContext(), R.color.forum_cell_highlight));
anim.setIntValues(viewColor.getColor(), ContextCompat
@@ -94,7 +77,6 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
R.drawable.list_item_thread_background);
layout.setActivated(false);
setIsRecyclable(true);
adapter.removeAnimatingItem(addedItem);
}
@Override
public void onAnimationCancel(Animator animation) {

View File

@@ -23,9 +23,7 @@ public abstract class ThreadItem implements MessageNode {
private final Author author;
private final Status status;
private int level = UNDEFINED;
private boolean isShowingDescendants = true;
private int descendantCount = 0;
private boolean isRead;
private boolean isRead, highlighted;
public ThreadItem(MessageId messageId, @Nullable MessageId parentId,
String text, long timestamp, Author author, Status status,
@@ -37,6 +35,7 @@ public abstract class ThreadItem implements MessageNode {
this.author = author;
this.status = status;
this.isRead = isRead;
this.highlighted = false;
}
public String getText() {
@@ -71,19 +70,11 @@ public abstract class ThreadItem implements MessageNode {
return status;
}
public boolean isShowingDescendants() {
return isShowingDescendants;
}
@Override
public void setLevel(int level) {
this.level = level;
}
public void setShowingDescendants(boolean showingDescendants) {
this.isShowingDescendants = showingDescendants;
}
public boolean isRead() {
return isRead;
}
@@ -92,13 +83,12 @@ public abstract class ThreadItem implements MessageNode {
isRead = read;
}
public boolean hasDescendants() {
return descendantCount > 0;
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
}
@Override
public void setDescendantCount(int descendantCount) {
this.descendantCount = descendantCount;
public boolean isHighlighted() {
return highlighted;
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.threaded;
import android.animation.ValueAnimator;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
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.android.util.VersionedAdapter;
import java.util.ArrayList;
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;
@UiThread
public class ThreadItemAdapter<I extends ThreadItem>
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
implements VersionedAdapter {
@@ -28,13 +24,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
static final int UNDEFINED = -1;
protected final NestedTreeList<I> items = new NestedTreeList<>();
private final Map<I, ValueAnimator> animatingItems = new HashMap<>();
private final ThreadItemListener<I> listener;
private final LinearLayoutManager layoutManager;
@Nullable
private I replyItem;
private volatile int revision = 0;
public ThreadItemAdapter(ThreadItemListener<I> listener,
@@ -53,24 +45,23 @@ public class ThreadItemAdapter<I extends ThreadItem>
@Override
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
I item = getVisibleItem(position);
if (item == null) return;
ui.bind(this, listener, item, position);
I item = items.get(position);
ui.bind(item, listener);
}
/**
* Contrary to the super class adapter,
* this returns the number of <b>visible</b> items,
* not all items in the dataset.
*/
@Override
public int getItemCount() {
return getVisiblePos(null);
return items.size();
}
@Nullable
I getReplyItem() {
return replyItem;
@Override
public int getRevision() {
return revision;
}
@Override
public void incrementRevision() {
revision++;
}
public void setItems(Collection<I> items) {
@@ -81,233 +72,51 @@ public class ThreadItemAdapter<I extends ThreadItem>
public void add(I item) {
items.add(item);
if (item.getParentId() == null) {
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;
}
}
}
}
notifyItemInserted(findItemPosition(item));
}
void scrollTo(I item) {
int visiblePos = getVisiblePos(item);
MessageId parentId = item.getParentId();
if (visiblePos == NO_POSITION && parentId != 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();
}
}
@Nullable
public I getItemAt(int position) {
if (position == NO_POSITION || position >= items.size()) {
return null;
}
if (visiblePos != NO_POSITION)
layoutManager.scrollToPositionWithOffset(visiblePos, 0);
return items.get(position);
}
int getReplyCount(I item) {
int counter = 0;
int pos = items.indexOf(item);
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++;
}
protected int findItemPosition(@Nullable I item) {
for (int i = 0; i < items.size(); i++) {
if (items.get(i).equals(item)) return i;
}
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
* @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.
* Only one item can be highlighted at a time.
*/
void setHighlightedItem(@Nullable MessageId id) {
for (int i = 0; i < items.size(); i++) {
I item = items.get(i);
if (id != null && item.getId().equals(id)) {
item.setHighlighted(true);
notifyItemChanged(i, item);
} else if (item.isHighlighted()) {
item.setHighlighted(false);
notifyItemChanged(i, item);
}
}
}
@Nullable
public I getVisibleItem(int position) {
int levelLimit = UNDEFINED;
for (I item : items) {
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;
}
I getHighlightedItem() {
for (I i : items) {
if (i.isHighlighted()) return i;
}
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.
*
@@ -321,24 +130,13 @@ public class ThreadItemAdapter<I extends ThreadItem>
return new UnreadCount(0, 0);
int unreadCounterTop = 0, unreadCounterBottom = 0;
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 (visibleCounter > positionBottom && !i.isRead()) {
unreadCounterBottom++;
} else if (visibleCounter < positionTop && !i.isRead()) {
for (int i = 0; i < items.size(); i++) {
I item = items.get(i);
if (i < positionTop && !item.isRead()) {
unreadCounterTop++;
} else if (!i.isShowingDescendants()) {
levelLimit = i.getLevel();
} else if (i > positionBottom && !item.isRead()) {
unreadCounterBottom++;
}
visibleCounter++;
}
return new UnreadCount(unreadCounterTop, unreadCounterBottom);
}
@@ -348,22 +146,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
*/
public int getVisibleUnreadPosBottom() {
final int positionBottom = layoutManager.findLastVisibleItemPosition();
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 (visibleCounter > positionBottom && !i.isRead()) {
return visibleCounter;
} else if (!i.isShowingDescendants()) {
levelLimit = i.getLevel();
}
visibleCounter++;
if (positionBottom == NO_POSITION) return NO_POSITION;
for (int i = positionBottom + 1; i < items.size(); i++) {
if (!items.get(i).isRead()) return i;
}
return NO_POSITION;
}
@@ -374,24 +159,12 @@ public class ThreadItemAdapter<I extends ThreadItem>
public int getVisibleUnreadPosTop() {
final int positionTop = layoutManager.findFirstVisibleItemPosition();
int position = NO_POSITION;
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 (visibleCounter < positionTop && !i.isRead()) {
position = visibleCounter;
} if (visibleCounter >= positionTop) {
for (int i = 0; i < items.size(); i++) {
if (i < positionTop && !items.get(i).isRead()) {
position = i;
} else if (i >= positionTop) {
return position;
} else if (!i.isShowingDescendants()) {
levelLimit = i.getLevel();
}
visibleCounter++;
}
return NO_POSITION;
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.threaded;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.annotation.UiThread;
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.api.client.NamedGroup;
import org.briarproject.briar.api.client.PostHeader;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
import java.util.Collection;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
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 BriarRecyclerView list;
private LinearLayoutManager layoutManager;
protected TextInputView textInput;
protected GroupId groupId;
private UnreadMessageButton upButton, downButton;
@Nullable
private MessageId replyId;
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.setListener(this);
list = (BriarRecyclerView) findViewById(R.id.list);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
list.setLayoutManager(linearLayoutManager);
adapter = createAdapter(linearLayoutManager);
layoutManager = new LinearLayoutManager(this);
list.setLayoutManager(layoutManager);
adapter = createAdapter(layoutManager);
list.setAdapter(adapter);
list.getRecyclerView().addOnScrollListener(
@@ -179,7 +182,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
adapter.setItems(items);
list.showData();
if (replyId != null)
adapter.setReplyItemById(replyId);
adapter.setHighlightedItem(replyId);
}
} else {
LOG.info("Concurrent update, reloading");
@@ -240,7 +243,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
super.onSaveInstanceState(outState);
boolean visible = textInput.getVisibility() == VISIBLE;
outState.putBoolean(KEY_INPUT_VISIBILITY, visible);
ThreadItem replyItem = adapter.getReplyItem();
ThreadItem replyItem = adapter.getHighlightedItem();
if (replyItem != null) {
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() {
if (textInput.getVisibility() == VISIBLE) {
textInput.setVisibility(GONE);
adapter.setReplyItem(null);
adapter.setHighlightedItem(null);
} else {
super.onBackPressed();
}
@@ -276,8 +279,21 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
}
@Override
public void onReplyClick(I item) {
public void onReplyClick(final I 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
@@ -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.getView().setBackgroundResource(R.color.briar_primary);
snackbar.show();
@@ -318,7 +342,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
textInput.showSoftKeyboard();
textInput.setHint(replyItem == null ? R.string.forum_new_message_hint :
R.string.forum_message_reply_hint);
adapter.setReplyItem(replyItem);
adapter.setHighlightedItem(
replyItem == null ? null : replyItem.getId());
}
@Override
@@ -326,10 +351,10 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
if (text.trim().length() == 0)
return;
if (StringUtils.utf8IsTooLong(text, getMaxBodyLength())) {
displaySnackbarShort(R.string.text_too_long);
displaySnackbar(R.string.text_too_long);
return;
}
I replyItem = adapter.getReplyItem();
I replyItem = adapter.getHighlightedItem();
UiResultExceptionHandler<I, DbException> handler =
new UiResultExceptionHandler<I, DbException>(this) {
@Override
@@ -346,7 +371,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
textInput.hideSoftKeyboard();
textInput.setVisibility(GONE);
textInput.setText("");
adapter.setReplyItem(null);
adapter.setHighlightedItem(null);
}
protected abstract int getMaxBodyLength();
@@ -377,7 +402,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
adapter.add(item);
if (isLocal) {
displaySnackbarShort(getItemPostedString());
displaySnackbar(getItemPostedString());
scrollToItemAtTop(item);
} else {
updateUnreadCount();
}

View File

@@ -16,32 +16,29 @@ import static android.view.View.VISIBLE;
public class ThreadPostViewHolder<I extends ThreadItem>
extends BaseThreadItemViewHolder<I> {
private final TextView lvlText, repliesText;
private final TextView lvlText;
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) {
super(v);
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];
for (int i = 0; i < lvls.length; i++) {
lvls[i] = v.findViewById(nestedLineIds[i]);
}
chevron = v.findViewById(R.id.chevron);
replyButton = v.findViewById(R.id.btn_reply);
}
// TODO improve encapsulation, so we don't need to pass the adapter here
@Override
public void bind(final ThreadItemAdapter<I> adapter,
final ThreadItemListener<I> listener, final I item, int pos) {
super.bind(adapter, listener, item, pos);
public void bind(final I item, final ThreadItemListener<I> listener) {
super.bind(item, listener);
for (int i = 0; i < lvls.length; i++) {
lvls[i].setVisibility(i < item.getLevel() ? VISIBLE : GONE);
@@ -53,41 +50,10 @@ public class ThreadPostViewHolder<I extends ThreadItem>
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() {
@Override
public void onClick(View v) {
listener.onReplyClick(item);
adapter.scrollTo(item);
}
});
}

View File

@@ -60,7 +60,7 @@
tools:text="Dec 24"/>
<View
style="@style/Divider.ForumList"
style="@style/Divider.ThreadItem"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/postCountView"/>

View File

@@ -97,7 +97,7 @@
<View
android:id="@+id/divider"
style="@style/Divider.ForumList"
style="@style/Divider.ThreadItem"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/statusView"

View File

@@ -6,23 +6,14 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_medium"
android:baselineAligned="false"
android:orientation="vertical">
<View
android:id="@+id/top_divider"
style="@style/Divider.ForumList"
android:layout_alignParentTop="true"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/top_divider"
android:layout_marginBottom="@dimen/margin_small"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginTop="@dimen/margin_medium"
android:layout_margin="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
@@ -32,9 +23,12 @@
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/info"
android:layout_alignLeft="@+id/text"
android:layout_alignTop="@+id/info"
android:layout_below="@+id/text"
android:layout_marginRight="@dimen/margin_small"
android:layout_marginRight="@dimen/margin_medium"
android:scaleType="center"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_visibility"/>
@@ -45,6 +39,7 @@
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_below="@+id/text"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_toRightOf="@+id/icon"
android:gravity="center_vertical"
android:minHeight="24dp"
@@ -57,10 +52,10 @@
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/text"
android:layout_below="@+id/info"
android:gravity="center"
android:layout_toLeftOf="@+id/optionsButton"
app:persona="commenter"/>
<Button
@@ -68,11 +63,16 @@
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/author"
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_alignTop="@+id/author"
android:layout_toRightOf="@+id/author"
android:layout_below="@+id/info"
android:gravity="right|center_vertical"
android:text="@string/options"/>
<View
style="@style/Divider.ThreadItem"
android:layout_below="@+id/author"
android:layout_marginTop="@dimen/margin_medium"/>
</RelativeLayout>

View File

@@ -81,7 +81,7 @@
android:text="@string/decline"/>
<View
style="@style/Divider.ForumList"
style="@style/Divider.ThreadItem"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/acceptButton"/>

View File

@@ -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."/>
<View
style="@style/Divider.ForumList"
style="@style/Divider.ThreadItem"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/descriptionView"

View File

@@ -62,23 +62,20 @@
android:background="@drawable/level_indicator_circle"
android:gravity="center"
android:textSize="@dimen/text_size_small"
android:visibility="gone"
/>
android:visibility="gone"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_weight="1">
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="@dimen/margin_medium"
android:paddingTop="@dimen/margin_medium"
android:padding="@dimen/margin_medium"
android:textColor="@color/briar_text_primary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
@@ -87,26 +84,12 @@
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="@dimen/button_size"
android:layout_height="wrap_content"
android:layout_alignLeft="@id/text"
android:layout_below="@id/text"
android:gravity="center"
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_marginLeft="@dimen/margin_medium"
android:layout_toLeftOf="@+id/btn_reply"
android:layout_toRightOf="@+id/author"
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"/>
app:persona="commenter"/>
<TextView
android:id="@+id/btn_reply"
@@ -115,32 +98,17 @@
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/author"
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: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
android:id="@+id/top_divider"
style="@style/Divider.ForumList"
android:layout_width="match_parent"
android:layout_height="@dimen/margin_separator"
style="@style/Divider.ThreadItem"
android:layout_alignLeft="@id/text"
android:layout_alignParentTop="true"/>
android:layout_below="@+id/author"
android:layout_marginTop="@dimen/margin_medium"/>
</RelativeLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -79,7 +79,7 @@
<item name="android:layout_marginLeft">@dimen/margin_large</item>
</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_height">1dp</item>
</style>
@@ -102,7 +102,7 @@
<style name="DiscussionLevelIndicator">
<item name="android:layout_marginLeft">4dp</item>
<item name="android:background">?android:attr/listDivider</item>
<item name="android:background">@color/divider</item>
</style>
<style name="BriarTabLayout" parent="Widget.Design.TabLayout">

View File

@@ -114,28 +114,15 @@ public class ForumActivityTest {
rc.getValue().onResult(dummyData);
ThreadItemAdapter<ForumItem> adapter = forumActivity.getAdapter();
Assert.assertNotNull(adapter);
// Cascade close
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()
.equals(adapter.getVisibleItem(0).getText()));
.equals(adapter.getItemAt(0).getText()));
assertTrue(dummyData.get(5).getText()
.equals(adapter.getVisibleItem(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());
.equals(adapter.getItemAt(1).getText()));
assertTrue(dummyData.get(2).getText()
.equals(adapter.getVisibleItem(2).getText()));
.equals(adapter.getItemAt(2).getText()));
assertTrue(dummyData.get(4).getText()
.equals(adapter.getVisibleItem(4).getText()));
.equals(adapter.getItemAt(4).getText()));
}
}

View File

@@ -31,8 +31,6 @@ public interface MessageTree<T extends MessageTree.MessageNode> {
void setLevel(int level);
void setDescendantCount(int descendantCount);
long getTimestamp();
}

View File

@@ -83,7 +83,6 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
list.add(node);
List<T> children = nodeMap.get(node.getId());
node.setLevel(level);
node.setDescendantCount(children.size());
for (T child : children) {
traverse(list, child, level + 1);
}