mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Show join messages properly in the threaded conversation
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package org.briarproject.android.threaded;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||
import org.briarproject.android.view.AuthorView;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
public class BaseThreadItemViewHolder<I extends ThreadItem>
|
||||
extends RecyclerView.ViewHolder {
|
||||
|
||||
private final static int ANIMATION_DURATION = 5000;
|
||||
|
||||
private final ViewGroup layout;
|
||||
private final TextView textView;
|
||||
private final AuthorView author;
|
||||
private final View topDivider;
|
||||
|
||||
public BaseThreadItemViewHolder(View v) {
|
||||
super(v);
|
||||
|
||||
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) {
|
||||
|
||||
textView.setText(StringUtils.trim(item.getText()));
|
||||
|
||||
if (pos == 0) {
|
||||
topDivider.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
topDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
author.setAuthor(item.getAuthor());
|
||||
author.setDate(item.getTimestamp());
|
||||
author.setAuthorStatus(item.getStatus());
|
||||
|
||||
if (item.equals(adapter.getReplyItem())) {
|
||||
layout.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.forum_cell_highlight));
|
||||
} else if (item.equals(adapter.getAddedItem())) {
|
||||
layout.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.forum_cell_highlight));
|
||||
animateFadeOut(adapter, adapter.getAddedItem());
|
||||
adapter.clearAddedItem();
|
||||
} else {
|
||||
layout.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.window_background));
|
||||
}
|
||||
}
|
||||
|
||||
private void animateFadeOut(final ThreadItemAdapter<I> adapter,
|
||||
final I addedItem) {
|
||||
|
||||
setIsRecyclable(false);
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
adapter.addAnimatingItem(addedItem, anim);
|
||||
ColorDrawable viewColor = (ColorDrawable) layout.getBackground();
|
||||
anim.setIntValues(viewColor.getColor(), ContextCompat
|
||||
.getColor(getContext(), R.color.window_background));
|
||||
anim.setEvaluator(new ArgbEvaluator());
|
||||
anim.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setIsRecyclable(true);
|
||||
adapter.removeAnimatingItem(addedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
setIsRecyclable(true);
|
||||
adapter.removeAnimatingItem(addedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
layout.setBackgroundColor(
|
||||
(Integer) valueAnimator.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
anim.setDuration(ANIMATION_DURATION);
|
||||
anim.start();
|
||||
}
|
||||
|
||||
protected Context getContext() {
|
||||
return textView.getContext();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
package org.briarproject.android.threaded;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.api.clients.MessageTree.MessageNode;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.Author.Status;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED;
|
||||
|
||||
/* This class is not thread safe */
|
||||
@UiThread
|
||||
@NotThreadSafe
|
||||
public abstract class ThreadItem implements MessageNode {
|
||||
|
||||
private final MessageId messageId;
|
||||
|
||||
@@ -5,7 +5,11 @@ import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.util.VersionedAdapter;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
@@ -17,8 +21,8 @@ import java.util.Map;
|
||||
|
||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
public abstract class ThreadItemAdapter<I extends ThreadItem>
|
||||
extends RecyclerView.Adapter<ThreadItemViewHolder<I>>
|
||||
public class ThreadItemAdapter<I extends ThreadItem>
|
||||
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
|
||||
implements VersionedAdapter {
|
||||
|
||||
static final int UNDEFINED = -1;
|
||||
@@ -42,7 +46,15 @@ public abstract class ThreadItemAdapter<I extends ThreadItem>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ThreadItemViewHolder<I> ui, int position) {
|
||||
public BaseThreadItemViewHolder<I> onCreateViewHolder(
|
||||
ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.list_item_thread, parent, false);
|
||||
return new ThreadItemViewHolder<>(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
|
||||
I item = getVisibleItem(position);
|
||||
if (item == null) return;
|
||||
listener.onItemVisible(item);
|
||||
|
||||
@@ -1,45 +1,30 @@
|
||||
package org.briarproject.android.threaded;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||
import org.briarproject.android.view.AuthorView;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
@UiThread
|
||||
public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
||||
extends RecyclerView.ViewHolder {
|
||||
@NotNullByDefault
|
||||
public class ThreadItemViewHolder<I extends ThreadItem>
|
||||
extends BaseThreadItemViewHolder<I> {
|
||||
|
||||
private final static int ANIMATION_DURATION = 5000;
|
||||
|
||||
private final TextView textView, lvlText, repliesText;
|
||||
private final AuthorView author;
|
||||
private final TextView lvlText, repliesText;
|
||||
private final View[] lvls;
|
||||
private final View chevron, replyButton;
|
||||
private final ViewGroup cell;
|
||||
private final View topDivider;
|
||||
|
||||
public ThreadItemViewHolder(View v) {
|
||||
super(v);
|
||||
|
||||
textView = (TextView) v.findViewById(R.id.text);
|
||||
lvlText = (TextView) v.findViewById(R.id.nested_line_text);
|
||||
author = (AuthorView) v.findViewById(R.id.author);
|
||||
repliesText = (TextView) v.findViewById(R.id.replies);
|
||||
int[] nestedLineIds = {
|
||||
R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
|
||||
@@ -51,21 +36,13 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
||||
}
|
||||
chevron = v.findViewById(R.id.chevron);
|
||||
replyButton = v.findViewById(R.id.btn_reply);
|
||||
cell = (ViewGroup) v.findViewById(R.id.forum_cell);
|
||||
topDivider = v.findViewById(R.id.top_divider);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
textView.setText(StringUtils.trim(item.getText()));
|
||||
|
||||
if (pos == 0) {
|
||||
topDivider.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
topDivider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
super.bind(adapter, listener, item, pos);
|
||||
|
||||
for (int i = 0; i < lvls.length; i++) {
|
||||
lvls[i].setVisibility(i < item.getLevel() ? VISIBLE : GONE);
|
||||
@@ -76,9 +53,6 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
||||
} else {
|
||||
lvlText.setVisibility(GONE);
|
||||
}
|
||||
author.setAuthor(item.getAuthor());
|
||||
author.setDate(item.getTimestamp());
|
||||
author.setAuthorStatus(item.getStatus());
|
||||
|
||||
int replies = adapter.getReplyCount(item);
|
||||
if (replies == 0) {
|
||||
@@ -110,18 +84,6 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
||||
} else {
|
||||
chevron.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (item.equals(adapter.getReplyItem())) {
|
||||
cell.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.forum_cell_highlight));
|
||||
} else if (item.equals(adapter.getAddedItem())) {
|
||||
cell.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.forum_cell_highlight));
|
||||
animateFadeOut(adapter, adapter.getAddedItem());
|
||||
adapter.clearAddedItem();
|
||||
} else {
|
||||
cell.setBackgroundColor(ContextCompat
|
||||
.getColor(getContext(), R.color.window_background));
|
||||
}
|
||||
replyButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -131,52 +93,4 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
||||
});
|
||||
}
|
||||
|
||||
private void animateFadeOut(final ThreadItemAdapter<I> adapter,
|
||||
final I addedItem) {
|
||||
|
||||
setIsRecyclable(false);
|
||||
ValueAnimator anim = new ValueAnimator();
|
||||
adapter.addAnimatingItem(addedItem, anim);
|
||||
ColorDrawable viewColor = (ColorDrawable) cell.getBackground();
|
||||
anim.setIntValues(viewColor.getColor(), ContextCompat
|
||||
.getColor(getContext(), R.color.window_background));
|
||||
anim.setEvaluator(new ArgbEvaluator());
|
||||
anim.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setIsRecyclable(true);
|
||||
adapter.removeAnimatingItem(addedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
setIsRecyclable(true);
|
||||
adapter.removeAnimatingItem(addedItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
cell.setBackgroundColor(
|
||||
(Integer) valueAnimator.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
anim.setDuration(ANIMATION_DURATION);
|
||||
anim.start();
|
||||
}
|
||||
|
||||
private Context getContext() {
|
||||
return textView.getContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.android.threaded;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
@@ -39,6 +40,8 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
|
||||
@UiThread
|
||||
void onGroupRemoved();
|
||||
|
||||
Context getApplicationContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
|
||||
private volatile GroupId groupId;
|
||||
|
||||
protected ThreadListListener<H> listener;
|
||||
protected volatile ThreadListListener<H> listener;
|
||||
|
||||
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||
@@ -159,7 +159,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
for (H header : headers) {
|
||||
if (!bodyCache.containsKey(header.getId())) {
|
||||
bodyCache.put(header.getId(),
|
||||
loadMessageBody(header.getId()));
|
||||
loadMessageBody(header));
|
||||
}
|
||||
}
|
||||
duration = System.currentTimeMillis() - now;
|
||||
@@ -181,7 +181,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
protected abstract Collection<H> loadHeaders() throws DbException;
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract String loadMessageBody(MessageId id) throws DbException;
|
||||
protected abstract String loadMessageBody(H header) throws DbException;
|
||||
|
||||
@Override
|
||||
public void loadItem(final H header,
|
||||
@@ -193,7 +193,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
long now = System.currentTimeMillis();
|
||||
String body;
|
||||
if (!bodyCache.containsKey(header.getId())) {
|
||||
body = loadMessageBody(header.getId());
|
||||
body = loadMessageBody(header);
|
||||
bodyCache.put(header.getId(), body);
|
||||
} else {
|
||||
body = bodyCache.get(header.getId());
|
||||
|
||||
Reference in New Issue
Block a user