From 59964c50872f938416bbb7279fe0155c046977c1 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Fri, 11 Nov 2016 14:51:42 -0200 Subject: [PATCH] Add UI for revealing contacts within a private group --- briar-android/AndroidManifest.xml | 11 ++ briar-android/res/drawable/ic_visibility.xml | 10 ++ .../res/drawable/ic_visibility_off.xml | 10 ++ .../res/drawable/ic_visibility_white.xml | 9 ++ .../res/layout/activity_reveal_contacts.xml | 25 ++++ .../layout/list_item_revealable_contact.xml | 76 +++++++++++ briar-android/res/menu/group_actions.xml | 6 + briar-android/res/values/strings.xml | 7 + .../android/ActivityComponent.java | 6 + .../briarproject/android/ActivityModule.java | 9 ++ .../conversation/GroupActivity.java | 19 ++- .../memberlist/MemberListItem.java | 2 +- .../reveal/RevealContactsActivity.java | 100 ++++++++++++++ .../reveal/RevealContactsController.java | 19 +++ .../reveal/RevealContactsControllerImpl.java | 122 ++++++++++++++++++ .../reveal/RevealContactsFragment.java | 70 ++++++++++ .../reveal/RevealableContactAdapter.java | 43 ++++++ .../reveal/RevealableContactItem.java | 26 ++++ .../reveal/RevealableContactViewHolder.java | 64 +++++++++ .../android/sharing/ShareActivity.java | 1 - 20 files changed, 628 insertions(+), 7 deletions(-) create mode 100644 briar-android/res/drawable/ic_visibility.xml create mode 100644 briar-android/res/drawable/ic_visibility_off.xml create mode 100644 briar-android/res/drawable/ic_visibility_white.xml create mode 100644 briar-android/res/layout/activity_reveal_contacts.xml create mode 100644 briar-android/res/layout/list_item_revealable_contact.xml create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsActivity.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsController.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsControllerImpl.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsFragment.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactAdapter.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactItem.java create mode 100644 briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index f5e81a2d45..16189e3751 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -142,6 +142,17 @@ /> </activity> + <activity + android:name=".android.privategroup.reveal.RevealContactsActivity" + android:label="@string/groups_reveal_contacts" + android:parentActivityName=".android.privategroup.conversation.GroupActivity" + android:windowSoftInputMode="adjustResize|stateAlwaysHidden"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".android.privategroup.conversation.GroupActivity" + /> + </activity> + <activity android:name=".android.privategroup.creation.GroupInviteActivity" android:label="@string/groups_invite_members" diff --git a/briar-android/res/drawable/ic_visibility.xml b/briar-android/res/drawable/ic_visibility.xml new file mode 100644 index 0000000000..146e495349 --- /dev/null +++ b/briar-android/res/drawable/ic_visibility.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="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/> +</vector> diff --git a/briar-android/res/drawable/ic_visibility_off.xml b/briar-android/res/drawable/ic_visibility_off.xml new file mode 100644 index 0000000000..80bea183c3 --- /dev/null +++ b/briar-android/res/drawable/ic_visibility_off.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:alpha="0.53" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/> +</vector> diff --git a/briar-android/res/drawable/ic_visibility_white.xml b/briar-android/res/drawable/ic_visibility_white.xml new file mode 100644 index 0000000000..c64e5d7a12 --- /dev/null +++ b/briar-android/res/drawable/ic_visibility_white.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/> +</vector> diff --git a/briar-android/res/layout/activity_reveal_contacts.xml b/briar-android/res/layout/activity_reveal_contacts.xml new file mode 100644 index 0000000000..4faa8c869c --- /dev/null +++ b/briar-android/res/layout/activity_reveal_contacts.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + tools:context=".android.privategroup.reveal.RevealContactsActivity"> + + <FrameLayout + android:id="@+id/fragmentContainer" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + + <Button + android:id="@+id/revealButton" + style="@style/BriarButton" + android:layout_marginEnd="@dimen/margin_small" + android:layout_marginLeft="@dimen/margin_small" + android:layout_marginRight="@dimen/margin_small" + android:layout_marginStart="@dimen/margin_small" + android:text="@string/groups_reveal_contacts"/> + +</LinearLayout> diff --git a/briar-android/res/layout/list_item_revealable_contact.xml b/briar-android/res/layout/list_item_revealable_contact.xml new file mode 100644 index 0000000000..c9606b5ecb --- /dev/null +++ b/briar-android/res/layout/list_item_revealable_contact.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + + <RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="vertical" + android:padding="@dimen/listitem_horizontal_margin"> + + <de.hdodenhof.circleimageview.CircleImageView + android:id="@+id/avatarView" + style="@style/BriarAvatar" + android:layout_width="@dimen/listitem_selectable_picture_size" + android:layout_height="@dimen/listitem_selectable_picture_size" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_marginEnd="@dimen/listitem_horizontal_margin" + android:layout_marginRight="@dimen/listitem_horizontal_margin" + android:transitionName="avatar" + tools:src="@drawable/ic_launcher"/> + + <org.thoughtcrime.securesms.components.emoji.EmojiTextView + android:id="@+id/nameView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@+id/avatarView" + android:textColor="@color/briar_text_primary" + android:textSize="@dimen/text_size_large" + tools:text="Revealable Contact"/> + + <ImageView + android:id="@+id/visibilityView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/nameView" + android:layout_marginRight="@dimen/margin_small" + android:layout_toRightOf="@+id/avatarView" + android:src="@drawable/ic_visibility" + tools:ignore="ContentDescription"/> + + <TextView + android:id="@+id/infoView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@+id/nameView" + android:layout_toLeftOf="@+id/checkBox" + android:layout_toRightOf="@+id/visibilityView" + android:gravity="center_vertical" + android:text="@string/groups_reveal_visible" + android:textColor="@color/briar_text_tertiary" + android:textSize="@dimen/text_size_small" + tools:visibility="visible"/> + + <CheckBox + android:id="@+id/checkBox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:clickable="false"/> + + </RelativeLayout> + + <View style="@style/Divider.ContactList"/> + +</LinearLayout> diff --git a/briar-android/res/menu/group_actions.xml b/briar-android/res/menu/group_actions.xml index 0c1fd68fa7..a67e732998 100644 --- a/briar-android/res/menu/group_actions.xml +++ b/briar-android/res/menu/group_actions.xml @@ -15,6 +15,12 @@ android:title="@string/groups_member_list" app:showAsAction="ifRoom"/> + <item + android:id="@+id/action_group_reveal" + android:icon="@drawable/ic_visibility_white" + android:title="@string/groups_reveal_contacts" + app:showAsAction="ifRoom"/> + <item android:id="@+id/action_group_invite" android:icon="@drawable/ic_add_white" diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 368fc5664f..0c5274d171 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -194,6 +194,13 @@ <string name="groups_invitations_response_accepted_received">%s accepted the group invitation.</string> <string name="groups_invitations_response_declined_received">%s declined the group invitation.</string> + <!-- Private Groups Revealing Contacts --> + <string name="groups_reveal_contacts">Reveal Contacts</string> + <string name="groups_reveal_visible">Your relationship is visible to the group</string> + <string name="groups_reveal_visible_revealed_by_us">Your relationship is visible to the group (revealed by you)</string> + <string name="groups_reveal_visible_revealed_by_contact">Your relationship is visible to the group (revealed by contact)</string> + <string name="groups_reveal_invisible">Your relationship is not visible to the group</string> + <!-- Forums --> <string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string> <string name="create_forum_title">New Forum</string> diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 6db54c450a..a8b3dee8b9 100644 --- a/briar-android/src/org/briarproject/android/ActivityComponent.java +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -37,6 +37,8 @@ import org.briarproject.android.privategroup.creation.GroupInviteFragment; import org.briarproject.android.privategroup.invitation.GroupInvitationActivity; import org.briarproject.android.privategroup.list.GroupListFragment; import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity; +import org.briarproject.android.privategroup.reveal.RevealContactsActivity; +import org.briarproject.android.privategroup.reveal.RevealContactsFragment; import org.briarproject.android.sharing.BlogInvitationActivity; import org.briarproject.android.sharing.BlogSharingStatusActivity; import org.briarproject.android.sharing.ForumInvitationActivity; @@ -91,6 +93,8 @@ public interface ActivityComponent { void inject(GroupMemberListActivity activity); + void inject(RevealContactsActivity activity); + void inject(CreateForumActivity activity); void inject(ShareForumActivity activity); @@ -146,6 +150,8 @@ public interface ActivityComponent { void inject(GroupInviteFragment fragment); + void inject(RevealContactsFragment activity); + void inject(ForumListFragment fragment); void inject(FeedFragment fragment); diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java index 1c2db12176..4f17c24bc6 100644 --- a/briar-android/src/org/briarproject/android/ActivityModule.java +++ b/briar-android/src/org/briarproject/android/ActivityModule.java @@ -31,6 +31,8 @@ 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.privategroup.reveal.RevealContactsController; +import org.briarproject.android.privategroup.reveal.RevealContactsControllerImpl; import org.briarproject.android.sharing.BlogInvitationController; import org.briarproject.android.sharing.BlogInvitationControllerImpl; import org.briarproject.android.sharing.ForumInvitationController; @@ -144,6 +146,13 @@ public class ActivityModule { return groupMemberListController; } + @ActivityScope + @Provides + protected RevealContactsController provideRevealContactsController( + RevealContactsControllerImpl revealContactsController) { + return revealContactsController; + } + @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 24dba86141..8a24aaba65 100644 --- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java +++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupActivity.java @@ -22,6 +22,7 @@ import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.privategroup.conversation.GroupController.GroupListener; import org.briarproject.android.privategroup.creation.GroupInviteActivity; import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity; +import org.briarproject.android.privategroup.reveal.RevealContactsActivity; import org.briarproject.android.threaded.ThreadListActivity; import org.briarproject.android.threaded.ThreadListController; import org.briarproject.api.db.DbException; @@ -48,8 +49,8 @@ public class GroupActivity extends GroupController controller; private boolean isCreator, isDissolved = false; - private MenuItem writeMenuItem, inviteMenuItem, leaveMenuItem, - dissolveMenuItem; + private MenuItem writeMenuItem, revealMenuItem, inviteMenuItem, + leaveMenuItem, dissolveMenuItem; @Override public void injectActivity(ActivityComponent component) { @@ -135,6 +136,7 @@ public class GroupActivity extends inflater.inflate(R.menu.group_actions, menu); writeMenuItem = menu.findItem(R.id.action_group_compose_message); + revealMenuItem = menu.findItem(R.id.action_group_reveal); inviteMenuItem = menu.findItem(R.id.action_group_invite); leaveMenuItem = menu.findItem(R.id.action_group_leave); dissolveMenuItem = menu.findItem(R.id.action_group_dissolve); @@ -157,10 +159,15 @@ public class GroupActivity extends i1.putExtra(GROUP_ID, groupId.getBytes()); ActivityCompat.startActivity(this, i1, options.toBundle()); return true; - case R.id.action_group_invite: - Intent i2 = new Intent(this, GroupInviteActivity.class); + case R.id.action_group_reveal: + Intent i2 = new Intent(this, RevealContactsActivity.class); i2.putExtra(GROUP_ID, groupId.getBytes()); - ActivityCompat.startActivityForResult(this, i2, REQUEST_INVITE, + ActivityCompat.startActivity(this, i2, options.toBundle()); + return true; + case R.id.action_group_invite: + Intent i3 = new Intent(this, GroupInviteActivity.class); + i3.putExtra(GROUP_ID, groupId.getBytes()); + ActivityCompat.startActivityForResult(this, i3, REQUEST_INVITE, options.toBundle()); return true; case R.id.action_group_leave: @@ -218,10 +225,12 @@ public class GroupActivity extends private void showMenuItems() { if (leaveMenuItem == null || dissolveMenuItem == null) return; if (isCreator) { + revealMenuItem.setVisible(false); inviteMenuItem.setVisible(true); leaveMenuItem.setVisible(false); dissolveMenuItem.setVisible(true); } else { + revealMenuItem.setVisible(true); inviteMenuItem.setVisible(false); leaveMenuItem.setVisible(true); dissolveMenuItem.setVisible(false); diff --git a/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java index 9ff87a510b..469bdf6bd4 100644 --- a/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java +++ b/briar-android/src/org/briarproject/android/privategroup/memberlist/MemberListItem.java @@ -19,7 +19,7 @@ class MemberListItem { public MemberListItem(GroupMember groupMember) { this.member = groupMember.getAuthor(); - this.sharing = groupMember.getVisibility() != INVISIBLE; // TODO #732 + this.sharing = groupMember.getVisibility() != INVISIBLE; this.status = groupMember.getStatus(); } diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsActivity.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsActivity.java new file mode 100644 index 0000000000..f76d514fd7 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsActivity.java @@ -0,0 +1,100 @@ +package org.briarproject.android.privategroup.reveal; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + +import org.briarproject.R; +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.contactselection.ContactSelectorActivity; +import org.briarproject.android.controller.handler.UiExceptionHandler; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +import javax.inject.Inject; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RevealContactsActivity extends ContactSelectorActivity + implements OnClickListener { + + private Button button; + + @Inject + RevealContactsController controller; + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + @SuppressWarnings("ConstantConditions") + public void onCreate(@Nullable Bundle bundle) { + super.onCreate(bundle); + + Intent i = getIntent(); + byte[] b = i.getByteArrayExtra(GROUP_ID); + if (b == null) throw new IllegalStateException("No GroupId"); + groupId = new GroupId(b); + + button = (Button) findViewById(R.id.revealButton); + button.setOnClickListener(this); + button.setEnabled(false); + + if (bundle == null) { + RevealContactsFragment fragment = + RevealContactsFragment.newInstance(groupId); + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragmentContainer, fragment) + .commit(); + } + } + + @Override + @LayoutRes + protected int getLayout() { + return R.layout.activity_reveal_contacts; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void contactsSelected(Collection<ContactId> contacts) { + super.contactsSelected(contacts); + button.setEnabled(!contacts.isEmpty()); + } + + @Override + public void onClick(View v) { + controller.reveal(groupId, contacts, + new UiExceptionHandler<DbException>(this) { + @Override + public void onExceptionUi(DbException exception) { + // TODO proper error handling + finish(); + } + }); + supportFinishAfterTransition(); + } + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsController.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsController.java new file mode 100644 index 0000000000..9c14e20a06 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsController.java @@ -0,0 +1,19 @@ +package org.briarproject.android.privategroup.reveal; + +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.controller.handler.ExceptionHandler; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +@NotNullByDefault +public interface RevealContactsController + extends ContactSelectorController<RevealableContactItem> { + + void reveal(GroupId g, Collection<ContactId> contacts, + ExceptionHandler<DbException> handler); + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsControllerImpl.java new file mode 100644 index 0000000000..40ddff92d0 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsControllerImpl.java @@ -0,0 +1,122 @@ +package org.briarproject.android.privategroup.reveal; + +import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.controller.handler.ExceptionHandler; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.clients.ProtocolStateException; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.db.DbException; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.privategroup.GroupMember; +import org.briarproject.api.privategroup.PrivateGroupManager; +import org.briarproject.api.privategroup.invitation.GroupInvitationManager; +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.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; +import static org.briarproject.api.privategroup.Visibility.INVISIBLE; + +@Immutable +@NotNullByDefault +public class RevealContactsControllerImpl extends DbControllerImpl + implements RevealContactsController { + + private static final Logger LOG = + Logger.getLogger(RevealContactsControllerImpl.class.getName()); + + private final PrivateGroupManager groupManager; + private final GroupInvitationManager groupInvitationManager; + private final ContactManager contactManager; + + @Inject + public RevealContactsControllerImpl(@DatabaseExecutor Executor dbExecutor, + LifecycleManager lifecycleManager, PrivateGroupManager groupManager, + GroupInvitationManager groupInvitationManager, + ContactManager contactManager) { + super(dbExecutor, lifecycleManager); + this.groupManager = groupManager; + this.groupInvitationManager = groupInvitationManager; + this.contactManager = contactManager; + } + + @Override + public void loadContacts(final GroupId g, + final Collection<ContactId> selection, + final ResultExceptionHandler<Collection<RevealableContactItem>, DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection<RevealableContactItem> items = + getItems(g, selection); + handler.onResult(items); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + @DatabaseExecutor + private Collection<RevealableContactItem> getItems(GroupId g, + Collection<ContactId> selection) throws DbException { + Collection<GroupMember> members = + groupManager.getMembers(g); + Collection<Contact> contacts = + contactManager.getActiveContacts(); + Collection<RevealableContactItem> items = + new ArrayList<>(members.size()); + for (GroupMember m : members) { + for (Contact c : contacts) { + if (m.getAuthor().equals(c.getAuthor())) { + boolean disabled = m.getVisibility() != INVISIBLE; + boolean selected = + disabled || selection.contains(c.getId()); + items.add(new RevealableContactItem(c, selected, disabled, + m.getVisibility())); + } + + } + } + return items; + } + + @Override + public void reveal(final GroupId g, final Collection<ContactId> contacts, + final ExceptionHandler<DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + for (ContactId c : contacts) { + try { + groupInvitationManager.revealRelationship(c, g); + } catch (ProtocolStateException e) { + // action is outdated, move to next contact + if (LOG.isLoggable(INFO)) + LOG.log(INFO, e.toString(), e); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + break; + } + } + } + }); + } + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsFragment.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsFragment.java new file mode 100644 index 0000000000..df34de80ff --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealContactsFragment.java @@ -0,0 +1,70 @@ +package org.briarproject.android.privategroup.reveal; + +import android.content.Context; +import android.os.Bundle; + +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener; +import org.briarproject.android.contactselection.BaseContactSelectorFragment; +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.nullsafety.MethodsNotNullByDefault; +import org.briarproject.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +import javax.inject.Inject; + +import static org.briarproject.android.BriarActivity.GROUP_ID; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class RevealContactsFragment extends + BaseContactSelectorFragment<RevealableContactItem, RevealableContactAdapter> { + + private final static String TAG = RevealContactsFragment.class.getName(); + + @Inject + RevealContactsController controller; + + public static RevealContactsFragment newInstance(GroupId groupId) { + Bundle args = new Bundle(); + args.putByteArray(GROUP_ID, groupId.getBytes()); + RevealContactsFragment fragment = new RevealContactsFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + protected ContactSelectorController<RevealableContactItem> getController() { + return controller; + } + + @Override + protected RevealableContactAdapter getAdapter(Context context, + OnContactClickListener<RevealableContactItem> listener) { + return new RevealableContactAdapter(context, listener); + } + + @Override + protected void onSelectionChanged() { + Collection<ContactId> selected = adapter.getSelectedContactIds(); + Collection<ContactId> disabled = adapter.getDisabledContactIds(); + selected.removeAll(disabled); + + // tell the activity which contacts have been selected + listener.contactsSelected(selected); + } + + @Override + public String getUniqueTag() { + return TAG; + } + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactAdapter.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactAdapter.java new file mode 100644 index 0000000000..c84303e788 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactAdapter.java @@ -0,0 +1,43 @@ +package org.briarproject.android.privategroup.reveal; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.briarproject.R; +import org.briarproject.android.contactselection.BaseContactSelectorAdapter; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.nullsafety.NotNullByDefault; + +import java.util.ArrayList; +import java.util.Collection; + +@NotNullByDefault +class RevealableContactAdapter extends + BaseContactSelectorAdapter<RevealableContactItem, RevealableContactViewHolder> { + + RevealableContactAdapter(Context context, + OnContactClickListener<RevealableContactItem> listener) { + super(context, RevealableContactItem.class, listener); + } + + @Override + public RevealableContactViewHolder onCreateViewHolder(ViewGroup viewGroup, + int i) { + View v = LayoutInflater.from(ctx).inflate( + R.layout.list_item_revealable_contact, viewGroup, false); + return new RevealableContactViewHolder(v); + } + + Collection<ContactId> getDisabledContactIds() { + Collection<ContactId> disabled = new ArrayList<>(); + + for (int i = 0; i < items.size(); i++) { + RevealableContactItem item = items.get(i); + if (item.isDisabled()) disabled.add(item.getContact().getId()); + } + return disabled; + } + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactItem.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactItem.java new file mode 100644 index 0000000000..493fbda2e5 --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactItem.java @@ -0,0 +1,26 @@ +package org.briarproject.android.privategroup.reveal; + +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.privategroup.Visibility; + +import javax.annotation.concurrent.NotThreadSafe; + +@NotThreadSafe +@NotNullByDefault +public class RevealableContactItem extends SelectableContactItem { + + private final Visibility visibility; + + public RevealableContactItem(Contact contact, boolean selected, + boolean disabled, Visibility visibility) { + super(contact, selected, disabled); + this.visibility = visibility; + } + + public Visibility getVisibility() { + return visibility; + } + +} diff --git a/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java new file mode 100644 index 0000000000..f2131482cb --- /dev/null +++ b/briar-android/src/org/briarproject/android/privategroup/reveal/RevealableContactViewHolder.java @@ -0,0 +1,64 @@ +package org.briarproject.android.privategroup.reveal; + +import android.support.annotation.UiThread; +import android.view.View; +import android.widget.ImageView; + +import org.briarproject.R; +import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener; +import org.briarproject.android.contactselection.BaseSelectableContactHolder; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.jetbrains.annotations.Nullable; + +import static org.briarproject.android.util.AndroidUtils.GREY_OUT; +import static org.briarproject.api.privategroup.Visibility.INVISIBLE; + +@UiThread +@NotNullByDefault +public class RevealableContactViewHolder + extends BaseSelectableContactHolder<RevealableContactItem> { + + private final ImageView icon; + + RevealableContactViewHolder(View v) { + super(v); + + icon = (ImageView) v.findViewById(R.id.visibilityView); + } + + @Override + protected void bind(RevealableContactItem item, @Nullable + OnContactClickListener<RevealableContactItem> listener) { + super.bind(item, listener); + + switch (item.getVisibility()) { + case VISIBLE: + info.setText(R.string.groups_reveal_visible); + break; + case REVEALED_BY_US: + info.setText(R.string.groups_reveal_visible_revealed_by_us); + break; + case REVEALED_BY_CONTACT: + info.setText( + R.string.groups_reveal_visible_revealed_by_contact); + break; + case INVISIBLE: + info.setText(R.string.groups_reveal_invisible); + break; + } + + if (item.getVisibility() == INVISIBLE) { + icon.setImageResource(R.drawable.ic_visibility_off); + } else { + icon.setImageResource(R.drawable.ic_visibility); + } + } + + @Override + protected void grayOutItem(boolean gray) { + super.grayOutItem(gray); + float alpha = gray ? GREY_OUT : 1f; + icon.setAlpha(alpha); + } + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java index 8940714226..8924d5300a 100644 --- a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java @@ -7,7 +7,6 @@ import android.support.annotation.UiThread; import org.briarproject.R; import org.briarproject.android.contactselection.ContactSelectorActivity; import org.briarproject.android.contactselection.ContactSelectorFragment; -import org.briarproject.android.contactselection.SelectableContactItem; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; import org.briarproject.api.contact.ContactId; import org.briarproject.api.nullsafety.MethodsNotNullByDefault; -- GitLab