diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 7e8006f8f049ac505841726b175497c48c479090..99ad3cfada95848bac44c53f9f195fdc71032623 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -128,7 +128,17 @@ android:parentActivityName=".android.NavDrawerActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" - android:value=".android.NavDrawerActivity" + android:value=".android.NavDrawerActivity"/> + </activity> + + <activity + android:name=".android.privategroup.memberlist.GroupMemberListActivity" + android:label="@string/groups_member_list" + android:parentActivityName=".android.privategroup.conversation.GroupActivity" + android:windowSoftInputMode="adjustResize|stateHidden"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".android.privategroup.conversation.GroupActivity" /> </activity> diff --git a/briar-android/res/drawable/ic_contact_introduction.xml b/briar-android/res/drawable/ic_contact_introduction.xml index 9395c7b93e79675f0ab31d05363b7aff5b91cfd1..56966678d412e370ea9b34e427295a7146bca84a 100644 --- a/briar-android/res/drawable/ic_contact_introduction.xml +++ b/briar-android/res/drawable/ic_contact_introduction.xml @@ -1,5 +1,10 @@ -<vector android:alpha="0.56" android:height="48dp" - android:viewportHeight="24.0" android:viewportWidth="24.0" - android:width="48dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="#FF000000" android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:alpha="0.54" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M9.01,14L2,14v2h7.01v3L13,15l-3.99,-4v3zM14.99,13v-3L22,10L22,8h-7.01L14.99,5L11,9l3.99,4z"/> </vector> diff --git a/briar-android/res/drawable/ic_sharing.xml b/briar-android/res/drawable/ic_sharing.xml new file mode 100644 index 0000000000000000000000000000000000000000..8327fd8e5cd3986ad29bcaddd037cb3c6acb4023 --- /dev/null +++ b/briar-android/res/drawable/ic_sharing.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:alpha="0.54" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"/> +</vector> 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 0000000000000000000000000000000000000000..f4bcebed3150c3b44558489e6285fe03d1bfb1d1 --- /dev/null +++ b/briar-android/res/layout/list_item_group_member.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/listitem_horizontal_margin" + android:layout_marginLeft="@dimen/listitem_horizontal_margin" + android:layout_marginRight="@dimen/listitem_horizontal_margin" + android:layout_marginStart="@dimen/listitem_horizontal_margin" + android:orientation="horizontal" + android:paddingBottom="@dimen/margin_medium" + android:paddingTop="@dimen/margin_medium"> + + <org.briarproject.android.view.AuthorView + android:id="@+id/authorView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:persona="list"/> + + <ImageView + android:id="@+id/sharingView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_sharing" + android:layout_marginLeft="@dimen/margin_medium" + android:contentDescription="@string/forum_invitation_already_sharing"/> + +</LinearLayout> diff --git a/briar-android/res/menu/group_actions.xml b/briar-android/res/menu/group_actions.xml index bf575e3255e7356bd6819277aaeef95709ec4f4a..d7901bdfa01f01fa4a172bbfe71af68006350664 100644 --- a/briar-android/res/menu/group_actions.xml +++ b/briar-android/res/menu/group_actions.xml @@ -11,7 +11,6 @@ <item android:id="@+id/action_group_member_list" - android:enabled="false" android:icon="@drawable/ic_group_white" android:title="@string/groups_member_list" app:showAsAction="ifRoom"/> diff --git a/briar-android/res/values/attrs.xml b/briar-android/res/values/attrs.xml index 83533880478a3ed4af9245804a72596f3c6f1911..d28d0e3f730ce8c32ec29770c58afb915a08fc12 100644 --- a/briar-android/res/values/attrs.xml +++ b/briar-android/res/values/attrs.xml @@ -11,6 +11,7 @@ <enum name="normal" value="0"/> <enum name="reblogger" value="1"/> <enum name="commenter" value="2"/> + <enum name="list" value="3"/> </attr> </declare-styleable> diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 0a7776b09e0b06731f864f51c48d138b606d1987..d2902428d660773b153a557f07cb303113046d0b 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.ShareForumActivity; -import org.briarproject.android.sharing.ShareForumMessageFragment; 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.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 6550d1752b6b05fbd484da2f97d506bfd332fb91..931b54bac16bbcd07a03430949831e2d0c11ef9f 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 5cd52de8d34ec7b07a1fc83696a22e482947a69c..a6a8066047e6696cc355b62682b05a447751593a 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,19 @@ 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()); + 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 0000000000000000000000000000000000000000..a3523f85596c8d90bfd44af4f4bbf3d425dbb3f6 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/GroupMemberListActivity.java @@ -0,0 +1,88 @@ +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); + + 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<Collection<MemberListItem>, DbException>(this) { + @Override + public void onResultUi(Collection<MemberListItem> 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 0000000000000000000000000000000000000000..0f657d2d9c634f73f8d26afc2d19bec239989390 --- /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<Collection<MemberListItem>, 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 0000000000000000000000000000000000000000..6b317558c432f175ec95c1d498a088e749cbff7e --- /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<Collection<MemberListItem>, DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection<MemberListItem> items = new ArrayList<>(); + Collection<GroupMember> 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 0000000000000000000000000000000000000000..409e76db1cb81bffce3ce014cb5b2dc1cf01be27 --- /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<MemberListItem, MemberListItemHolder> { + + 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 0000000000000000000000000000000000000000..ef0c73d91f2e1a5cacccea458d6f1bbe0a44c92f --- /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.Immutable; + +@Immutable +@NotNullByDefault +class MemberListItem { + + private final Author member; + private final Status status; + private final 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 0000000000000000000000000000000000000000..39232faca751456166100741d012d9a9a9c2e2d4 --- /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 2a1e928cdaad71420f364122fcc3e7d43449b6f2..b31472acad9bc231fd8ae0f86ee553693a7c5ee9 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; } }