diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 7e8006f8f..ca4a2e679 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -128,7 +128,17 @@
android:parentActivityName=".android.NavDrawerActivity">
+
+
+
+
diff --git a/briar-android/res/drawable/ic_contact_introduction.xml b/briar-android/res/drawable/ic_contact_introduction.xml
index 9395c7b93..56966678d 100644
--- a/briar-android/res/drawable/ic_contact_introduction.xml
+++ b/briar-android/res/drawable/ic_contact_introduction.xml
@@ -1,5 +1,10 @@
-
-
+
+
diff --git a/briar-android/res/drawable/ic_sharing.xml b/briar-android/res/drawable/ic_sharing.xml
new file mode 100644
index 000000000..8327fd8e5
--- /dev/null
+++ b/briar-android/res/drawable/ic_sharing.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/briar-android/res/layout/list_item_group_member.xml b/briar-android/res/layout/list_item_group_member.xml
new file mode 100644
index 000000000..f4bcebed3
--- /dev/null
+++ b/briar-android/res/layout/list_item_group_member.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/briar-android/res/menu/group_actions.xml b/briar-android/res/menu/group_actions.xml
index bf575e325..d7901bdfa 100644
--- a/briar-android/res/menu/group_actions.xml
+++ b/briar-android/res/menu/group_actions.xml
@@ -11,7 +11,6 @@
diff --git a/briar-android/res/values/attrs.xml b/briar-android/res/values/attrs.xml
index 835338804..d28d0e3f7 100644
--- a/briar-android/res/values/attrs.xml
+++ b/briar-android/res/values/attrs.xml
@@ -11,6 +11,7 @@
+
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 0a7776b09..d2902428d 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -28,21 +28,22 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity;
+import org.briarproject.android.privategroup.conversation.GroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupFragment;
-import org.briarproject.android.privategroup.conversation.GroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment;
-import org.briarproject.android.privategroup.list.GroupListFragment;
import org.briarproject.android.privategroup.invitation.GroupInvitationActivity;
-import org.briarproject.android.sharing.ShareBlogActivity;
+import org.briarproject.android.privategroup.list.GroupListFragment;
+import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
+import org.briarproject.android.sharing.BlogInvitationActivity;
import org.briarproject.android.sharing.BlogSharingStatusActivity;
import org.briarproject.android.sharing.ContactSelectorFragment;
-import org.briarproject.android.sharing.BlogInvitationActivity;
import org.briarproject.android.sharing.ForumInvitationActivity;
+import org.briarproject.android.sharing.ForumSharingStatusActivity;
+import org.briarproject.android.sharing.ShareBlogActivity;
+import org.briarproject.android.sharing.ShareBlogMessageFragment;
import org.briarproject.android.sharing.ShareForumActivity;
import org.briarproject.android.sharing.ShareForumMessageFragment;
-import org.briarproject.android.sharing.ForumSharingStatusActivity;
-import org.briarproject.android.sharing.ShareBlogMessageFragment;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
@@ -80,6 +81,7 @@ public interface ActivityComponent {
void inject(CreateGroupActivity activity);
void inject(GroupActivity activity);
void inject(GroupInvitationActivity activity);
+ void inject(GroupMemberListActivity activity);
void inject(CreateForumActivity activity);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 6550d1752..931b54bac 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -29,6 +29,8 @@ import org.briarproject.android.privategroup.invitation.GroupInvitationControlle
import org.briarproject.android.privategroup.invitation.GroupInvitationControllerImpl;
import org.briarproject.android.privategroup.list.GroupListController;
import org.briarproject.android.privategroup.list.GroupListControllerImpl;
+import org.briarproject.android.privategroup.memberlist.GroupMemberListController;
+import org.briarproject.android.privategroup.memberlist.GroupMemberListControllerImpl;
import org.briarproject.android.sharing.BlogInvitationController;
import org.briarproject.android.sharing.BlogInvitationControllerImpl;
import org.briarproject.android.sharing.ForumInvitationController;
@@ -131,6 +133,13 @@ public class ActivityModule {
return groupInvitationController;
}
+ @ActivityScope
+ @Provides
+ protected GroupMemberListController provideGroupMemberListController(
+ GroupMemberListControllerImpl groupMemberListController) {
+ return groupMemberListController;
+ }
+
@ActivityScope
@Provides
protected ForumController provideForumController(
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
index 5cd52de8d..2a62ebe80 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java
@@ -6,6 +6,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.StringRes;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
@@ -16,6 +18,7 @@ import android.view.MenuItem;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
import org.briarproject.android.threaded.ThreadListActivity;
import org.briarproject.android.threaded.ThreadListController;
import org.briarproject.api.db.DbException;
@@ -24,6 +27,7 @@ import org.briarproject.api.privategroup.PrivateGroup;
import javax.inject.Inject;
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
public class GroupActivity extends
@@ -133,12 +137,20 @@ public class GroupActivity extends
case R.id.action_group_compose_message:
showTextInput(null);
return true;
+ case R.id.action_group_member_list:
+ Intent i = new Intent(this, GroupMemberListActivity.class);
+ i.putExtra(GROUP_ID, groupId.getBytes());
+ i.putExtra(GROUP_NAME, getTitle());
+ ActivityOptionsCompat options =
+ makeCustomAnimation(this, android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right);
+ ActivityCompat.startActivity(this, i, options.toBundle());
+ return true;
case R.id.action_group_leave:
showLeaveGroupDialog();
return true;
case R.id.action_group_dissolve:
showDissolveGroupDialog();
- return true;
default:
return super.onOptionsItemSelected(item);
}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListActivity.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListActivity.java
new file mode 100644
index 000000000..56145d788
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListActivity.java
@@ -0,0 +1,91 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.MenuItem;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.BriarActivity;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.android.view.BriarRecyclerView;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+import javax.inject.Inject;
+
+public class GroupMemberListActivity extends BriarActivity {
+
+ @Inject
+ GroupMemberListController controller;
+
+ private MemberListAdapter adapter;
+ private BriarRecyclerView list;
+ private GroupId groupId;
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onCreate(final Bundle state) {
+ super.onCreate(state);
+
+ setContentView(R.layout.list);
+
+ Intent i = getIntent();
+ byte[] b = i.getByteArrayExtra(GROUP_ID);
+ if (b == null) throw new IllegalStateException("No GroupId in intent.");
+ groupId = new GroupId(b);
+ String name = i.getStringExtra(GROUP_NAME);
+ if (name == null) throw new IllegalStateException("No name in intent.");
+ setTitle(name + " " + getString(R.string.groups_member_list));
+
+ list = (BriarRecyclerView) findViewById(R.id.list);
+ LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
+ list.setLayoutManager(linearLayoutManager);
+ adapter = new MemberListAdapter(this);
+ list.setAdapter(adapter);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ controller.loadMembers(groupId,
+ new UiResultExceptionHandler, DbException>(this) {
+ @Override
+ public void onResultUi(Collection members) {
+ adapter.addAll(members);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO proper error handling
+ finish();
+ }
+ });
+ list.startPeriodicUpdate();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ list.stopPeriodicUpdate();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListController.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListController.java
new file mode 100644
index 000000000..0f657d2d9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListController.java
@@ -0,0 +1,15 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import org.briarproject.android.controller.DbController;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+
+public interface GroupMemberListController extends DbController {
+
+ void loadMembers(GroupId groupId,
+ ResultExceptionHandler, DbException> handler);
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListControllerImpl.java
new file mode 100644
index 000000000..6b317558c
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListControllerImpl.java
@@ -0,0 +1,60 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import org.briarproject.android.controller.DbControllerImpl;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.GroupMember;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+
+public class GroupMemberListControllerImpl extends DbControllerImpl
+ implements GroupMemberListController {
+
+ private static final Logger LOG =
+ Logger.getLogger(GroupMemberListControllerImpl.class.getName());
+
+ private final PrivateGroupManager privateGroupManager;
+
+ @Inject
+ GroupMemberListControllerImpl(@DatabaseExecutor Executor dbExecutor,
+ LifecycleManager lifecycleManager,
+ PrivateGroupManager privateGroupManager) {
+ super(dbExecutor, lifecycleManager);
+ this.privateGroupManager = privateGroupManager;
+ }
+
+ @Override
+ public void loadMembers(final GroupId groupId, final
+ ResultExceptionHandler, DbException> handler) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Collection items = new ArrayList<>();
+ Collection members =
+ privateGroupManager.getMembers(groupId);
+ for (GroupMember m : members) {
+ items.add(new MemberListItem(m));
+ }
+ handler.onResult(items);
+ } catch (DbException e) {
+ if (LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ handler.onException(e);
+ }
+ }
+ });
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListAdapter.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListAdapter.java
new file mode 100644
index 000000000..409e76db1
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListAdapter.java
@@ -0,0 +1,48 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.R;
+import org.briarproject.android.util.BriarAdapter;
+
+class MemberListAdapter extends
+ BriarAdapter {
+
+ MemberListAdapter(Context context) {
+ super(context, MemberListItem.class);
+ }
+
+ @Override
+ public MemberListItemHolder onCreateViewHolder(ViewGroup viewGroup,
+ int i) {
+ View v = LayoutInflater.from(ctx).inflate(
+ R.layout.list_item_group_member, viewGroup, false);
+ return new MemberListItemHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(MemberListItemHolder ui, int position) {
+ ui.bind(items.get(position));
+ }
+
+ @Override
+ public int compare(MemberListItem m1, MemberListItem m2) {
+ return m1.getMember().getName().compareTo(m2.getMember().getName());
+ }
+
+ @Override
+ public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
+ if (m1.isSharing() != m2.isSharing()) return false;
+ if (m1.getStatus() != m2.getStatus()) return false;
+ return true;
+ }
+
+ @Override
+ public boolean areItemsTheSame(MemberListItem m1, MemberListItem m2) {
+ return m1.getMember().equals(m2.getMember());
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java
new file mode 100644
index 000000000..7b5a7cdb5
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java
@@ -0,0 +1,36 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.Author.Status;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.api.privategroup.GroupMember;
+
+import javax.annotation.concurrent.NotThreadSafe;
+
+@NotThreadSafe
+@NotNullByDefault
+public class MemberListItem {
+
+ private final Author member;
+ private Status status;
+ private boolean sharing;
+
+ public MemberListItem(GroupMember groupMember) {
+ this.member = groupMember.getAuthor();
+ this.sharing = groupMember.isShared();
+ this.status = groupMember.getStatus();
+ }
+
+ public Author getMember() {
+ return member;
+ }
+
+ public boolean isSharing() {
+ return sharing;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItemHolder.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItemHolder.java
new file mode 100644
index 000000000..39232faca
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItemHolder.java
@@ -0,0 +1,38 @@
+package org.briarproject.android.privategroup.memberlist;
+
+import android.support.annotation.UiThread;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.briarproject.R;
+import org.briarproject.android.view.AuthorView;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+@UiThread
+@NotNullByDefault
+class MemberListItemHolder extends RecyclerView.ViewHolder {
+
+ private final AuthorView author;
+ private final ImageView sharing;
+
+ MemberListItemHolder(View v) {
+ super(v);
+ author = (AuthorView) v.findViewById(R.id.authorView);
+ sharing = (ImageView) v.findViewById(R.id.sharingView);
+ }
+
+ protected void bind(MemberListItem item) {
+ author.setAuthor(item.getMember());
+ author.setAuthorStatus(item.getStatus());
+ if (item.isSharing()) {
+ sharing.setVisibility(VISIBLE);
+ } else {
+ sharing.setVisibility(INVISIBLE);
+ }
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/view/AuthorView.java b/briar-android/src/org/briarproject/android/view/AuthorView.java
index 2a1e928cd..b31472aca 100644
--- a/briar-android/src/org/briarproject/android/view/AuthorView.java
+++ b/briar-android/src/org/briarproject/android/view/AuthorView.java
@@ -12,7 +12,6 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -136,7 +135,7 @@ public class AuthorView extends RelativeLayout {
break;
// commenter
case 2:
- ViewGroup.LayoutParams params = avatar.getLayoutParams();
+ LayoutParams params = (LayoutParams) avatar.getLayoutParams();
int size = getResources().getDimensionPixelSize(
R.dimen.blogs_avatar_comment_size);
params.height = size;
@@ -146,6 +145,25 @@ public class AuthorView extends RelativeLayout {
.getDimensionPixelSize(R.dimen.text_size_tiny);
authorName.setTextSize(COMPLEX_UNIT_PX, textSize);
break;
+ // list
+ case 3:
+ date.setVisibility(GONE);
+ params = (LayoutParams) avatar.getLayoutParams();
+ size = getResources().getDimensionPixelSize(
+ R.dimen.listitem_picture_size_small);
+ params.height = size;
+ params.width = size;
+ avatar.setLayoutParams(params);
+ textSize = getResources()
+ .getDimensionPixelSize(R.dimen.text_size_medium);
+ authorName.setTextSize(COMPLEX_UNIT_PX, textSize);
+ params = (LayoutParams) authorName.getLayoutParams();
+ params.addRule(CENTER_VERTICAL);
+ authorName.setLayoutParams(params);
+ params = (LayoutParams) trustIndicator.getLayoutParams();
+ params.addRule(CENTER_VERTICAL);
+ trustIndicator.setLayoutParams(params);
+ break;
}
}