mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 06:39:54 +01:00
Show join messages properly in the threaded conversation
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/layout"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/forum_cell"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
48
briar-android/res/layout/list_item_thread_notice.xml
Normal file
48
briar-android/res/layout/list_item_thread_notice.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
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_width="match_parent"
|
||||||
|
android:layout_height="@dimen/margin_separator"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/margin_small"
|
||||||
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
|
android:layout_marginRight="@dimen/margin_medium"
|
||||||
|
android:layout_marginTop="@dimen/margin_medium"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<org.briarproject.android.view.AuthorView
|
||||||
|
android:id="@+id/author"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:persona="commenter"/>
|
||||||
|
|
||||||
|
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="@dimen/margin_medium"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textColor="@color/briar_text_secondary"
|
||||||
|
android:textIsSelectable="true"
|
||||||
|
android:textSize="@dimen/text_size_medium"
|
||||||
|
android:textStyle="italic"
|
||||||
|
tools:text="@string/groups_member_joined"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -166,6 +166,7 @@
|
|||||||
<string name="groups_invite_members">Invite Members</string>
|
<string name="groups_invite_members">Invite Members</string>
|
||||||
<string name="groups_leave">Leave Group</string>
|
<string name="groups_leave">Leave Group</string>
|
||||||
<string name="groups_dissolve">Dissolve Group</string>
|
<string name="groups_dissolve">Dissolve Group</string>
|
||||||
|
<string name="groups_member_joined">joined the group.</string>
|
||||||
|
|
||||||
<!-- Private Group Invitations -->
|
<!-- Private Group Invitations -->
|
||||||
<string name="groups_invitations_title">Group Invitations</string>
|
<string name="groups_invitations_title">Group Invitations</string>
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ public class ActivityModule {
|
|||||||
@Provides
|
@Provides
|
||||||
protected GroupController provideGroupController(
|
protected GroupController provideGroupController(
|
||||||
GroupControllerImpl groupController) {
|
GroupControllerImpl groupController) {
|
||||||
|
activity.addLifecycleController(groupController);
|
||||||
return groupController;
|
return groupController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import android.widget.Toast;
|
|||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.ActivityComponent;
|
import org.briarproject.android.ActivityComponent;
|
||||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.android.sharing.ShareForumActivity;
|
|
||||||
import org.briarproject.android.sharing.ForumSharingStatusActivity;
|
import org.briarproject.android.sharing.ForumSharingStatusActivity;
|
||||||
|
import org.briarproject.android.sharing.ShareForumActivity;
|
||||||
|
import org.briarproject.android.threaded.ThreadItemAdapter;
|
||||||
import org.briarproject.android.threaded.ThreadListActivity;
|
import org.briarproject.android.threaded.ThreadListActivity;
|
||||||
import org.briarproject.android.threaded.ThreadListController;
|
import org.briarproject.android.threaded.ThreadListController;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
@@ -35,7 +36,7 @@ import static android.widget.Toast.LENGTH_SHORT;
|
|||||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||||
|
|
||||||
public class ForumActivity extends
|
public class ForumActivity extends
|
||||||
ThreadListActivity<Forum, ForumItem, ForumPostHeader, NestedForumAdapter> {
|
ThreadListActivity<Forum, ForumItem, ForumPostHeader, ThreadItemAdapter<ForumItem>> {
|
||||||
|
|
||||||
private static final int REQUEST_FORUM_SHARED = 3;
|
private static final int REQUEST_FORUM_SHARED = 3;
|
||||||
|
|
||||||
@@ -74,9 +75,9 @@ public class ForumActivity extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected NestedForumAdapter createAdapter(
|
protected ThreadItemAdapter<ForumItem> createAdapter(
|
||||||
LinearLayoutManager layoutManager) {
|
LinearLayoutManager layoutManager) {
|
||||||
return new NestedForumAdapter(this, layoutManager);
|
return new ThreadItemAdapter<>(this, layoutManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ public class ForumControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String loadMessageBody(MessageId id) throws DbException {
|
protected String loadMessageBody(ForumPostHeader h) throws DbException {
|
||||||
return StringUtils.fromUtf8(forumManager.getPostBody(id));
|
return StringUtils.fromUtf8(forumManager.getPostBody(h.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
package org.briarproject.android.forum;
|
|
||||||
|
|
||||||
import android.support.annotation.UiThread;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import org.briarproject.R;
|
|
||||||
import org.briarproject.android.threaded.ThreadItemAdapter;
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
class NestedForumAdapter extends ThreadItemAdapter<ForumItem> {
|
|
||||||
|
|
||||||
NestedForumAdapter(ThreadItemListener<ForumItem> listener,
|
|
||||||
LinearLayoutManager layoutManager) {
|
|
||||||
super(listener, layoutManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NestedForumHolder onCreateViewHolder(ViewGroup parent,
|
|
||||||
int viewType) {
|
|
||||||
View v = LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.list_item_forum_post, parent, false);
|
|
||||||
return new NestedForumHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package org.briarproject.android.forum;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.briarproject.android.threaded.ThreadItemViewHolder;
|
|
||||||
|
|
||||||
public class NestedForumHolder extends ThreadItemViewHolder<ForumItem> {
|
|
||||||
|
|
||||||
public NestedForumHolder(View v) {
|
|
||||||
super(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package org.briarproject.android.privategroup.conversation;
|
|||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.api.AndroidNotificationManager;
|
import org.briarproject.android.api.AndroidNotificationManager;
|
||||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.android.threaded.ThreadListControllerImpl;
|
import org.briarproject.android.threaded.ThreadListControllerImpl;
|
||||||
@@ -17,6 +18,7 @@ import org.briarproject.api.lifecycle.LifecycleManager;
|
|||||||
import org.briarproject.api.privategroup.GroupMessage;
|
import org.briarproject.api.privategroup.GroupMessage;
|
||||||
import org.briarproject.api.privategroup.GroupMessageFactory;
|
import org.briarproject.api.privategroup.GroupMessageFactory;
|
||||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||||
|
import org.briarproject.api.privategroup.JoinMessageHeader;
|
||||||
import org.briarproject.api.privategroup.PrivateGroup;
|
import org.briarproject.api.privategroup.PrivateGroup;
|
||||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
@@ -90,8 +92,13 @@ public class GroupControllerImpl extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String loadMessageBody(MessageId id) throws DbException {
|
protected String loadMessageBody(GroupMessageHeader header)
|
||||||
return privateGroupManager.getMessageBody(id);
|
throws DbException {
|
||||||
|
if (header instanceof JoinMessageHeader) {
|
||||||
|
return listener.getApplicationContext()
|
||||||
|
.getString(R.string.groups_member_joined);
|
||||||
|
}
|
||||||
|
return privateGroupManager.getMessageBody(header.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -162,6 +169,9 @@ public class GroupControllerImpl extends
|
|||||||
@Override
|
@Override
|
||||||
protected GroupMessageItem buildItem(GroupMessageHeader header,
|
protected GroupMessageItem buildItem(GroupMessageHeader header,
|
||||||
String body) {
|
String body) {
|
||||||
|
if (header instanceof JoinMessageHeader) {
|
||||||
|
return new JoinMessageItem(header, body);
|
||||||
|
}
|
||||||
return new GroupMessageItem(header, body);
|
return new GroupMessageItem(header, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
|
import org.briarproject.android.threaded.BaseThreadItemViewHolder;
|
||||||
import org.briarproject.android.threaded.ThreadItemAdapter;
|
import org.briarproject.android.threaded.ThreadItemAdapter;
|
||||||
|
import org.briarproject.android.threaded.ThreadItemViewHolder;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
||||||
@@ -18,11 +20,23 @@ public class GroupMessageAdapter extends ThreadItemAdapter<GroupMessageItem> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupMessageViewHolder onCreateViewHolder(ViewGroup parent,
|
public int getItemViewType(int position) {
|
||||||
int viewType) {
|
GroupMessageItem item = getVisibleItem(position);
|
||||||
|
if (item instanceof JoinMessageItem) {
|
||||||
|
return R.layout.list_item_thread_notice;
|
||||||
|
}
|
||||||
|
return R.layout.list_item_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseThreadItemViewHolder<GroupMessageItem> onCreateViewHolder(
|
||||||
|
ViewGroup parent, int type) {
|
||||||
View v = LayoutInflater.from(parent.getContext())
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_item_forum_post, parent, false);
|
.inflate(type, parent, false);
|
||||||
return new GroupMessageViewHolder(v);
|
if (type == R.layout.list_item_thread_notice) {
|
||||||
|
return new BaseThreadItemViewHolder<>(v);
|
||||||
|
}
|
||||||
|
return new ThreadItemViewHolder<>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.briarproject.api.sync.MessageId;
|
|||||||
|
|
||||||
class GroupMessageItem extends ThreadItem {
|
class GroupMessageItem extends ThreadItem {
|
||||||
|
|
||||||
GroupMessageItem(MessageId messageId, MessageId parentId,
|
private GroupMessageItem(MessageId messageId, MessageId parentId,
|
||||||
String text, long timestamp, Author author, Status status,
|
String text, long timestamp, Author author, Status status,
|
||||||
boolean isRead) {
|
boolean isRead) {
|
||||||
super(messageId, parentId, text, timestamp, author, status, isRead);
|
super(messageId, parentId, text, timestamp, author, status, isRead);
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package org.briarproject.android.privategroup.conversation;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.briarproject.android.threaded.ThreadItemViewHolder;
|
|
||||||
|
|
||||||
public class GroupMessageViewHolder
|
|
||||||
extends ThreadItemViewHolder<GroupMessageItem> {
|
|
||||||
|
|
||||||
public GroupMessageViewHolder(View v) {
|
|
||||||
super(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.briarproject.android.privategroup.conversation;
|
||||||
|
|
||||||
|
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||||
|
|
||||||
|
class JoinMessageItem extends GroupMessageItem {
|
||||||
|
|
||||||
|
JoinMessageItem(GroupMessageHeader h,
|
||||||
|
String text) {
|
||||||
|
super(h, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLevel() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDescendants() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
package org.briarproject.android.threaded;
|
||||||
|
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.api.clients.MessageTree.MessageNode;
|
import org.briarproject.api.clients.MessageTree.MessageNode;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.Author.Status;
|
import org.briarproject.api.identity.Author.Status;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED;
|
import static org.briarproject.android.threaded.ThreadItemAdapter.UNDEFINED;
|
||||||
|
|
||||||
/* This class is not thread safe */
|
@UiThread
|
||||||
|
@NotThreadSafe
|
||||||
public abstract class ThreadItem implements MessageNode {
|
public abstract class ThreadItem implements MessageNode {
|
||||||
|
|
||||||
private final MessageId messageId;
|
private final MessageId messageId;
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ 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;
|
||||||
import android.support.v7.widget.RecyclerView;
|
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.android.util.VersionedAdapter;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
|
||||||
@@ -17,8 +21,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
|
|
||||||
public abstract class ThreadItemAdapter<I extends ThreadItem>
|
public class ThreadItemAdapter<I extends ThreadItem>
|
||||||
extends RecyclerView.Adapter<ThreadItemViewHolder<I>>
|
extends RecyclerView.Adapter<BaseThreadItemViewHolder<I>>
|
||||||
implements VersionedAdapter {
|
implements VersionedAdapter {
|
||||||
|
|
||||||
static final int UNDEFINED = -1;
|
static final int UNDEFINED = -1;
|
||||||
@@ -42,7 +46,15 @@ public abstract class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
I item = getVisibleItem(position);
|
||||||
if (item == null) return;
|
if (item == null) return;
|
||||||
listener.onItemVisible(item);
|
listener.onItemVisible(item);
|
||||||
|
|||||||
@@ -1,45 +1,30 @@
|
|||||||
package org.briarproject.android.threaded;
|
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.annotation.UiThread;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
import org.briarproject.android.view.AuthorView;
|
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.util.StringUtils;
|
|
||||||
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
@NotNullByDefault
|
||||||
extends RecyclerView.ViewHolder {
|
public class ThreadItemViewHolder<I extends ThreadItem>
|
||||||
|
extends BaseThreadItemViewHolder<I> {
|
||||||
|
|
||||||
private final static int ANIMATION_DURATION = 5000;
|
private final TextView lvlText, repliesText;
|
||||||
|
|
||||||
private final TextView textView, lvlText, repliesText;
|
|
||||||
private final AuthorView author;
|
|
||||||
private final View[] lvls;
|
private final View[] lvls;
|
||||||
private final View chevron, replyButton;
|
private final View chevron, replyButton;
|
||||||
private final ViewGroup cell;
|
|
||||||
private final View topDivider;
|
|
||||||
|
|
||||||
public ThreadItemViewHolder(View v) {
|
public ThreadItemViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
|
||||||
textView = (TextView) v.findViewById(R.id.text);
|
|
||||||
lvlText = (TextView) v.findViewById(R.id.nested_line_text);
|
lvlText = (TextView) v.findViewById(R.id.nested_line_text);
|
||||||
author = (AuthorView) v.findViewById(R.id.author);
|
|
||||||
repliesText = (TextView) v.findViewById(R.id.replies);
|
repliesText = (TextView) v.findViewById(R.id.replies);
|
||||||
int[] nestedLineIds = {
|
int[] nestedLineIds = {
|
||||||
R.id.nested_line_1, R.id.nested_line_2, R.id.nested_line_3,
|
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);
|
chevron = v.findViewById(R.id.chevron);
|
||||||
replyButton = v.findViewById(R.id.btn_reply);
|
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
|
// TODO improve encapsulation, so we don't need to pass the adapter here
|
||||||
|
@Override
|
||||||
public void bind(final ThreadItemAdapter<I> adapter,
|
public void bind(final ThreadItemAdapter<I> adapter,
|
||||||
final ThreadItemListener<I> listener, final I item, int pos) {
|
final ThreadItemListener<I> listener, final I item, int pos) {
|
||||||
|
super.bind(adapter, listener, item, pos);
|
||||||
textView.setText(StringUtils.trim(item.getText()));
|
|
||||||
|
|
||||||
if (pos == 0) {
|
|
||||||
topDivider.setVisibility(View.INVISIBLE);
|
|
||||||
} else {
|
|
||||||
topDivider.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
@@ -76,9 +53,6 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
|||||||
} else {
|
} else {
|
||||||
lvlText.setVisibility(GONE);
|
lvlText.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
author.setAuthor(item.getAuthor());
|
|
||||||
author.setDate(item.getTimestamp());
|
|
||||||
author.setAuthorStatus(item.getStatus());
|
|
||||||
|
|
||||||
int replies = adapter.getReplyCount(item);
|
int replies = adapter.getReplyCount(item);
|
||||||
if (replies == 0) {
|
if (replies == 0) {
|
||||||
@@ -110,18 +84,6 @@ public abstract class ThreadItemViewHolder<I extends ThreadItem>
|
|||||||
} else {
|
} else {
|
||||||
chevron.setVisibility(INVISIBLE);
|
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() {
|
replyButton.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
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;
|
package org.briarproject.android.threaded;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
@@ -39,6 +40,8 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void onGroupRemoved();
|
void onGroupRemoved();
|
||||||
|
|
||||||
|
Context getApplicationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
|
|
||||||
private volatile GroupId groupId;
|
private volatile GroupId groupId;
|
||||||
|
|
||||||
protected ThreadListListener<H> listener;
|
protected volatile ThreadListListener<H> listener;
|
||||||
|
|
||||||
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||||
@@ -159,7 +159,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
for (H header : headers) {
|
for (H header : headers) {
|
||||||
if (!bodyCache.containsKey(header.getId())) {
|
if (!bodyCache.containsKey(header.getId())) {
|
||||||
bodyCache.put(header.getId(),
|
bodyCache.put(header.getId(),
|
||||||
loadMessageBody(header.getId()));
|
loadMessageBody(header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
duration = System.currentTimeMillis() - now;
|
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;
|
protected abstract Collection<H> loadHeaders() throws DbException;
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
protected abstract String loadMessageBody(MessageId id) throws DbException;
|
protected abstract String loadMessageBody(H header) throws DbException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadItem(final H header,
|
public void loadItem(final H header,
|
||||||
@@ -193,7 +193,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
String body;
|
String body;
|
||||||
if (!bodyCache.containsKey(header.getId())) {
|
if (!bodyCache.containsKey(header.getId())) {
|
||||||
body = loadMessageBody(header.getId());
|
body = loadMessageBody(header);
|
||||||
bodyCache.put(header.getId(), body);
|
bodyCache.put(header.getId(), body);
|
||||||
} else {
|
} else {
|
||||||
body = bodyCache.get(header.getId());
|
body = bodyCache.get(header.getId());
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.briarproject.BuildConfig;
|
|||||||
import org.briarproject.TestUtils;
|
import org.briarproject.TestUtils;
|
||||||
import org.briarproject.android.TestBriarApplication;
|
import org.briarproject.android.TestBriarApplication;
|
||||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
|
import org.briarproject.android.threaded.ThreadItemAdapter;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
@@ -111,7 +112,7 @@ public class ForumActivityTest {
|
|||||||
List<ForumItem> dummyData = getDummyData();
|
List<ForumItem> dummyData = getDummyData();
|
||||||
verify(mc, times(1)).loadItems(rc.capture());
|
verify(mc, times(1)).loadItems(rc.capture());
|
||||||
rc.getValue().onResult(dummyData);
|
rc.getValue().onResult(dummyData);
|
||||||
NestedForumAdapter adapter = forumActivity.getAdapter();
|
ThreadItemAdapter<ForumItem> adapter = forumActivity.getAdapter();
|
||||||
Assert.assertNotNull(adapter);
|
Assert.assertNotNull(adapter);
|
||||||
// Cascade close
|
// Cascade close
|
||||||
assertEquals(6, adapter.getItemCount());
|
assertEquals(6, adapter.getItemCount());
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.android.forum;
|
|||||||
import org.briarproject.android.ActivityModule;
|
import org.briarproject.android.ActivityModule;
|
||||||
import org.briarproject.android.controller.BriarController;
|
import org.briarproject.android.controller.BriarController;
|
||||||
import org.briarproject.android.controller.BriarControllerImpl;
|
import org.briarproject.android.controller.BriarControllerImpl;
|
||||||
|
import org.briarproject.android.threaded.ThreadItemAdapter;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,7 +16,7 @@ public class TestForumActivity extends ForumActivity {
|
|||||||
return forumController;
|
return forumController;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NestedForumAdapter getAdapter() {
|
public ThreadItemAdapter<ForumItem> getAdapter() {
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.briarproject.api.sync.Message;
|
|||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
import org.briarproject.clients.BdfIncomingMessageHook;
|
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -236,10 +235,6 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
|
|||||||
@Override
|
@Override
|
||||||
public String getMessageBody(MessageId m) throws DbException {
|
public String getMessageBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
// TODO remove
|
|
||||||
if (clientHelper.getMessageMetadataAsDictionary(m).getLong(KEY_TYPE) != POST.getInt())
|
|
||||||
return "new member joined";
|
|
||||||
|
|
||||||
// type(0), member_name(1), member_public_key(2), parent_id(3),
|
// type(0), member_name(1), member_public_key(2), parent_id(3),
|
||||||
// previous_message_id(4), content(5), signature(6)
|
// previous_message_id(4), content(5), signature(6)
|
||||||
return clientHelper.getMessageAsList(m).getString(5);
|
return clientHelper.getMessageAsList(m).getString(5);
|
||||||
|
|||||||
Reference in New Issue
Block a user