mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Merge branch '707-implement-ux-for-showing-and-answering-private-group-invitations' into 'master'
Implement UX for showing and answering private group invitations As usual, this MR contains several logically separate commits that could be split out into smaller MRs if desired. It consists of two main parts: * Showing open invitations in the list of private groups with a snackbar * Showing invitations and responses in the private conversation For both parts, the existing code was refactored to allow for a smooth implementation and to leave maintainable code behind.    Closes #707 See merge request !357
This commit is contained in:
@@ -547,7 +547,7 @@ public class BlogSharingIntegrationTest extends BriarIntegrationTest {
|
||||
BlogInvitationReceivedEvent event =
|
||||
(BlogInvitationReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId1, event.getContactId());
|
||||
Blog b = event.getBlog();
|
||||
Blog b = event.getShareable();
|
||||
try {
|
||||
Contact c = contactManager0.getContact(contactId1);
|
||||
blogSharingManager0.respondToInvitation(b, c, true);
|
||||
@@ -589,7 +589,7 @@ public class BlogSharingIntegrationTest extends BriarIntegrationTest {
|
||||
(BlogInvitationReceivedEvent) e;
|
||||
requestReceived = true;
|
||||
if (!answer) return;
|
||||
Blog b = event.getBlog();
|
||||
Blog b = event.getShareable();
|
||||
try {
|
||||
eventWaiter.assertEquals(1,
|
||||
blogSharingManager1.getInvitations().size());
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
@@ -762,7 +762,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
"Sharer2 to Invitee");
|
||||
|
||||
// make sure we now have two invitations to the same forum available
|
||||
Collection<InvitationItem> forums =
|
||||
Collection<SharingInvitationItem> forums =
|
||||
forumSharingManager1.getInvitations();
|
||||
assertEquals(1, forums.size());
|
||||
assertEquals(2, forums.iterator().next().getNewSharers().size());
|
||||
@@ -939,7 +939,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
(ForumInvitationReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId1, event.getContactId());
|
||||
requestReceived = true;
|
||||
Forum f = event.getForum();
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
Contact c = contactManager0.getContact(contactId1);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
@@ -982,11 +982,11 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
(ForumInvitationReceivedEvent) e;
|
||||
requestReceived = true;
|
||||
if (!answer) return;
|
||||
Forum f = event.getForum();
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
InvitationItem invitation =
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
|
||||
@@ -123,7 +123,17 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.InvitationsForumActivity"
|
||||
android:name=".android.privategroup.invitation.GroupInvitationActivity"
|
||||
android:label="@string/groups_invitations_title"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.ForumInvitationActivity"
|
||||
android:label="@string/forum_invitations_title"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
@@ -133,7 +143,7 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.InvitationsBlogActivity"
|
||||
android:name=".android.sharing.BlogInvitationActivity"
|
||||
android:label="@string/blogs_sharing_invitations_title"
|
||||
android:parentActivityName=".android.contact.ConversationActivity">
|
||||
<meta-data
|
||||
@@ -187,7 +197,7 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.SharingStatusForumActivity"
|
||||
android:name=".android.sharing.ForumSharingStatusActivity"
|
||||
android:label="@string/sharing_status"
|
||||
android:parentActivityName=".android.forum.ForumActivity">
|
||||
<meta-data
|
||||
@@ -197,7 +207,7 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.SharingStatusBlogActivity"
|
||||
android:name=".android.sharing.BlogSharingStatusActivity"
|
||||
android:label="@string/sharing_status"
|
||||
android:parentActivityName=".android.blogs.BlogActivity">
|
||||
<meta-data
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
android:id="@+id/msgLayout"
|
||||
android:id="@+id/layout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -11,7 +11,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/msgBody"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/briar_text_primary"
|
||||
@@ -20,7 +20,7 @@
|
||||
tools:text="Short message"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgTime"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|end"
|
||||
@@ -7,7 +7,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/msgLayout"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|end"
|
||||
@@ -16,7 +16,7 @@
|
||||
android:background="@drawable/msg_out">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/msgBody"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/briar_text_primary_inverse"
|
||||
@@ -25,12 +25,12 @@
|
||||
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/msgTime"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/msgBody"
|
||||
android:layout_below="@+id/text"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/private_message_date_inverse"
|
||||
@@ -38,13 +38,13 @@
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/msgStatus"
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/msgTime"
|
||||
android:layout_alignBottom="@+id/time"
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
android:layout_toEndOf="@+id/msgTime"
|
||||
android:layout_toRightOf="@+id/msgTime"
|
||||
android:layout_toEndOf="@+id/time"
|
||||
android:layout_toRightOf="@+id/time"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/message_delivered_white"/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/msgBody"
|
||||
android:id="@+id/msgText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left|start"
|
||||
@@ -20,7 +20,7 @@
|
||||
tools:text="Short message"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
@@ -28,7 +28,7 @@
|
||||
android:background="@drawable/notice_in_bottom">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/introductionText"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="80dp"
|
||||
@@ -39,28 +39,17 @@
|
||||
tools:text="@string/forum_invitation_received"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionTime"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/showInvitationsButton"
|
||||
android:layout_alignEnd="@+id/text"
|
||||
android:layout_alignRight="@+id/text"
|
||||
android:layout_below="@+id/text"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/showInvitationsButton"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_marginBottom="-15dp"
|
||||
tools:text="@string/forum_show_invitations"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -7,7 +7,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/msgBody"
|
||||
android:id="@+id/msgText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
|
||||
@@ -19,7 +19,7 @@
|
||||
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
|
||||
@@ -27,7 +27,7 @@
|
||||
android:background="@drawable/notice_out_bottom">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/introductionText"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
@@ -37,25 +37,25 @@
|
||||
tools:text="@string/introduction_request_received"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionTime"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_below="@+id/text"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/introductionStatus"
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/introductionTime"
|
||||
android:layout_alignBottom="@+id/time"
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
android:layout_toEndOf="@+id/introductionTime"
|
||||
android:layout_toRightOf="@+id/introductionTime"
|
||||
android:layout_toEndOf="@+id/time"
|
||||
android:layout_toRightOf="@+id/time"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/message_delivered"/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/msgBody"
|
||||
android:id="@+id/msgText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
@@ -19,7 +19,7 @@
|
||||
tools:text="Short message"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/noticeLayout"
|
||||
android:id="@+id/layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
@@ -27,7 +27,7 @@
|
||||
android:background="@drawable/notice_in_bottom">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/introductionText"
|
||||
android:id="@+id/text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="80dp"
|
||||
@@ -38,11 +38,11 @@
|
||||
tools:text="@string/introduction_request_received"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/introductionTime"
|
||||
android:id="@+id/time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_alignEnd="@+id/text"
|
||||
android:layout_alignRight="@+id/text"
|
||||
android:layout_below="@+id/declineButton"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:textColor="@color/private_message_date"
|
||||
@@ -54,9 +54,9 @@
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignEnd="@+id/introductionText"
|
||||
android:layout_alignRight="@+id/introductionText"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_alignEnd="@+id/text"
|
||||
android:layout_alignRight="@+id/text"
|
||||
android:layout_below="@+id/text"
|
||||
android:text="@string/accept"/>
|
||||
|
||||
<Button
|
||||
@@ -64,7 +64,7 @@
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/introductionText"
|
||||
android:layout_below="@+id/text"
|
||||
android:layout_marginBottom="-15dp"
|
||||
android:layout_toLeftOf="@+id/acceptButton"
|
||||
android:layout_toStartOf="@+id/acceptButton"
|
||||
@@ -1,33 +0,0 @@
|
||||
<?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="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/notice_in"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/noticeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="italic"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
tools:text="@string/introduction_response_accepted_received"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noticeTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|end"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,52 +0,0 @@
|
||||
<?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
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="right|end"
|
||||
android:background="@drawable/notice_out"
|
||||
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
|
||||
android:layout_marginRight="@dimen/message_bubble_margin_tail">
|
||||
|
||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
android:id="@+id/noticeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:textStyle="italic"
|
||||
android:textColor="@color/briar_text_secondary"
|
||||
tools:text="@string/introduction_response_accepted_sent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noticeTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/noticeText"
|
||||
android:textColor="@color/private_message_date"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="Dec 24, 13:37"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/noticeStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignBottom="@+id/noticeTime"
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
android:layout_toEndOf="@+id/noticeTime"
|
||||
android:layout_toRightOf="@+id/noticeTime"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:src="@drawable/message_delivered"/>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -133,7 +133,6 @@
|
||||
<string name="introduction_request_sent">You have asked to introduce %1$s to %2$s.</string>
|
||||
<string name="introduction_request_received">%1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list?</string>
|
||||
<string name="introduction_request_exists_received">%1$s has asked to introduce you to %2$s, but %2$s is already in your contact list. Since %1$s might not know that, you can still respond:</string>
|
||||
<string name="introduction_request_for_our_identity_received">%1$s has asked to introduce you to %2$s, but %2$s is one of your other identities, so you cannot accept the introduction:</string>
|
||||
<string name="introduction_request_answered_received">%1$s has asked to introduce you to %2$s.</string>
|
||||
<string name="introduction_response_accepted_sent">You accepted the introduction to %1$s.</string>
|
||||
<string name="introduction_response_declined_sent">You declined the introduction to %1$s.</string>
|
||||
@@ -168,6 +167,21 @@
|
||||
<string name="groups_leave">Leave Group</string>
|
||||
<string name="groups_dissolve">Dissolve Group</string>
|
||||
|
||||
<!-- Private Group Invitations -->
|
||||
<string name="groups_invitations_title">Group Invitations</string>
|
||||
<string name="groups_invitations_invitation_sent">You have invited %1$s to your group "%2$s".</string>
|
||||
<string name="groups_invitations_invitation_received">%1$s has invited you to join the group "%2$s".</string>
|
||||
<string name="groups_invitations_joined">Joined group</string>
|
||||
<string name="groups_invitations_declined">Group invitation declined</string>
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">%d open group invitation</item>
|
||||
<item quantity="other">%d open group invitations</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">You accepted the group invitation from %s.</string>
|
||||
<string name="groups_invitations_response_declined_sent">You declined the group invitation from %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s accepted your group invitation.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s declined your group invitation.</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>
|
||||
@@ -206,7 +220,6 @@
|
||||
<string name="forum_share_error">There was an error sharing this forum.</string>
|
||||
<string name="forum_invitation_received">%1$s has shared the forum \"%2$s\" with you.</string>
|
||||
<string name="forum_invitation_sent">You have shared the forum \"%1$s\" with %2$s.</string>
|
||||
<string name="forum_show_invitations">Show Forum Invitations</string>
|
||||
<string name="forum_invitations_title">Forum Invitations</string>
|
||||
<string name="forum_invitation_exists">You accepted an invitation to this forum already. Accepting more invitations will grow and strengthen the communication in the forum.</string>
|
||||
<string name="forum_joined_toast">Joined Forum</string>
|
||||
@@ -270,7 +283,6 @@
|
||||
<string name="blogs_sharing_response_declined_received">%s declined the blog invitation.</string>
|
||||
<string name="blogs_sharing_invitation_received">%1$s has shared the personal blog of %2$s with you.</string>
|
||||
<string name="blogs_sharing_invitation_sent">You have shared the personal blog of %1$s with %2$s.</string>
|
||||
<string name="blogs_sharing_show_invitations">Show Blog Invitations</string>
|
||||
<string name="blogs_sharing_invitations_title">Blog Invitations</string>
|
||||
<string name="blogs_sharing_exists">You are subscribed to this blog already. Accepting again can lead to faster blog post delivery.</string>
|
||||
<string name="blogs_sharing_joined_toast">Subscribed to Blog</string>
|
||||
|
||||
@@ -33,15 +33,16 @@ 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.sharing.ContactSelectorFragment;
|
||||
import org.briarproject.android.sharing.InvitationsBlogActivity;
|
||||
import org.briarproject.android.sharing.InvitationsForumActivity;
|
||||
import org.briarproject.android.privategroup.invitation.GroupInvitationActivity;
|
||||
import org.briarproject.android.sharing.ShareBlogActivity;
|
||||
import org.briarproject.android.sharing.ShareBlogMessageFragment;
|
||||
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.SharingStatusBlogActivity;
|
||||
import org.briarproject.android.sharing.SharingStatusForumActivity;
|
||||
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;
|
||||
|
||||
@@ -72,13 +73,13 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(ConversationActivity activity);
|
||||
|
||||
void inject(InvitationsForumActivity activity);
|
||||
void inject(ForumInvitationActivity activity);
|
||||
|
||||
void inject(InvitationsBlogActivity activity);
|
||||
void inject(BlogInvitationActivity activity);
|
||||
|
||||
void inject(CreateGroupActivity activity);
|
||||
|
||||
void inject(GroupActivity activity);
|
||||
void inject(GroupInvitationActivity activity);
|
||||
|
||||
void inject(CreateForumActivity activity);
|
||||
|
||||
@@ -86,9 +87,9 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(ShareBlogActivity activity);
|
||||
|
||||
void inject(SharingStatusForumActivity activity);
|
||||
void inject(ForumSharingStatusActivity activity);
|
||||
|
||||
void inject(SharingStatusBlogActivity activity);
|
||||
void inject(BlogSharingStatusActivity activity);
|
||||
|
||||
void inject(ForumActivity activity);
|
||||
|
||||
|
||||
@@ -25,8 +25,14 @@ import org.briarproject.android.privategroup.conversation.GroupController;
|
||||
import org.briarproject.android.privategroup.conversation.GroupControllerImpl;
|
||||
import org.briarproject.android.privategroup.creation.CreateGroupController;
|
||||
import org.briarproject.android.privategroup.creation.CreateGroupControllerImpl;
|
||||
import org.briarproject.android.privategroup.invitation.GroupInvitationController;
|
||||
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.sharing.BlogInvitationController;
|
||||
import org.briarproject.android.sharing.BlogInvitationControllerImpl;
|
||||
import org.briarproject.android.sharing.ForumInvitationController;
|
||||
import org.briarproject.android.sharing.ForumInvitationControllerImpl;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -117,6 +123,13 @@ public class ActivityModule {
|
||||
return groupController;
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Provides
|
||||
protected GroupInvitationController provideInvitationGroupController(
|
||||
GroupInvitationControllerImpl groupInvitationController) {
|
||||
return groupInvitationController;
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Provides
|
||||
protected ForumController provideForumController(
|
||||
@@ -125,6 +138,22 @@ public class ActivityModule {
|
||||
return forumController;
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Provides
|
||||
protected ForumInvitationController provideInvitationForumController(
|
||||
ForumInvitationControllerImpl forumInvitationController) {
|
||||
activity.addLifecycleController(forumInvitationController);
|
||||
return forumInvitationController;
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Provides
|
||||
protected BlogInvitationController provideInvitationBlogController(
|
||||
BlogInvitationControllerImpl blogInvitationController) {
|
||||
activity.addLifecycleController(blogInvitationController);
|
||||
return blogInvitationController;
|
||||
}
|
||||
|
||||
@ActivityScope
|
||||
@Provides
|
||||
BlogController provideBlogController(BlogControllerImpl blogController) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.plugins.ConnectionRegistry;
|
||||
import org.briarproject.api.plugins.PluginManager;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.settings.SettingsManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.plugins.AndroidPluginsModule;
|
||||
@@ -96,6 +97,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
|
||||
|
||||
PrivateGroupManager privateGroupManager();
|
||||
|
||||
GroupInvitationManager groupInvitationManager();
|
||||
|
||||
ForumManager forumManager();
|
||||
|
||||
ForumSharingManager forumSharingManager();
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.sharing.ShareBlogActivity;
|
||||
import org.briarproject.android.sharing.SharingStatusBlogActivity;
|
||||
import org.briarproject.android.sharing.BlogSharingStatusActivity;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.db.DbException;
|
||||
@@ -164,7 +164,7 @@ public class BlogFragment extends BaseFragment implements
|
||||
return true;
|
||||
case R.id.action_blog_sharing_status:
|
||||
Intent i3 = new Intent(getActivity(),
|
||||
SharingStatusBlogActivity.class);
|
||||
BlogSharingStatusActivity.class);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivity(i3, options.toBundle());
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.keyagreement.KeyAgreementActivity;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.api.clients.BaseMessageHeader;
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
@@ -270,34 +271,35 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
LOG.info("Private message received, updating item");
|
||||
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
|
||||
PrivateMessageHeader h = p.getMessageHeader();
|
||||
updateItem(p.getContactId(), ConversationItem.from(h));
|
||||
updateItem(p.getContactId(), h);
|
||||
} else if (e instanceof IntroductionRequestReceivedEvent) {
|
||||
LOG.info("Introduction request received, updating item");
|
||||
IntroductionRequestReceivedEvent m =
|
||||
(IntroductionRequestReceivedEvent) e;
|
||||
IntroductionRequest ir = m.getIntroductionRequest();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
updateItem(m.getContactId(), ir);
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
LOG.info("Introduction response received, updating item");
|
||||
IntroductionResponseReceivedEvent m =
|
||||
(IntroductionResponseReceivedEvent) e;
|
||||
IntroductionResponse ir = m.getIntroductionResponse();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
updateItem(m.getContactId(), ir);
|
||||
} else if (e instanceof InvitationRequestReceivedEvent) {
|
||||
LOG.info("Invitation request received, updating item");
|
||||
InvitationRequestReceivedEvent m = (InvitationRequestReceivedEvent) e;
|
||||
LOG.info("Invitation Request received, update item");
|
||||
InvitationRequestReceivedEvent m =
|
||||
(InvitationRequestReceivedEvent) e;
|
||||
InvitationRequest ir = m.getRequest();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
updateItem(m.getContactId(), ir);
|
||||
} else if (e instanceof InvitationResponseReceivedEvent) {
|
||||
LOG.info("Invitation response received, updating item");
|
||||
InvitationResponseReceivedEvent m =
|
||||
(InvitationResponseReceivedEvent) e;
|
||||
InvitationResponse ir = m.getResponse();
|
||||
updateItem(m.getContactId(), ConversationItem.from(ir));
|
||||
updateItem(m.getContactId(), ir);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateItem(final ContactId c, final ConversationItem m) {
|
||||
private void updateItem(final ContactId c, final BaseMessageHeader h) {
|
||||
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -305,7 +307,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
int position = adapter.findItemPosition(c);
|
||||
ContactListItem item = adapter.getItemAt(position);
|
||||
if (item != null) {
|
||||
item.addMessage(m);
|
||||
ConversationItem i = ConversationItem.from(getContext(), h);
|
||||
item.addMessage(i);
|
||||
adapter.updateItemAt(position, item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
// This class is NOT thread-safe
|
||||
@NotThreadSafe
|
||||
public class ContactListItem {
|
||||
|
||||
private final Contact contact;
|
||||
@@ -34,8 +34,7 @@ public class ContactListItem {
|
||||
empty = empty && message == null;
|
||||
if (message != null) {
|
||||
if (message.getTime() > timestamp) timestamp = message.getTime();
|
||||
if (message instanceof IncomingItem &&
|
||||
!((IncomingItem) message).isRead())
|
||||
if (!message.isRead())
|
||||
unread++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.android.contact;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
@@ -27,7 +28,7 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.contact.ConversationAdapter.IntroductionHandler;
|
||||
import org.briarproject.android.contact.ConversationAdapter.RequestListener;
|
||||
import org.briarproject.android.introduction.IntroductionActivity;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.android.view.TextInputView;
|
||||
@@ -39,6 +40,7 @@ import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoExecutor;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.event.ContactConnectedEvent;
|
||||
@@ -64,6 +66,7 @@ import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.plugins.ConnectionRegistry;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.settings.Settings;
|
||||
import org.briarproject.api.settings.SettingsManager;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
@@ -72,6 +75,7 @@ import org.briarproject.api.sharing.InvitationResponse;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -92,16 +96,15 @@ import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.OnHidePromptListener;
|
||||
|
||||
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||
import static android.support.v7.util.SortedList.INVALID_POSITION;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
|
||||
import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
|
||||
|
||||
public class ConversationActivity extends BriarActivity
|
||||
implements EventListener, IntroductionHandler, TextInputListener {
|
||||
implements EventListener, RequestListener, TextInputListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ConversationActivity.class.getName());
|
||||
@@ -117,7 +120,7 @@ public class ConversationActivity extends BriarActivity
|
||||
@CryptoExecutor
|
||||
Executor cryptoExecutor;
|
||||
|
||||
private final Map<MessageId, byte[]> bodyCache = new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
|
||||
|
||||
private ConversationAdapter adapter;
|
||||
private Toolbar toolbar;
|
||||
@@ -144,6 +147,8 @@ public class ConversationActivity extends BriarActivity
|
||||
volatile ForumSharingManager forumSharingManager;
|
||||
@Inject
|
||||
volatile BlogSharingManager blogSharingManager;
|
||||
@Inject
|
||||
volatile GroupInvitationManager groupInvitationManager;
|
||||
|
||||
private volatile GroupId groupId = null;
|
||||
private volatile ContactId contactId = null;
|
||||
@@ -325,7 +330,6 @@ public class ConversationActivity extends BriarActivity
|
||||
toolbarStatus
|
||||
.setContentDescription(getString(R.string.offline));
|
||||
}
|
||||
adapter.setContactName(contactName);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -350,10 +354,14 @@ public class ConversationActivity extends BriarActivity
|
||||
Collection<InvitationMessage> blogInvitations =
|
||||
blogSharingManager
|
||||
.getInvitationMessages(contactId);
|
||||
Collection<InvitationMessage> groupInvitations =
|
||||
groupInvitationManager
|
||||
.getInvitationMessages(contactId);
|
||||
List<InvitationMessage> invitations = new ArrayList<>(
|
||||
forumInvitations.size() + blogInvitations.size());
|
||||
invitations.addAll(forumInvitations);
|
||||
invitations.addAll(blogInvitations);
|
||||
invitations.addAll(groupInvitations);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading messages took " + duration + " ms");
|
||||
@@ -397,34 +405,41 @@ public class ConversationActivity extends BriarActivity
|
||||
Collection<PrivateMessageHeader> headers,
|
||||
Collection<IntroductionMessage> introductions,
|
||||
Collection<InvitationMessage> invitations) {
|
||||
int size = headers.size() + introductions.size() + invitations.size();
|
||||
int size =
|
||||
headers.size() + introductions.size() + invitations.size();
|
||||
List<ConversationItem> items = new ArrayList<>(size);
|
||||
for (PrivateMessageHeader h : headers) {
|
||||
ConversationMessageItem item = ConversationItem.from(h);
|
||||
byte[] body = bodyCache.get(h.getId());
|
||||
ConversationItem item = ConversationItem.from(h);
|
||||
String body = bodyCache.get(h.getId());
|
||||
if (body == null) loadMessageBody(h.getId());
|
||||
else item.setBody(body);
|
||||
items.add(item);
|
||||
}
|
||||
for (IntroductionMessage im : introductions) {
|
||||
if (im instanceof IntroductionRequest) {
|
||||
IntroductionRequest ir = (IntroductionRequest) im;
|
||||
items.add(ConversationItem.from(ir));
|
||||
for (IntroductionMessage m : introductions) {
|
||||
ConversationItem item;
|
||||
if (m instanceof IntroductionRequest) {
|
||||
IntroductionRequest i = (IntroductionRequest) m;
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this, contactName, i);
|
||||
} else {
|
||||
IntroductionResponse ir = (IntroductionResponse) im;
|
||||
items.add(ConversationItem.from(ConversationActivity.this,
|
||||
contactName, ir));
|
||||
IntroductionResponse i = (IntroductionResponse) m;
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this, contactName, i);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
for (InvitationMessage im : invitations) {
|
||||
if (im instanceof InvitationRequest) {
|
||||
InvitationRequest ir = (InvitationRequest) im;
|
||||
items.add(ConversationItem.from(ir));
|
||||
} else if (im instanceof InvitationResponse) {
|
||||
InvitationResponse ir = (InvitationResponse) im;
|
||||
items.add(ConversationItem.from(ConversationActivity.this,
|
||||
contactName, ir));
|
||||
for (InvitationMessage i : invitations) {
|
||||
ConversationItem item;
|
||||
if (i instanceof InvitationRequest) {
|
||||
InvitationRequest r = (InvitationRequest) i;
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this, contactName, r);
|
||||
} else {
|
||||
InvitationResponse r = (InvitationResponse) i;
|
||||
item = ConversationItem
|
||||
.from(ConversationActivity.this, contactName, r);
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
@@ -439,7 +454,7 @@ public class ConversationActivity extends BriarActivity
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading body took " + duration + " ms");
|
||||
displayMessageBody(m, body);
|
||||
displayMessageBody(m, StringUtils.fromUtf8(body));
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -448,15 +463,15 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void displayMessageBody(final MessageId m, final byte[] body) {
|
||||
private void displayMessageBody(final MessageId m, final String body) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
bodyCache.put(m, body);
|
||||
SparseArray<ConversationMessageItem> messages =
|
||||
SparseArray<ConversationItem> messages =
|
||||
adapter.getPrivateMessages();
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
ConversationMessageItem item = messages.valueAt(i);
|
||||
ConversationItem item = messages.valueAt(i);
|
||||
if (item.getId().equals(m)) {
|
||||
item.setBody(body);
|
||||
adapter.notifyItemChanged(messages.keyAt(i));
|
||||
@@ -482,9 +497,9 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
private void markMessagesRead() {
|
||||
Map<MessageId, GroupId> unread = new HashMap<>();
|
||||
SparseArray<IncomingItem> list = adapter.getIncomingMessages();
|
||||
SparseArray<ConversationItem> list = adapter.getIncomingMessages();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
IncomingItem item = list.valueAt(i);
|
||||
ConversationItem item = list.valueAt(i);
|
||||
if (!item.isRead())
|
||||
unread.put(item.getId(), item.getGroupId());
|
||||
}
|
||||
@@ -561,7 +576,8 @@ public class ConversationActivity extends BriarActivity
|
||||
if (event.getContactId().equals(contactId)) {
|
||||
LOG.info("Introduction request received, adding...");
|
||||
IntroductionRequest ir = event.getIntroductionRequest();
|
||||
ConversationItem item = new ConversationIntroductionInItem(ir);
|
||||
ConversationItem item =
|
||||
ConversationItem.from(this, contactName, ir);
|
||||
addConversationItem(item);
|
||||
}
|
||||
} else if (e instanceof IntroductionResponseReceivedEvent) {
|
||||
@@ -580,7 +596,8 @@ public class ConversationActivity extends BriarActivity
|
||||
if (event.getContactId().equals(contactId)) {
|
||||
LOG.info("Invitation received, adding...");
|
||||
InvitationRequest ir = event.getRequest();
|
||||
ConversationItem item = ConversationItem.from(ir);
|
||||
ConversationItem item =
|
||||
ConversationItem.from(this, contactName, ir);
|
||||
addConversationItem(item);
|
||||
}
|
||||
} else if (e instanceof InvitationResponseReceivedEvent) {
|
||||
@@ -603,9 +620,10 @@ public class ConversationActivity extends BriarActivity
|
||||
public void run() {
|
||||
adapter.incrementRevision();
|
||||
Set<MessageId> messages = new HashSet<>(messageIds);
|
||||
SparseArray<OutgoingItem> list = adapter.getOutgoingMessages();
|
||||
SparseArray<ConversationOutItem> list =
|
||||
adapter.getOutgoingMessages();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
OutgoingItem item = list.valueAt(i);
|
||||
ConversationOutItem item = list.valueAt(i);
|
||||
if (messages.contains(item.getId())) {
|
||||
item.setSent(sent);
|
||||
item.setSeen(seen);
|
||||
@@ -622,7 +640,7 @@ public class ConversationActivity extends BriarActivity
|
||||
text = StringUtils.truncateUtf8(text, MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
createMessage(StringUtils.toUtf8(text), timestamp);
|
||||
createMessage(text, timestamp);
|
||||
textInputView.setText("");
|
||||
}
|
||||
|
||||
@@ -632,14 +650,14 @@ public class ConversationActivity extends BriarActivity
|
||||
return item == null ? 0 : item.getTime() + 1;
|
||||
}
|
||||
|
||||
private void createMessage(final byte[] body, final long timestamp) {
|
||||
private void createMessage(final String body, final long timestamp) {
|
||||
cryptoExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
storeMessage(privateMessageFactory.createPrivateMessage(
|
||||
groupId, timestamp, null, "text/plain", body),
|
||||
body);
|
||||
groupId, timestamp, null, "text/plain",
|
||||
StringUtils.toUtf8(body)), body);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -647,7 +665,7 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void storeMessage(final PrivateMessage m, final byte[] body) {
|
||||
private void storeMessage(final PrivateMessage m, final String body) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -661,7 +679,7 @@ public class ConversationActivity extends BriarActivity
|
||||
PrivateMessageHeader h = new PrivateMessageHeader(id,
|
||||
groupId, m.getMessage().getTimestamp(),
|
||||
m.getContentType(), true, false, false, false);
|
||||
ConversationMessageItem item = ConversationItem.from(h);
|
||||
ConversationItem item = ConversationItem.from(h);
|
||||
item.setBody(body);
|
||||
bodyCache.put(id, body);
|
||||
addConversationItem(item);
|
||||
@@ -812,21 +830,37 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void respondToIntroduction(final SessionId sessionId,
|
||||
public void respondToRequest(@NotNull final ConversationRequestItem item,
|
||||
final boolean accept) {
|
||||
int position = adapter.findItemPosition(item);
|
||||
if (position != INVALID_POSITION) {
|
||||
adapter.notifyItemChanged(position, item);
|
||||
}
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
|
||||
try {
|
||||
if (accept) {
|
||||
introductionManager.acceptIntroduction(contactId,
|
||||
sessionId, timestamp);
|
||||
} else {
|
||||
introductionManager.declineIntroduction(contactId,
|
||||
sessionId, timestamp);
|
||||
switch (item.getRequestType()) {
|
||||
case INTRODUCTION:
|
||||
respondToIntroductionRequest(item.getSessionId(),
|
||||
accept, timestamp);
|
||||
break;
|
||||
case FORUM:
|
||||
respondToForumRequest(item.getSessionId(), accept);
|
||||
break;
|
||||
case BLOG:
|
||||
respondToBlogRequest(item.getSessionId(), accept);
|
||||
break;
|
||||
case GROUP:
|
||||
respondToGroupRequest(item.getSessionId(), accept);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown Request Type");
|
||||
}
|
||||
loadMessages();
|
||||
} catch (DbException | FormatException e) {
|
||||
@@ -839,6 +873,34 @@ public class ConversationActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToIntroductionRequest(SessionId sessionId,
|
||||
boolean accept, long time) throws DbException, FormatException {
|
||||
if (accept) {
|
||||
introductionManager.acceptIntroduction(contactId, sessionId, time);
|
||||
} else {
|
||||
introductionManager.declineIntroduction(contactId, sessionId, time);
|
||||
}
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToForumRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
forumSharingManager.respondToInvitation(id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToBlogRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
blogSharingManager.respondToInvitation(id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToGroupRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
groupInvitationManager.respondToInvitation(id, accept);
|
||||
}
|
||||
|
||||
private void introductionResponseError() {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -1,367 +1,66 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.sharing.InvitationsBlogActivity;
|
||||
import org.briarproject.android.sharing.InvitationsForumActivity;
|
||||
import org.briarproject.android.util.AndroidUtils;
|
||||
import org.briarproject.android.util.BriarAdapter;
|
||||
import org.briarproject.api.blogs.BlogInvitationRequest;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import static android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import static org.briarproject.android.contact.ConversationItem.MSG_IN_UNREAD;
|
||||
import static org.briarproject.android.contact.ConversationItem.MSG_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.NOTICE_IN;
|
||||
import static org.briarproject.android.contact.ConversationItem.NOTICE_OUT;
|
||||
import static org.briarproject.android.contact.ConversationItem.OutgoingItem;
|
||||
class ConversationAdapter
|
||||
extends BriarAdapter<ConversationItem, ConversationItemViewHolder> {
|
||||
|
||||
class ConversationAdapter extends BriarAdapter<ConversationItem, ViewHolder> {
|
||||
private RequestListener listener;
|
||||
|
||||
private IntroductionHandler intro;
|
||||
private String contactName;
|
||||
|
||||
ConversationAdapter(Context ctx, IntroductionHandler introductionHandler) {
|
||||
ConversationAdapter(Context ctx, RequestListener requestListener) {
|
||||
super(ctx, ConversationItem.class);
|
||||
intro = introductionHandler;
|
||||
}
|
||||
|
||||
void setContactName(String contactName) {
|
||||
this.contactName = contactName;
|
||||
notifyDataSetChanged();
|
||||
listener = requestListener;
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return items.get(position).getType();
|
||||
ConversationItem item = items.get(position);
|
||||
return item.getLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
||||
View v;
|
||||
|
||||
// outgoing message (local)
|
||||
if (type == MSG_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_out, viewGroup, false);
|
||||
return new MessageHolder(v, type);
|
||||
} else if (type == INTRODUCTION_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_introduction_in, viewGroup, false);
|
||||
return new IntroductionHolder(v, type);
|
||||
} else if (type == INTRODUCTION_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_notice_out, viewGroup, false);
|
||||
return new IntroductionHolder(v, type);
|
||||
} else if (type == NOTICE_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_notice_in, viewGroup, false);
|
||||
return new NoticeHolder(v, type);
|
||||
} else if (type == NOTICE_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_notice_out, viewGroup, false);
|
||||
return new NoticeHolder(v, type);
|
||||
} else if (type == FORUM_INVITATION_IN || type == BLOG_INVITATION_IN) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_shareable_invitation_in, viewGroup,
|
||||
false);
|
||||
return new InvitationHolder(v, type);
|
||||
} else if (type == FORUM_INVITATION_OUT ||
|
||||
type == BLOG_INVITATION_OUT) {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_notice_out, viewGroup, false);
|
||||
return new InvitationHolder(v, type);
|
||||
}
|
||||
// incoming message (non-local)
|
||||
else {
|
||||
v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
R.layout.list_item_msg_in, viewGroup, false);
|
||||
return new MessageHolder(v, type);
|
||||
public ConversationItemViewHolder onCreateViewHolder(ViewGroup viewGroup,
|
||||
@LayoutRes int type) {
|
||||
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||
type, viewGroup, false);
|
||||
switch (type) {
|
||||
case R.layout.list_item_conversation_msg_in:
|
||||
return new ConversationItemViewHolder(v);
|
||||
case R.layout.list_item_conversation_msg_out:
|
||||
return new ConversationMessageOutViewHolder(v);
|
||||
case R.layout.list_item_conversation_notice_in:
|
||||
return new ConversationNoticeInViewHolder(v);
|
||||
case R.layout.list_item_conversation_notice_out:
|
||||
return new ConversationNoticeOutViewHolder(v);
|
||||
case R.layout.list_item_conversation_request:
|
||||
return new ConversationRequestViewHolder(v);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown ConversationItem");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder ui, int position) {
|
||||
ConversationItem item = getItemAt(position);
|
||||
if (item instanceof ConversationMessageItem) {
|
||||
bindMessage((MessageHolder) ui, (ConversationMessageItem) item);
|
||||
} else if (item instanceof ConversationIntroductionOutItem) {
|
||||
bindIntroduction((IntroductionHolder) ui,
|
||||
(ConversationIntroductionOutItem) item, position);
|
||||
} else if (item instanceof ConversationIntroductionInItem) {
|
||||
bindIntroduction((IntroductionHolder) ui,
|
||||
(ConversationIntroductionInItem) item, position);
|
||||
} else if (item instanceof ConversationNoticeOutItem) {
|
||||
bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item);
|
||||
} else if (item instanceof ConversationNoticeInItem) {
|
||||
bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item);
|
||||
} else if (item instanceof ConversationShareableInvitationOutItem) {
|
||||
bindInvitation((InvitationHolder) ui,
|
||||
(ConversationShareableInvitationOutItem) item);
|
||||
} else if (item instanceof ConversationShareableInvitationInItem) {
|
||||
bindInvitation((InvitationHolder) ui,
|
||||
(ConversationShareableInvitationInItem) item);
|
||||
public void onBindViewHolder(ConversationItemViewHolder ui, int position) {
|
||||
ConversationItem item = items.get(position);
|
||||
if (item instanceof ConversationRequestItem) {
|
||||
((ConversationRequestViewHolder) ui).bind(item, listener);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unhandled Conversation Item");
|
||||
ui.bind(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindMessage(MessageHolder ui, ConversationMessageItem item) {
|
||||
PrivateMessageHeader header = item.getHeader();
|
||||
|
||||
if (item instanceof ConversationItem.OutgoingItem) {
|
||||
if (((OutgoingItem) item).isSeen()) {
|
||||
ui.status.setImageResource(R.drawable.message_delivered_white);
|
||||
} else if (((OutgoingItem) item).isSent()) {
|
||||
ui.status.setImageResource(R.drawable.message_sent_white);
|
||||
} else {
|
||||
ui.status.setImageResource(R.drawable.message_stored_white);
|
||||
}
|
||||
} else {
|
||||
if (item.getType() == MSG_IN_UNREAD) {
|
||||
// TODO implement new unread message highlight according to #232
|
||||
/* int left = ui.layout.getPaddingLeft();
|
||||
int top = ui.layout.getPaddingTop();
|
||||
int right = ui.layout.getPaddingRight();
|
||||
int bottom = ui.layout.getPaddingBottom();
|
||||
|
||||
// show unread messages in different color to not miss them
|
||||
ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
|
||||
|
||||
// re-apply the previous padding due to bug in some Android versions
|
||||
// see: https://code.google.com/p/android/issues/detail?id=17885
|
||||
ui.layout.setPadding(left, top, right, bottom);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (item.getBody() == null) {
|
||||
ui.body.setText("\u2026");
|
||||
} else if (header.getContentType().equals("text/plain")) {
|
||||
ui.body.setText(
|
||||
StringUtils.trim(StringUtils.fromUtf8(item.getBody())));
|
||||
} else {
|
||||
// TODO support other content types
|
||||
}
|
||||
|
||||
long timestamp = header.getTimestamp();
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, timestamp));
|
||||
}
|
||||
|
||||
private void bindIntroduction(IntroductionHolder ui,
|
||||
final ConversationIntroductionItem item, final int position) {
|
||||
|
||||
final IntroductionRequest ir = item.getIntroductionRequest();
|
||||
int backgroundRes;
|
||||
|
||||
String message = ir.getMessage();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
ui.message.setVisibility(GONE);
|
||||
if (item instanceof ConversationIntroductionOutItem) {
|
||||
backgroundRes = R.drawable.notice_out;
|
||||
} else {
|
||||
backgroundRes = R.drawable.notice_in;
|
||||
}
|
||||
} else {
|
||||
ui.message.setText(StringUtils.trim(message));
|
||||
ui.message.setVisibility(VISIBLE);
|
||||
if (item instanceof ConversationIntroductionOutItem) {
|
||||
backgroundRes = R.drawable.notice_out_bottom;
|
||||
} else {
|
||||
backgroundRes = R.drawable.notice_in_bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// Outgoing Introduction Request
|
||||
if (item instanceof ConversationIntroductionOutItem) {
|
||||
ui.text.setText(ctx.getString(R.string.introduction_request_sent,
|
||||
contactName, ir.getName()));
|
||||
ConversationIntroductionOutItem i =
|
||||
(ConversationIntroductionOutItem) item;
|
||||
if (i.isSeen()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
} else if (i.isSent()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_sent);
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_stored);
|
||||
}
|
||||
}
|
||||
// Incoming Introduction Request (Answered)
|
||||
else if (item.wasAnswered()) {
|
||||
ui.text.setText(ctx.getString(
|
||||
R.string.introduction_request_answered_received,
|
||||
contactName, ir.getName()));
|
||||
ui.acceptButton.setVisibility(GONE);
|
||||
ui.declineButton.setVisibility(GONE);
|
||||
}
|
||||
// Incoming Introduction Request (Not Answered)
|
||||
else {
|
||||
if (item.getIntroductionRequest().contactExists()) {
|
||||
ui.text.setText(ctx.getString(
|
||||
R.string.introduction_request_exists_received,
|
||||
contactName, ir.getName()));
|
||||
} else {
|
||||
ui.text.setText(
|
||||
ctx.getString(R.string.introduction_request_received,
|
||||
contactName, ir.getName()));
|
||||
}
|
||||
|
||||
if (item.getIntroductionRequest().doesIntroduceOtherIdentity()) {
|
||||
// don't allow accept when one of our identities is introduced
|
||||
ui.acceptButton.setVisibility(GONE);
|
||||
ui.text.setText(ctx.getString(
|
||||
R.string.introduction_request_for_our_identity_received,
|
||||
contactName, ir.getName()));
|
||||
} else {
|
||||
ui.acceptButton.setVisibility(VISIBLE);
|
||||
ui.acceptButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
intro.respondToIntroduction(ir.getSessionId(), true);
|
||||
item.setAnswered(true);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
ui.declineButton.setVisibility(VISIBLE);
|
||||
ui.declineButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
intro.respondToIntroduction(ir.getSessionId(), false);
|
||||
item.setAnswered(true);
|
||||
notifyItemChanged(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
|
||||
ui.notice.setBackgroundResource(backgroundRes);
|
||||
}
|
||||
|
||||
private void bindNotice(NoticeHolder ui, ConversationNoticeItem item) {
|
||||
ui.text.setText(item.getText());
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
|
||||
|
||||
if (item instanceof ConversationNoticeOutItem) {
|
||||
ConversationNoticeOutItem n = (ConversationNoticeOutItem) item;
|
||||
if (n.isSeen()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
} else if (n.isSent()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_sent);
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_stored);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void bindInvitation(InvitationHolder ui,
|
||||
final ConversationShareableInvitationItem item) {
|
||||
|
||||
final InvitationRequest ir = item.getInvitationRequest();
|
||||
String name = "";
|
||||
int receivedRes = 0, sentRes = 0, buttonRes = 0, backgroundRes;
|
||||
if (ir instanceof ForumInvitationRequest) {
|
||||
name = ((ForumInvitationRequest) ir).getForumName();
|
||||
receivedRes = R.string.forum_invitation_received;
|
||||
sentRes = R.string.forum_invitation_sent;
|
||||
buttonRes = R.string.forum_show_invitations;
|
||||
} else if (ir instanceof BlogInvitationRequest) {
|
||||
name = ((BlogInvitationRequest) ir).getBlogAuthorName();
|
||||
receivedRes = R.string.blogs_sharing_invitation_received;
|
||||
sentRes = R.string.blogs_sharing_invitation_sent;
|
||||
buttonRes = R.string.blogs_sharing_show_invitations;
|
||||
}
|
||||
|
||||
String message = ir.getMessage();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
ui.message.setVisibility(GONE);
|
||||
if (item instanceof ConversationShareableInvitationOutItem) {
|
||||
backgroundRes = R.drawable.notice_out;
|
||||
} else {
|
||||
backgroundRes = R.drawable.notice_in;
|
||||
}
|
||||
} else {
|
||||
ui.message.setVisibility(VISIBLE);
|
||||
ui.message.setText(StringUtils.trim(message));
|
||||
if (item instanceof ConversationShareableInvitationOutItem) {
|
||||
backgroundRes = R.drawable.notice_out_bottom;
|
||||
} else {
|
||||
backgroundRes = R.drawable.notice_in_bottom;
|
||||
}
|
||||
}
|
||||
|
||||
// Outgoing Invitation
|
||||
if (item instanceof ConversationShareableInvitationOutItem) {
|
||||
ui.text.setText(ctx.getString(sentRes, name, contactName));
|
||||
ConversationShareableInvitationOutItem i =
|
||||
(ConversationShareableInvitationOutItem) item;
|
||||
if (i.isSeen()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_delivered);
|
||||
} else if (i.isSent()) {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_sent);
|
||||
} else {
|
||||
//noinspection ConstantConditions
|
||||
ui.status.setImageResource(R.drawable.message_stored);
|
||||
}
|
||||
}
|
||||
// Incoming Invitation
|
||||
else {
|
||||
ui.text.setText(ctx.getString(receivedRes, contactName, name));
|
||||
|
||||
if (ir.isAvailable()) {
|
||||
final Class c = ir instanceof ForumInvitationRequest ?
|
||||
InvitationsForumActivity.class :
|
||||
InvitationsBlogActivity.class;
|
||||
ui.showInvitationsButton.setText(ctx.getString(buttonRes));
|
||||
ui.showInvitationsButton.setVisibility(VISIBLE);
|
||||
ui.showInvitationsButton
|
||||
.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(ctx, c);
|
||||
ctx.startActivity(i);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
ui.showInvitationsButton.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime()));
|
||||
ui.notice.setBackgroundResource(backgroundRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ConversationItem c1,
|
||||
ConversationItem c2) {
|
||||
@@ -393,138 +92,48 @@ class ConversationAdapter extends BriarAdapter<ConversationItem, ViewHolder> {
|
||||
}
|
||||
}
|
||||
|
||||
SparseArray<IncomingItem> getIncomingMessages() {
|
||||
SparseArray<IncomingItem> messages = new SparseArray<>();
|
||||
SparseArray<ConversationItem> getIncomingMessages() {
|
||||
SparseArray<ConversationItem> messages = new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
if (item instanceof IncomingItem) {
|
||||
messages.put(i, (IncomingItem) item);
|
||||
if (item.isIncoming()) {
|
||||
messages.put(i, item);
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
SparseArray<OutgoingItem> getOutgoingMessages() {
|
||||
SparseArray<OutgoingItem> messages = new SparseArray<>();
|
||||
SparseArray<ConversationOutItem> getOutgoingMessages() {
|
||||
SparseArray<ConversationOutItem> messages = new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
if (item instanceof OutgoingItem) {
|
||||
messages.put(i, (OutgoingItem) item);
|
||||
if (item instanceof ConversationOutItem) {
|
||||
messages.put(i, (ConversationOutItem) item);
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
SparseArray<ConversationMessageItem> getPrivateMessages() {
|
||||
SparseArray<ConversationMessageItem> messages = new SparseArray<>();
|
||||
SparseArray<ConversationItem> getPrivateMessages() {
|
||||
SparseArray<ConversationItem> messages = new SparseArray<>();
|
||||
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
if (item instanceof ConversationMessageItem) {
|
||||
messages.put(i, (ConversationMessageItem) item);
|
||||
if (item instanceof ConversationMessageInItem) {
|
||||
messages.put(i, item);
|
||||
} else if (item instanceof ConversationMessageOutItem) {
|
||||
messages.put(i, item);
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
private static class MessageHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public ViewGroup layout;
|
||||
public TextView body;
|
||||
private TextView date;
|
||||
public ImageView status;
|
||||
|
||||
private MessageHolder(View v, int type) {
|
||||
super(v);
|
||||
|
||||
layout = (ViewGroup) v.findViewById(R.id.msgLayout);
|
||||
body = (TextView) v.findViewById(R.id.msgBody);
|
||||
date = (TextView) v.findViewById(R.id.msgTime);
|
||||
|
||||
// outgoing message (local)
|
||||
if (type == MSG_OUT) {
|
||||
status = (ImageView) v.findViewById(R.id.msgStatus);
|
||||
}
|
||||
}
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
interface RequestListener {
|
||||
void respondToRequest(ConversationRequestItem item, boolean accept);
|
||||
}
|
||||
|
||||
private static class IntroductionHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView message;
|
||||
private final ViewGroup notice;
|
||||
private final TextView text;
|
||||
private final Button acceptButton;
|
||||
private final Button declineButton;
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
private IntroductionHolder(View v, int type) {
|
||||
super(v);
|
||||
|
||||
message = (TextView) v.findViewById(R.id.msgBody);
|
||||
notice = (ViewGroup) v.findViewById(R.id.noticeLayout);
|
||||
text = (TextView) v.findViewById(R.id.introductionText);
|
||||
acceptButton = (Button) v.findViewById(R.id.acceptButton);
|
||||
declineButton = (Button) v.findViewById(R.id.declineButton);
|
||||
date = (TextView) v.findViewById(R.id.introductionTime);
|
||||
|
||||
if (type == INTRODUCTION_OUT) {
|
||||
status = (ImageView) v.findViewById(R.id.introductionStatus);
|
||||
} else {
|
||||
status = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoticeHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView text;
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
private NoticeHolder(View v, int type) {
|
||||
super(v);
|
||||
|
||||
text = (TextView) v.findViewById(R.id.noticeText);
|
||||
date = (TextView) v.findViewById(R.id.noticeTime);
|
||||
|
||||
if (type == NOTICE_OUT) {
|
||||
status = (ImageView) v.findViewById(R.id.noticeStatus);
|
||||
} else {
|
||||
status = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class InvitationHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextView message;
|
||||
private final View notice;
|
||||
private final TextView text;
|
||||
private final Button showInvitationsButton;
|
||||
private final TextView date;
|
||||
private final ImageView status;
|
||||
|
||||
private InvitationHolder(View v, int type) {
|
||||
super(v);
|
||||
|
||||
message = (TextView) v.findViewById(R.id.msgBody);
|
||||
text = (TextView) v.findViewById(R.id.introductionText);
|
||||
notice = v.findViewById(R.id.noticeLayout);
|
||||
showInvitationsButton = (Button) v.findViewById(R.id.showInvitationsButton);
|
||||
date = (TextView) v.findViewById(R.id.introductionTime);
|
||||
|
||||
if (type == FORUM_INVITATION_OUT || type == BLOG_INVITATION_OUT) {
|
||||
status = (ImageView) v.findViewById(R.id.introductionStatus);
|
||||
} else {
|
||||
status = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface IntroductionHandler {
|
||||
void respondToIntroduction(SessionId sessionId, boolean accept);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.android.contact.ConversationItem.IncomingItem;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationIntroductionInItem extends ConversationIntroductionItem
|
||||
implements IncomingItem {
|
||||
|
||||
private boolean read;
|
||||
|
||||
ConversationIntroductionInItem(@NotNull IntroductionRequest ir) {
|
||||
super(ir);
|
||||
|
||||
this.read = ir.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return INTRODUCTION_IN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// This class is not thread-safe
|
||||
abstract class ConversationIntroductionItem extends ConversationItem {
|
||||
|
||||
private final IntroductionRequest ir;
|
||||
private boolean answered;
|
||||
|
||||
ConversationIntroductionItem(@NotNull IntroductionRequest ir) {
|
||||
super(ir.getMessageId(), ir.getGroupId(), ir.getTimestamp());
|
||||
|
||||
this.ir = ir;
|
||||
this.answered = ir.wasAnswered();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
IntroductionRequest getIntroductionRequest() {
|
||||
return ir;
|
||||
}
|
||||
|
||||
boolean wasAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
/**
|
||||
* This class is needed and can not be replaced by an ConversationNoticeOutItem,
|
||||
* because it carries the optional introduction message
|
||||
* to be displayed as a regular private message.
|
||||
*
|
||||
* This class is not thread-safe
|
||||
*/
|
||||
class ConversationIntroductionOutItem extends ConversationIntroductionItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
ConversationIntroductionOutItem(IntroductionRequest ir) {
|
||||
super(ir);
|
||||
this.sent = ir.isSent();
|
||||
this.seen = ir.isSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return INTRODUCTION_OUT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,85 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.StringRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.contact.ConversationRequestItem.RequestType;
|
||||
import org.briarproject.api.blogs.BlogInvitationRequest;
|
||||
import org.briarproject.api.blogs.BlogInvitationResponse;
|
||||
import org.briarproject.api.clients.BaseMessageHeader;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.forum.ForumInvitationResponse;
|
||||
import org.briarproject.api.introduction.IntroductionMessage;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
import org.briarproject.api.introduction.IntroductionResponse;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationRequest;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationResponse;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
import org.briarproject.api.sharing.InvitationResponse;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// This class is not thread-safe
|
||||
public abstract class ConversationItem {
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
// this is needed for RecyclerView adapter which requires an int type
|
||||
final static int MSG_IN = 0;
|
||||
final static int MSG_IN_UNREAD = 1;
|
||||
final static int MSG_OUT = 2;
|
||||
final static int INTRODUCTION_IN = 3;
|
||||
final static int INTRODUCTION_OUT = 4;
|
||||
final static int NOTICE_IN = 5;
|
||||
final static int NOTICE_OUT = 6;
|
||||
final static int FORUM_INVITATION_IN = 7;
|
||||
final static int FORUM_INVITATION_OUT = 8;
|
||||
final static int BLOG_INVITATION_IN = 9;
|
||||
final static int BLOG_INVITATION_OUT = 10;
|
||||
import static org.briarproject.android.contact.ConversationRequestItem.RequestType.BLOG;
|
||||
import static org.briarproject.android.contact.ConversationRequestItem.RequestType.FORUM;
|
||||
import static org.briarproject.android.contact.ConversationRequestItem.RequestType.GROUP;
|
||||
import static org.briarproject.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
abstract class ConversationItem {
|
||||
|
||||
protected @Nullable String body;
|
||||
final private MessageId id;
|
||||
final private GroupId groupId;
|
||||
final private long time;
|
||||
private boolean read;
|
||||
|
||||
public ConversationItem(@NotNull MessageId id, @NotNull GroupId groupId,
|
||||
long time) {
|
||||
ConversationItem(MessageId id, GroupId groupId, @Nullable String body,
|
||||
long time, boolean read) {
|
||||
this.id = id;
|
||||
this.groupId = groupId;
|
||||
this.body = body;
|
||||
this.time = time;
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
abstract int getType();
|
||||
|
||||
@NotNull
|
||||
public MessageId getId() {
|
||||
MessageId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GroupId getGroupId() {
|
||||
GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public static ConversationMessageItem from(PrivateMessageHeader h) {
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
abstract public boolean isIncoming();
|
||||
|
||||
@LayoutRes
|
||||
abstract public int getLayout();
|
||||
|
||||
static ConversationItem from(PrivateMessageHeader h) {
|
||||
if (h.isLocal()) {
|
||||
return new ConversationMessageOutItem(h);
|
||||
} else {
|
||||
@@ -67,17 +87,40 @@ public abstract class ConversationItem {
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationIntroductionItem from(IntroductionRequest ir) {
|
||||
static ConversationItem from(Context ctx, String contactName,
|
||||
IntroductionRequest ir) {
|
||||
if (ir.isLocal()) {
|
||||
return new ConversationIntroductionOutItem(ir);
|
||||
String text = ctx.getString(R.string.introduction_request_sent,
|
||||
contactName, ir.getName());
|
||||
return new ConversationNoticeOutItem(ir.getMessageId(),
|
||||
ir.getGroupId(), text, ir.getMessage(), ir.getTimestamp(),
|
||||
ir.isSent(), ir.isSeen());
|
||||
} else {
|
||||
return new ConversationIntroductionInItem(ir);
|
||||
String text;
|
||||
if (ir.wasAnswered()) {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_request_answered_received,
|
||||
contactName, ir.getName());
|
||||
return new ConversationNoticeInItem(ir.getMessageId(),
|
||||
ir.getGroupId(), text, ir.getMessage(), ir.getTimestamp(),
|
||||
ir.isRead());
|
||||
} else if (ir.contactExists()){
|
||||
text = ctx.getString(
|
||||
R.string.introduction_request_exists_received,
|
||||
contactName, ir.getName());
|
||||
} else {
|
||||
text = ctx.getString(R.string.introduction_request_received,
|
||||
contactName, ir.getName());
|
||||
}
|
||||
return new ConversationRequestItem(ir.getMessageId(),
|
||||
ir.getGroupId(), INTRODUCTION, ir.getSessionId(), text,
|
||||
ir.getMessage(), ir.getTimestamp(), ir.isRead(),
|
||||
ir.wasAnswered());
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationNoticeItem from(Context ctx, String contactName,
|
||||
static ConversationItem from(Context ctx, String contactName,
|
||||
IntroductionResponse ir) {
|
||||
|
||||
if (ir.isLocal()) {
|
||||
String text;
|
||||
if (ir.wasAccepted()) {
|
||||
@@ -90,7 +133,7 @@ public abstract class ConversationItem {
|
||||
ir.getName());
|
||||
}
|
||||
return new ConversationNoticeOutItem(ir.getMessageId(),
|
||||
ir.getGroupId(), text, ir.getTimestamp(), ir.isSent(),
|
||||
ir.getGroupId(), text, null, ir.getTimestamp(), ir.isSent(),
|
||||
ir.isSeen());
|
||||
} else {
|
||||
String text;
|
||||
@@ -110,143 +153,146 @@ public abstract class ConversationItem {
|
||||
}
|
||||
}
|
||||
return new ConversationNoticeInItem(ir.getMessageId(),
|
||||
ir.getGroupId(), text, ir.getTimestamp(), ir.isRead());
|
||||
ir.getGroupId(), text, null, ir.getTimestamp(),
|
||||
ir.isRead());
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationShareableInvitationItem from(
|
||||
InvitationRequest fim) {
|
||||
if (fim.isLocal()) {
|
||||
return new ConversationShareableInvitationOutItem(fim);
|
||||
static ConversationItem from(Context ctx, String contactName,
|
||||
InvitationRequest ir) {
|
||||
if (ir.isLocal()) {
|
||||
String text;
|
||||
if (ir instanceof ForumInvitationRequest) {
|
||||
text = ctx.getString(R.string.forum_invitation_sent,
|
||||
((ForumInvitationRequest) ir).getForumName(),
|
||||
contactName);
|
||||
} else if (ir instanceof BlogInvitationRequest) {
|
||||
text = ctx.getString(R.string.blogs_sharing_invitation_sent,
|
||||
((BlogInvitationRequest) ir).getBlogAuthorName(),
|
||||
contactName);
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_sent,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
}
|
||||
return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(),
|
||||
text, ir.getMessage(), ir.getTimestamp(), ir.isSent(),
|
||||
ir.isSeen());
|
||||
} else {
|
||||
return new ConversationShareableInvitationInItem(fim);
|
||||
String text;
|
||||
RequestType type;
|
||||
if (ir instanceof ForumInvitationRequest) {
|
||||
text = ctx.getString(R.string.forum_invitation_received,
|
||||
contactName,
|
||||
((ForumInvitationRequest) ir).getForumName());
|
||||
type = FORUM;
|
||||
} else if (ir instanceof BlogInvitationRequest) {
|
||||
text = ctx.getString(R.string.blogs_sharing_invitation_received,
|
||||
contactName,
|
||||
((BlogInvitationRequest) ir).getBlogAuthorName());
|
||||
type = BLOG;
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_received,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
type = GROUP;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
}
|
||||
if (!ir.isAvailable()) {
|
||||
return new ConversationNoticeInItem(ir.getId(), ir.getGroupId(),
|
||||
text, ir.getMessage(), ir.getTimestamp(), ir.isRead());
|
||||
}
|
||||
return new ConversationRequestItem(ir.getId(),
|
||||
ir.getGroupId(), type, ir.getSessionId(), text,
|
||||
ir.getMessage(), ir.getTimestamp(), ir.isRead(),
|
||||
!ir.isAvailable());
|
||||
}
|
||||
}
|
||||
|
||||
public static ConversationNoticeItem from(Context ctx, String contactName,
|
||||
static ConversationItem from(Context ctx, String contactName,
|
||||
InvitationResponse ir) {
|
||||
|
||||
if (ir instanceof ForumInvitationResponse) {
|
||||
return from(ctx, contactName, (ForumInvitationResponse) ir);
|
||||
} else if (ir instanceof BlogInvitationResponse) {
|
||||
return from(ctx, contactName, (BlogInvitationResponse) ir);
|
||||
@StringRes int res;
|
||||
if (ir.isLocal()) {
|
||||
if (ir.wasAccepted()) {
|
||||
if (ir instanceof ForumInvitationResponse) {
|
||||
res = R.string.forum_invitation_response_accepted_sent;
|
||||
} else if (ir instanceof BlogInvitationResponse) {
|
||||
res = R.string.blogs_sharing_response_accepted_sent;
|
||||
} else if (ir instanceof GroupInvitationResponse) {
|
||||
res = R.string.groups_invitations_response_accepted_sent;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown InvitationResponse");
|
||||
}
|
||||
} else {
|
||||
if (ir instanceof ForumInvitationResponse) {
|
||||
res = R.string.forum_invitation_response_declined_sent;
|
||||
} else if (ir instanceof BlogInvitationResponse) {
|
||||
res = R.string.blogs_sharing_response_declined_sent;
|
||||
} else if (ir instanceof GroupInvitationResponse) {
|
||||
res = R.string.groups_invitations_response_declined_sent;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown InvitationResponse");
|
||||
}
|
||||
}
|
||||
String text = ctx.getString(res, contactName);
|
||||
return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(),
|
||||
text, null, ir.getTimestamp(), ir.isSent(), ir.isSeen());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown Invitation Response.");
|
||||
}
|
||||
}
|
||||
|
||||
private static ConversationNoticeItem from(Context ctx, String contactName,
|
||||
ForumInvitationResponse fir) {
|
||||
|
||||
if (fir.isLocal()) {
|
||||
String text;
|
||||
if (fir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_accepted_sent,
|
||||
contactName);
|
||||
if (ir.wasAccepted()) {
|
||||
if (ir instanceof ForumInvitationResponse) {
|
||||
res = R.string.forum_invitation_response_accepted_received;
|
||||
} else if (ir instanceof BlogInvitationResponse) {
|
||||
res = R.string.blogs_sharing_response_accepted_received;
|
||||
} else if (ir instanceof GroupInvitationResponse) {
|
||||
res = R.string.groups_invitations_response_accepted_received;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown InvitationResponse");
|
||||
}
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_declined_sent,
|
||||
contactName);
|
||||
if (ir instanceof ForumInvitationResponse) {
|
||||
res = R.string.forum_invitation_response_declined_received;
|
||||
} else if (ir instanceof BlogInvitationResponse) {
|
||||
res = R.string.blogs_sharing_response_declined_received;
|
||||
} else if (ir instanceof GroupInvitationResponse) {
|
||||
res = R.string.groups_invitations_response_declined_received;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown InvitationResponse");
|
||||
}
|
||||
}
|
||||
return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(),
|
||||
text, fir.getTimestamp(), fir.isSent(), fir.isSeen());
|
||||
} else {
|
||||
String text;
|
||||
if (fir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_accepted_received,
|
||||
contactName);
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_declined_received,
|
||||
contactName);
|
||||
}
|
||||
return new ConversationNoticeInItem(fir.getId(), fir.getGroupId(),
|
||||
text, fir.getTimestamp(), fir.isRead());
|
||||
}
|
||||
}
|
||||
|
||||
private static ConversationNoticeItem from(Context ctx, String contactName,
|
||||
BlogInvitationResponse fir) {
|
||||
|
||||
if (fir.isLocal()) {
|
||||
String text;
|
||||
if (fir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_accepted_sent,
|
||||
contactName);
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_declined_sent,
|
||||
contactName);
|
||||
}
|
||||
return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(),
|
||||
text, fir.getTimestamp(), fir.isSent(), fir.isSeen());
|
||||
} else {
|
||||
String text;
|
||||
if (fir.wasAccepted()) {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_accepted_received,
|
||||
contactName);
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_declined_received,
|
||||
contactName);
|
||||
}
|
||||
return new ConversationNoticeInItem(fir.getId(), fir.getGroupId(),
|
||||
text, fir.getTimestamp(), fir.isRead());
|
||||
String text = ctx.getString(res, contactName);
|
||||
return new ConversationNoticeInItem(ir.getId(), ir.getGroupId(),
|
||||
text, null, ir.getTimestamp(), ir.isRead());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should not be used to get user-facing objects,
|
||||
* Its purpose is only to provide data for the contact list.
|
||||
*/
|
||||
public static ConversationItem from(IntroductionMessage im) {
|
||||
if (im.isLocal())
|
||||
return new ConversationNoticeOutItem(im.getMessageId(),
|
||||
im.getGroupId(), "", im.getTimestamp(), false, false);
|
||||
return new ConversationNoticeInItem(im.getMessageId(), im.getGroupId(),
|
||||
"", im.getTimestamp(), im.isRead());
|
||||
* This method should not be used to display the resulting ConversationItem
|
||||
* in the UI, but only to update list information based on the
|
||||
* BaseMessageHeader.
|
||||
**/
|
||||
static ConversationItem from(Context ctx, BaseMessageHeader h) {
|
||||
if (h instanceof PrivateMessageHeader) {
|
||||
return from((PrivateMessageHeader) h);
|
||||
} else if(h instanceof IntroductionRequest) {
|
||||
return from(ctx, "", (IntroductionRequest) h);
|
||||
} else if(h instanceof IntroductionResponse) {
|
||||
return from(ctx, "", (IntroductionResponse) h);
|
||||
} else if(h instanceof InvitationRequest) {
|
||||
return from(ctx, "", (InvitationRequest) h);
|
||||
} else if(h instanceof InvitationResponse) {
|
||||
return from(ctx, "", (InvitationResponse) h);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown message header");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should not be used to get user-facing objects,
|
||||
* Its purpose is only to provide data for the contact list.
|
||||
*/
|
||||
public static ConversationItem from(InvitationMessage im) {
|
||||
if (im.isLocal())
|
||||
return new ConversationNoticeOutItem(im.getId(), im.getGroupId(),
|
||||
"", im.getTimestamp(), false, false);
|
||||
return new ConversationNoticeInItem(im.getId(), im.getGroupId(), "",
|
||||
im.getTimestamp(), im.isRead());
|
||||
}
|
||||
|
||||
interface OutgoingItem {
|
||||
|
||||
@NotNull
|
||||
MessageId getId();
|
||||
|
||||
boolean isSent();
|
||||
|
||||
void setSent(boolean sent);
|
||||
|
||||
boolean isSeen();
|
||||
|
||||
void setSeen(boolean seen);
|
||||
}
|
||||
|
||||
interface IncomingItem {
|
||||
|
||||
@NotNull
|
||||
MessageId getId();
|
||||
|
||||
@NotNull
|
||||
GroupId getGroupId();
|
||||
|
||||
boolean isRead();
|
||||
|
||||
void setRead(boolean read);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.RecyclerView.ViewHolder;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.util.AndroidUtils;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class ConversationItemViewHolder extends ViewHolder {
|
||||
|
||||
protected final ViewGroup layout;
|
||||
private final TextView text;
|
||||
private final TextView time;
|
||||
|
||||
ConversationItemViewHolder(View v) {
|
||||
super(v);
|
||||
layout = (ViewGroup) v.findViewById(R.id.layout);
|
||||
text = (TextView) v.findViewById(R.id.text);
|
||||
time = (TextView) v.findViewById(R.id.time);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
void bind(ConversationItem item) {
|
||||
if (item.getBody() == null) {
|
||||
text.setText("\u2026");
|
||||
} else {
|
||||
text.setText(StringUtils.trim(item.getBody()));
|
||||
}
|
||||
|
||||
long timestamp = item.getTime();
|
||||
time.setText(AndroidUtils.formatDate(time.getContext(), timestamp));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +1,30 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationMessageInItem extends ConversationMessageItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
private boolean read;
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class ConversationMessageInItem extends ConversationItem {
|
||||
|
||||
ConversationMessageInItem(PrivateMessageHeader header) {
|
||||
super(header);
|
||||
|
||||
read = header.isRead();
|
||||
ConversationMessageInItem(PrivateMessageHeader h) {
|
||||
super(h.getId(), h.getGroupId(), null, h.getTimestamp(), h.isRead());
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return MSG_IN;
|
||||
public boolean isIncoming() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
public int getLayout() {
|
||||
return R.layout.list_item_conversation_msg_in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
abstract class ConversationMessageItem extends ConversationItem {
|
||||
|
||||
private final PrivateMessageHeader header;
|
||||
private byte[] body;
|
||||
|
||||
ConversationMessageItem(PrivateMessageHeader header) {
|
||||
super(header.getId(), header.getGroupId(), header.getTimestamp());
|
||||
|
||||
this.header = header;
|
||||
body = null;
|
||||
}
|
||||
|
||||
PrivateMessageHeader getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
byte[] getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
void setBody(byte[] body) {
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,26 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationMessageOutItem extends ConversationMessageItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
private boolean sent, seen;
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class ConversationMessageOutItem extends ConversationOutItem {
|
||||
|
||||
ConversationMessageOutItem(PrivateMessageHeader header) {
|
||||
super(header);
|
||||
|
||||
sent = header.isSent();
|
||||
seen = header.isSeen();
|
||||
ConversationMessageOutItem(PrivateMessageHeader h) {
|
||||
super(h.getId(), h.getGroupId(), null, h.getTimestamp(), h.isSent(),
|
||||
h.isSeen());
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
int getType() {
|
||||
return MSG_OUT;
|
||||
public int getLayout() {
|
||||
return R.layout.list_item_conversation_msg_out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
class ConversationMessageOutViewHolder extends ConversationOutItemViewHolder {
|
||||
|
||||
ConversationMessageOutViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasDarkBackground() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +1,43 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationNoticeInItem extends ConversationNoticeItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
private boolean read;
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class ConversationNoticeInItem extends ConversationItem {
|
||||
|
||||
ConversationNoticeInItem(MessageId id, GroupId groupId, String text,
|
||||
long time, boolean read) {
|
||||
super(id, groupId, text, time);
|
||||
@Nullable
|
||||
private final String msgText;
|
||||
|
||||
this.read = read;
|
||||
ConversationNoticeInItem(MessageId id, GroupId groupId,
|
||||
String text, @Nullable String msgText, long time,
|
||||
boolean read) {
|
||||
super(id, groupId, text, time, read);
|
||||
this.msgText = msgText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMsgText() {
|
||||
return msgText;
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return NOTICE_IN;
|
||||
public boolean isIncoming() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
public int getLayout() {
|
||||
return R.layout.list_item_conversation_notice_in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class ConversationNoticeInViewHolder extends ConversationItemViewHolder {
|
||||
|
||||
private final TextView msgText;
|
||||
|
||||
ConversationNoticeInViewHolder(View v) {
|
||||
super(v);
|
||||
msgText = (TextView) v.findViewById(R.id.msgText);
|
||||
}
|
||||
|
||||
@Override
|
||||
void bind(ConversationItem conversationItem) {
|
||||
super.bind(conversationItem);
|
||||
|
||||
ConversationNoticeInItem item =
|
||||
(ConversationNoticeInItem) conversationItem;
|
||||
|
||||
String message = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
msgText.setVisibility(GONE);
|
||||
layout.setBackgroundResource(R.drawable.notice_in);
|
||||
} else {
|
||||
msgText.setVisibility(VISIBLE);
|
||||
msgText.setText(StringUtils.trim(message));
|
||||
layout.setBackgroundResource(R.drawable.notice_in_bottom);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
abstract class ConversationNoticeItem extends ConversationItem {
|
||||
|
||||
private final String text;
|
||||
|
||||
ConversationNoticeItem(MessageId id, GroupId groupId, String text,
|
||||
long time) {
|
||||
super(id, groupId, time);
|
||||
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,38 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationNoticeOutItem extends ConversationNoticeItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
private boolean sent, seen;
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class ConversationNoticeOutItem extends ConversationOutItem {
|
||||
|
||||
ConversationNoticeOutItem(MessageId id, GroupId groupId, String text,
|
||||
long time, boolean sent, boolean seen) {
|
||||
super(id, groupId, text, time);
|
||||
@Nullable
|
||||
private final String msgText;
|
||||
|
||||
this.sent = sent;
|
||||
this.seen = seen;
|
||||
ConversationNoticeOutItem(MessageId id, GroupId groupId,
|
||||
String text, @Nullable String msgText, long time,
|
||||
boolean sent, boolean seen) {
|
||||
super(id, groupId, text, time, sent, seen);
|
||||
this.msgText = msgText;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMsgText() {
|
||||
return msgText;
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
int getType() {
|
||||
return NOTICE_OUT;
|
||||
public int getLayout() {
|
||||
return R.layout.list_item_conversation_notice_out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class ConversationNoticeOutViewHolder extends ConversationOutItemViewHolder {
|
||||
|
||||
private final TextView msgText;
|
||||
|
||||
ConversationNoticeOutViewHolder(View v) {
|
||||
super(v);
|
||||
msgText = (TextView) v.findViewById(R.id.msgText);
|
||||
}
|
||||
|
||||
@Override
|
||||
void bind(ConversationItem conversationItem) {
|
||||
super.bind(conversationItem);
|
||||
|
||||
ConversationNoticeOutItem item =
|
||||
(ConversationNoticeOutItem) conversationItem;
|
||||
|
||||
String message = item.getMsgText();
|
||||
if (StringUtils.isNullOrEmpty(message)) {
|
||||
msgText.setVisibility(GONE);
|
||||
layout.setBackgroundResource(R.drawable.notice_out);
|
||||
} else {
|
||||
msgText.setVisibility(VISIBLE);
|
||||
msgText.setText(StringUtils.trim(message));
|
||||
layout.setBackgroundResource(R.drawable.notice_out_bottom);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasDarkBackground() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
abstract class ConversationOutItem extends ConversationItem {
|
||||
|
||||
private boolean sent, seen;
|
||||
|
||||
ConversationOutItem(MessageId id, GroupId groupId, @Nullable String text,
|
||||
long time, boolean sent, boolean seen) {
|
||||
super(id, groupId, text, time, true);
|
||||
|
||||
this.sent = sent;
|
||||
this.seen = seen;
|
||||
}
|
||||
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIncoming() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
abstract class ConversationOutItemViewHolder
|
||||
extends ConversationItemViewHolder {
|
||||
|
||||
private final ImageView status;
|
||||
|
||||
ConversationOutItemViewHolder(View v) {
|
||||
super(v);
|
||||
status = (ImageView) v.findViewById(R.id.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
void bind(ConversationItem conversationItem) {
|
||||
super.bind(conversationItem);
|
||||
|
||||
ConversationOutItem item = (ConversationOutItem) conversationItem;
|
||||
|
||||
int res;
|
||||
if (item.isSeen()) {
|
||||
if (hasDarkBackground()) res = R.drawable.message_delivered_white;
|
||||
else res = R.drawable.message_delivered;
|
||||
} else if (item.isSent()) {
|
||||
if (hasDarkBackground()) res = R.drawable.message_sent_white;
|
||||
else res = R.drawable.message_sent;
|
||||
} else {
|
||||
if (hasDarkBackground()) res = R.drawable.message_stored_white;
|
||||
else res = R.drawable.message_stored;
|
||||
}
|
||||
status.setImageResource(res);
|
||||
}
|
||||
|
||||
protected abstract boolean hasDarkBackground();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
class ConversationRequestItem extends ConversationNoticeInItem {
|
||||
|
||||
enum RequestType { INTRODUCTION, FORUM, BLOG, GROUP };
|
||||
private final RequestType requestType;
|
||||
private final SessionId sessionId;
|
||||
private boolean answered;
|
||||
|
||||
ConversationRequestItem(MessageId id, GroupId groupId,
|
||||
RequestType requestType, SessionId sessionId, String text,
|
||||
@Nullable String msgText, long time, boolean read,
|
||||
boolean answered) {
|
||||
super(id, groupId, text, msgText, time, read);
|
||||
this.requestType = requestType;
|
||||
this.sessionId = sessionId;
|
||||
this.answered = answered;
|
||||
}
|
||||
|
||||
public RequestType getRequestType() {
|
||||
return requestType;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
boolean wasAnswered() {
|
||||
return answered;
|
||||
}
|
||||
|
||||
void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
|
||||
@LayoutRes
|
||||
@Override
|
||||
public int getLayout() {
|
||||
return R.layout.list_item_conversation_request;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.support.annotation.UiThread;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.contact.ConversationAdapter.RequestListener;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
class ConversationRequestViewHolder extends ConversationNoticeInViewHolder {
|
||||
|
||||
private final Button acceptButton;
|
||||
private final Button declineButton;
|
||||
|
||||
ConversationRequestViewHolder(View v) {
|
||||
super(v);
|
||||
acceptButton = (Button) v.findViewById(R.id.acceptButton);
|
||||
declineButton = (Button) v.findViewById(R.id.declineButton);
|
||||
}
|
||||
|
||||
void bind(ConversationItem conversationItem,
|
||||
final RequestListener listener) {
|
||||
super.bind(conversationItem);
|
||||
|
||||
final ConversationRequestItem item =
|
||||
(ConversationRequestItem) conversationItem;
|
||||
|
||||
if (item.wasAnswered()) {
|
||||
acceptButton.setVisibility(GONE);
|
||||
declineButton.setVisibility(GONE);
|
||||
} else {
|
||||
acceptButton.setVisibility(VISIBLE);
|
||||
acceptButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
item.setAnswered(true);
|
||||
listener.respondToRequest(item, true);
|
||||
}
|
||||
});
|
||||
declineButton.setVisibility(VISIBLE);
|
||||
declineButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
item.setAnswered(true);
|
||||
listener.respondToRequest(item, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.blogs.BlogInvitationRequest;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationShareableInvitationInItem
|
||||
extends ConversationShareableInvitationItem
|
||||
implements ConversationItem.IncomingItem {
|
||||
|
||||
private final int type;
|
||||
private boolean read;
|
||||
|
||||
ConversationShareableInvitationInItem(InvitationRequest ir) {
|
||||
super(ir);
|
||||
|
||||
if (ir instanceof ForumInvitationRequest) {
|
||||
this.type = FORUM_INVITATION_IN;
|
||||
} else if (ir instanceof BlogInvitationRequest) {
|
||||
this.type = BLOG_INVITATION_IN;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown Invitation Type.");
|
||||
}
|
||||
|
||||
this.read = ir.isRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
|
||||
abstract class ConversationShareableInvitationItem extends ConversationItem {
|
||||
|
||||
private final InvitationRequest fim;
|
||||
|
||||
ConversationShareableInvitationItem(InvitationRequest fim) {
|
||||
super(fim.getId(), fim.getGroupId(), fim.getTimestamp());
|
||||
|
||||
this.fim = fim;
|
||||
}
|
||||
|
||||
InvitationRequest getInvitationRequest() {
|
||||
return fim;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import org.briarproject.api.blogs.BlogInvitationRequest;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
|
||||
/**
|
||||
* This class is needed and can not be replaced by an ConversationNoticeOutItem,
|
||||
* because it carries the optional invitation message
|
||||
* to be displayed as a regular private message.
|
||||
* <p/>
|
||||
* This class is not thread-safe
|
||||
*/
|
||||
class ConversationShareableInvitationOutItem
|
||||
extends ConversationShareableInvitationItem
|
||||
implements ConversationItem.OutgoingItem {
|
||||
|
||||
private final int type;
|
||||
private boolean sent, seen;
|
||||
|
||||
ConversationShareableInvitationOutItem(InvitationRequest ir) {
|
||||
super(ir);
|
||||
|
||||
if (ir instanceof ForumInvitationRequest) {
|
||||
this.type = FORUM_INVITATION_OUT;
|
||||
} else if (ir instanceof BlogInvitationRequest) {
|
||||
this.type = BLOG_INVITATION_OUT;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown Invitation Type.");
|
||||
}
|
||||
|
||||
this.sent = ir.isSent();
|
||||
this.seen = ir.isSeen();
|
||||
}
|
||||
|
||||
@Override
|
||||
int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSent(boolean sent) {
|
||||
this.sent = sent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSeen() {
|
||||
return seen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeen(boolean seen) {
|
||||
this.seen = seen;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.sharing.ShareForumActivity;
|
||||
import org.briarproject.android.sharing.SharingStatusForumActivity;
|
||||
import org.briarproject.android.sharing.ForumSharingStatusActivity;
|
||||
import org.briarproject.android.threaded.ThreadListActivity;
|
||||
import org.briarproject.android.threaded.ThreadListController;
|
||||
import org.briarproject.api.db.DbException;
|
||||
@@ -114,7 +114,7 @@ public class ForumActivity extends
|
||||
REQUEST_FORUM_SHARED, options.toBundle());
|
||||
return true;
|
||||
case R.id.action_forum_sharing_status:
|
||||
Intent i3 = new Intent(this, SharingStatusForumActivity.class);
|
||||
Intent i3 = new Intent(this, ForumSharingStatusActivity.class);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||
ActivityCompat.startActivity(this, i3, options.toBundle());
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.api.AndroidNotificationManager;
|
||||
import org.briarproject.android.fragment.BaseEventFragment;
|
||||
import org.briarproject.android.sharing.InvitationsForumActivity;
|
||||
import org.briarproject.android.sharing.ForumInvitationActivity;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.db.DbException;
|
||||
@@ -220,10 +220,10 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
if (availableCount == 0) {
|
||||
snackbar.dismiss();
|
||||
} else {
|
||||
snackbar.show();
|
||||
snackbar.setText(getResources().getQuantityString(
|
||||
R.plurals.forums_shared, availableCount,
|
||||
availableCount));
|
||||
if (!snackbar.isShownOrQueued()) snackbar.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -286,7 +286,7 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
// snackbar click
|
||||
Intent i = new Intent(getContext(), InvitationsForumActivity.class);
|
||||
Intent i = new Intent(getContext(), ForumInvitationActivity.class);
|
||||
startActivity(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package org.briarproject.android.privategroup.invitation;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.sharing.InvitationActivity;
|
||||
import org.briarproject.android.sharing.InvitationAdapter;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
|
||||
public class GroupInvitationActivity
|
||||
extends InvitationActivity<GroupInvitationItem> {
|
||||
|
||||
@Inject
|
||||
protected GroupInvitationController controller;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GroupInvitationController getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter<GroupInvitationItem, ?> getAdapter(Context ctx,
|
||||
InvitationClickListener<GroupInvitationItem> listener) {
|
||||
return new GroupInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.groups_invitations_joined;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.groups_invitations_declined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.briarproject.android.privategroup.invitation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.android.sharing.InvitationAdapter;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
|
||||
class GroupInvitationAdapter extends
|
||||
InvitationAdapter<GroupInvitationItem, GroupInvitationViewHolder> {
|
||||
|
||||
GroupInvitationAdapter(Context ctx,
|
||||
InvitationClickListener<GroupInvitationItem> listener) {
|
||||
super(ctx, GroupInvitationItem.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupInvitationViewHolder onCreateViewHolder(ViewGroup parent,
|
||||
int viewType) {
|
||||
return new GroupInvitationViewHolder(getView(parent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(GroupInvitationItem item1,
|
||||
GroupInvitationItem item2) {
|
||||
return item1.isSubscribed() == item2.isSubscribed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.android.privategroup.invitation;
|
||||
|
||||
import org.briarproject.android.sharing.InvitationController;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
|
||||
public interface GroupInvitationController
|
||||
extends InvitationController<GroupInvitationItem> {
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.briarproject.android.privategroup.invitation;
|
||||
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.android.sharing.InvitationControllerImpl;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.GroupInvitationReceivedEvent;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class GroupInvitationControllerImpl
|
||||
extends InvitationControllerImpl<GroupInvitationItem>
|
||||
implements GroupInvitationController {
|
||||
|
||||
private final PrivateGroupManager privateGroupManager;
|
||||
private final GroupInvitationManager groupInvitationManager;
|
||||
|
||||
@Inject
|
||||
GroupInvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||
PrivateGroupManager privateGroupManager,
|
||||
GroupInvitationManager groupInvitationManager) {
|
||||
super(dbExecutor, lifecycleManager, eventBus);
|
||||
this.privateGroupManager = privateGroupManager;
|
||||
this.groupInvitationManager = groupInvitationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof GroupInvitationReceivedEvent) {
|
||||
LOG.info("Group invitation received, reloading");
|
||||
listener.loadInvitations(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientId getShareableClientId() {
|
||||
return privateGroupManager.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<GroupInvitationItem> getInvitations()
|
||||
throws DbException {
|
||||
return groupInvitationManager.getInvitations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(final GroupInvitationItem item,
|
||||
final boolean accept,
|
||||
final ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
PrivateGroup g = item.getShareable();
|
||||
Contact c = item.getCreator();
|
||||
groupInvitationManager.respondToInvitation(g, c, accept);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.briarproject.android.privategroup.invitation;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
import org.briarproject.android.sharing.InvitationViewHolder;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
|
||||
public class GroupInvitationViewHolder extends InvitationViewHolder<GroupInvitationItem> {
|
||||
|
||||
public GroupInvitationViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(@Nullable final GroupInvitationItem item,
|
||||
final InvitationClickListener<GroupInvitationItem> listener) {
|
||||
super.onBind(item, listener);
|
||||
if (item == null) return;
|
||||
|
||||
sharedBy.setText(
|
||||
sharedBy.getContext().getString(R.string.groups_created_by,
|
||||
item.getCreator().getAuthor().getName()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -30,6 +30,9 @@ public interface GroupListController extends DbController {
|
||||
void removeGroup(GroupId g,
|
||||
ResultExceptionHandler<Void, DbException> result);
|
||||
|
||||
void loadAvailableGroups(
|
||||
ResultExceptionHandler<Integer, DbException> result);
|
||||
|
||||
interface GroupListListener extends DestroyableContext {
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
@@ -41,6 +42,7 @@ public class GroupListControllerImpl extends DbControllerImpl
|
||||
Logger.getLogger(GroupListControllerImpl.class.getName());
|
||||
|
||||
private final PrivateGroupManager groupManager;
|
||||
private final GroupInvitationManager groupInvitationManager;
|
||||
private final EventBus eventBus;
|
||||
private final AndroidNotificationManager notificationManager;
|
||||
private final IdentityManager identityManager;
|
||||
@@ -50,10 +52,12 @@ public class GroupListControllerImpl extends DbControllerImpl
|
||||
@Inject
|
||||
GroupListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, PrivateGroupManager groupManager,
|
||||
EventBus eventBus, AndroidNotificationManager notificationManager,
|
||||
GroupInvitationManager groupInvitationManager, EventBus eventBus,
|
||||
AndroidNotificationManager notificationManager,
|
||||
IdentityManager identityManager) {
|
||||
super(dbExecutor, lifecycleManager);
|
||||
this.groupManager = groupManager;
|
||||
this.groupInvitationManager = groupInvitationManager;
|
||||
this.eventBus = eventBus;
|
||||
this.notificationManager = notificationManager;
|
||||
this.identityManager = identityManager;
|
||||
@@ -187,4 +191,22 @@ public class GroupListControllerImpl extends DbControllerImpl
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAvailableGroups(
|
||||
final ResultExceptionHandler<Integer, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
handler.onResult(
|
||||
groupInvitationManager.getInvitations().size());
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.R;
|
||||
@@ -18,6 +21,7 @@ import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.fragment.BaseFragment;
|
||||
import org.briarproject.android.privategroup.creation.CreateGroupActivity;
|
||||
import org.briarproject.android.privategroup.invitation.GroupInvitationActivity;
|
||||
import org.briarproject.android.privategroup.list.GroupListController.GroupListListener;
|
||||
import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
@@ -30,10 +34,11 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
|
||||
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||
|
||||
public class GroupListFragment extends BaseFragment implements
|
||||
GroupListListener, OnGroupRemoveClickListener {
|
||||
GroupListListener, OnGroupRemoveClickListener, OnClickListener {
|
||||
|
||||
public final static String TAG = GroupListFragment.class.getName();
|
||||
private static final Logger LOG = Logger.getLogger(TAG);
|
||||
@@ -47,6 +52,7 @@ public class GroupListFragment extends BaseFragment implements
|
||||
|
||||
private BriarRecyclerView list;
|
||||
private GroupListAdapter adapter;
|
||||
private Snackbar snackbar;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -61,6 +67,12 @@ public class GroupListFragment extends BaseFragment implements
|
||||
list.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
list.setAdapter(adapter);
|
||||
|
||||
snackbar = Snackbar.make(list, "", LENGTH_INDEFINITE);
|
||||
snackbar.getView().setBackgroundResource(R.color.briar_primary);
|
||||
snackbar.setAction(R.string.show, this);
|
||||
snackbar.setActionTextColor(ContextCompat
|
||||
.getColor(getContext(), R.color.briar_button_positive));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -76,6 +88,7 @@ public class GroupListFragment extends BaseFragment implements
|
||||
controller.onStart();
|
||||
list.startPeriodicUpdate();
|
||||
loadGroups();
|
||||
loadAvailableGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,4 +193,40 @@ public class GroupListFragment extends BaseFragment implements
|
||||
});
|
||||
}
|
||||
|
||||
private void loadAvailableGroups() {
|
||||
controller.loadAvailableGroups(
|
||||
new UiResultExceptionHandler<Integer, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Integer num) {
|
||||
if (num == 0) {
|
||||
snackbar.dismiss();
|
||||
} else {
|
||||
snackbar.setText(getResources().getQuantityString(
|
||||
R.plurals.groups_invitations_open, num,
|
||||
num));
|
||||
if (!snackbar.isShownOrQueued()) snackbar.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO handle this error
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is handling the available groups snackbar action
|
||||
*/
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(getContext(), GroupInvitationActivity.class);
|
||||
ActivityOptionsCompat options =
|
||||
makeCustomAnimation(getActivity(),
|
||||
android.R.anim.slide_in_left,
|
||||
android.R.anim.slide_out_right);
|
||||
startActivity(i, options.toBundle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
|
||||
public class BlogInvitationActivity
|
||||
extends InvitationActivity<SharingInvitationItem> {
|
||||
|
||||
@Inject
|
||||
BlogInvitationController controller;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationController<SharingInvitationItem> getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter<SharingInvitationItem, ?> getAdapter(
|
||||
Context ctx,
|
||||
InvitationClickListener<SharingInvitationItem> listener) {
|
||||
return new SharingInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.blogs_sharing_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.blogs_sharing_declined_toast;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
class BlogInvitationAdapter extends InvitationAdapter {
|
||||
|
||||
BlogInvitationAdapter(Context ctx, AvailableForumClickListener listener) {
|
||||
super(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InvitationsViewHolder ui, int position) {
|
||||
super.onBindViewHolder(ui, position);
|
||||
InvitationItem item = getItemAt(position);
|
||||
if (item == null) return;
|
||||
|
||||
Blog blog = (Blog) item.getShareable();
|
||||
|
||||
ui.avatar.setAuthorAvatar(blog.getAuthor());
|
||||
|
||||
ui.name.setText(ctx.getString(R.string.blogs_personal_blog,
|
||||
blog.getAuthor().getName()));
|
||||
|
||||
if (item.isSubscribed()) {
|
||||
ui.subscribed.setText(ctx.getString(R.string.blogs_sharing_exists));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(InvitationItem o1, InvitationItem o2) {
|
||||
return String.CASE_INSENSITIVE_ORDER
|
||||
.compare(((Blog) o1.getShareable()).getAuthor().getName(),
|
||||
((Blog) o2.getShareable()).getAuthor().getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
|
||||
public interface BlogInvitationController
|
||||
extends InvitationController<SharingInvitationItem> {
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogSharingManager;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.BlogInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class BlogInvitationControllerImpl
|
||||
extends InvitationControllerImpl<SharingInvitationItem>
|
||||
implements BlogInvitationController {
|
||||
|
||||
private final BlogManager blogManager;
|
||||
private final BlogSharingManager blogSharingManager;
|
||||
|
||||
@Inject
|
||||
BlogInvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||
BlogManager blogManager, BlogSharingManager blogSharingManager) {
|
||||
super(dbExecutor, lifecycleManager, eventBus);
|
||||
this.blogManager = blogManager;
|
||||
this.blogSharingManager = blogSharingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof BlogInvitationReceivedEvent) {
|
||||
LOG.info("Blog invitation received, reloading");
|
||||
listener.loadInvitations(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientId getShareableClientId() {
|
||||
return blogManager.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<SharingInvitationItem> getInvitations() throws DbException {
|
||||
return blogSharingManager.getInvitations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(final SharingInvitationItem item,
|
||||
final boolean accept,
|
||||
final ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Blog f = (Blog) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
blogSharingManager.respondToInvitation(f, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class SharingStatusBlogActivity extends SharingStatusActivity {
|
||||
public class BlogSharingStatusActivity extends SharingStatusActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
|
||||
public class ForumInvitationActivity
|
||||
extends InvitationActivity<SharingInvitationItem> {
|
||||
|
||||
@Inject
|
||||
ForumInvitationController controller;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationController<SharingInvitationItem> getController() {
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter<SharingInvitationItem, ?> getAdapter(
|
||||
Context ctx,
|
||||
InvitationClickListener<SharingInvitationItem> listener) {
|
||||
return new SharingInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.forum_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.forum_declined_toast;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
class ForumInvitationAdapter extends InvitationAdapter {
|
||||
|
||||
ForumInvitationAdapter(Context ctx, AvailableForumClickListener listener) {
|
||||
super(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InvitationsViewHolder ui, int position) {
|
||||
super.onBindViewHolder(ui, position);
|
||||
InvitationItem item = getItemAt(position);
|
||||
if (item == null) return;
|
||||
|
||||
Forum forum = (Forum) item.getShareable();
|
||||
|
||||
ui.avatar.setText(forum.getName().substring(0, 1));
|
||||
ui.avatar.setBackgroundBytes(item.getShareable().getId().getBytes());
|
||||
|
||||
ui.name.setText(forum.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(InvitationItem o1, InvitationItem o2) {
|
||||
return String.CASE_INSENSITIVE_ORDER
|
||||
.compare(((Forum) o1.getShareable()).getName(),
|
||||
((Forum) o2.getShareable()).getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
|
||||
public interface ForumInvitationController
|
||||
extends InvitationController<SharingInvitationItem> {
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.db.DatabaseExecutor;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public class ForumInvitationControllerImpl
|
||||
extends InvitationControllerImpl<SharingInvitationItem>
|
||||
implements ForumInvitationController {
|
||||
|
||||
private final ForumManager forumManager;
|
||||
private final ForumSharingManager forumSharingManager;
|
||||
|
||||
@Inject
|
||||
ForumInvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, EventBus eventBus,
|
||||
ForumManager forumManager,
|
||||
ForumSharingManager forumSharingManager) {
|
||||
super(dbExecutor, lifecycleManager, eventBus);
|
||||
this.forumManager = forumManager;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof ForumInvitationReceivedEvent) {
|
||||
LOG.info("Forum invitation received, reloading");
|
||||
listener.loadInvitations(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientId getShareableClientId() {
|
||||
return forumManager.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<SharingInvitationItem> getInvitations() throws DbException {
|
||||
return forumSharingManager.getInvitations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(final SharingInvitationItem item,
|
||||
final boolean accept,
|
||||
final ResultExceptionHandler<Void, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Forum f = (Forum) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
forumSharingManager.respondToInvitation(f, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class SharingStatusForumActivity extends SharingStatusActivity {
|
||||
public class ForumSharingStatusActivity extends SharingStatusActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
@@ -2,39 +2,34 @@ package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.android.sharing.InvitationController.InvitationListener;
|
||||
import org.briarproject.android.view.BriarRecyclerView;
|
||||
import org.briarproject.api.event.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
|
||||
abstract class InvitationsActivity extends BriarActivity
|
||||
implements EventListener, AvailableForumClickListener {
|
||||
public abstract class InvitationActivity<I extends InvitationItem>
|
||||
extends BriarActivity
|
||||
implements InvitationListener, InvitationClickListener<I> {
|
||||
|
||||
protected static final Logger LOG =
|
||||
Logger.getLogger(InvitationsActivity.class.getName());
|
||||
Logger.getLogger(InvitationActivity.class.getName());
|
||||
|
||||
protected InvitationAdapter adapter;
|
||||
private InvitationAdapter<I, ?> adapter;
|
||||
private BriarRecyclerView list;
|
||||
|
||||
@Inject
|
||||
EventBus eventBus;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
@@ -42,7 +37,6 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
setContentView(R.layout.list);
|
||||
|
||||
adapter = getAdapter(this, this);
|
||||
|
||||
list = (BriarRecyclerView) findViewById(R.id.list);
|
||||
if (list != null) {
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
@@ -50,32 +44,24 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected InvitationAdapter<I, ?> getAdapter(Context ctx,
|
||||
InvitationClickListener<I> listener);
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
loadInvitations(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
eventBus.removeListener(this);
|
||||
adapter.clear();
|
||||
list.showProgressBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactRemovedEvent) {
|
||||
LOG.info("Contact removed, reloading...");
|
||||
loadInvitations(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(InvitationItem item, boolean accept) {
|
||||
public void onItemClick(I item, boolean accept) {
|
||||
respondToInvitation(item, accept);
|
||||
|
||||
// show toast
|
||||
@@ -91,26 +77,58 @@ abstract class InvitationsActivity extends BriarActivity
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected InvitationAdapter getAdapter(Context ctx,
|
||||
AvailableForumClickListener listener);
|
||||
@Override
|
||||
public void loadInvitations(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
getController().loadInvitations(clear,
|
||||
new UiResultExceptionHandler<Collection<I>, DbException>(
|
||||
this) {
|
||||
@Override
|
||||
public void onResultUi(Collection<I> items) {
|
||||
displayInvitations(revision, items, clear);
|
||||
}
|
||||
|
||||
abstract protected void loadInvitations(boolean clear);
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO proper error handling
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
abstract protected void respondToInvitation(final InvitationItem item,
|
||||
final boolean accept);
|
||||
abstract protected InvitationController<I> getController();
|
||||
|
||||
protected void respondToInvitation(final I item,
|
||||
final boolean accept) {
|
||||
getController().respondToInvitation(item, accept,
|
||||
new UiResultExceptionHandler<Void, DbException>(this) {
|
||||
@Override
|
||||
public void onResultUi(Void result) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExceptionUi(DbException exception) {
|
||||
// TODO proper error handling
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@StringRes
|
||||
abstract protected int getAcceptRes();
|
||||
|
||||
@StringRes
|
||||
abstract protected int getDeclineRes();
|
||||
|
||||
protected void displayInvitations(final int revision,
|
||||
final Collection<InvitationItem> invitations, final boolean clear) {
|
||||
final Collection<I> invitations, final boolean clear) {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (invitations.isEmpty()) {
|
||||
LOG.info("No more invitations available, finishing");
|
||||
finish();
|
||||
supportFinishAfterTransition();
|
||||
} else if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (clear) adapter.setItems(invitations);
|
||||
@@ -1,111 +1,51 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.util.BriarAdapter;
|
||||
import org.briarproject.android.view.TextAvatarView;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
public abstract class InvitationAdapter<I extends InvitationItem, VH extends InvitationViewHolder<I>>
|
||||
extends BriarAdapter<I, VH> {
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
private final InvitationClickListener<I> listener;
|
||||
|
||||
abstract class InvitationAdapter extends
|
||||
BriarAdapter<InvitationItem, InvitationAdapter.InvitationsViewHolder> {
|
||||
|
||||
private final AvailableForumClickListener listener;
|
||||
|
||||
InvitationAdapter(Context ctx, AvailableForumClickListener listener) {
|
||||
super(ctx, InvitationItem.class);
|
||||
public InvitationAdapter(Context ctx, Class<I> c,
|
||||
InvitationClickListener<I> listener) {
|
||||
super(ctx, c);
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvitationsViewHolder onCreateViewHolder(ViewGroup parent,
|
||||
int viewType) {
|
||||
|
||||
View v = LayoutInflater.from(ctx)
|
||||
.inflate(R.layout.list_item_invitations, parent, false);
|
||||
return new InvitationsViewHolder(v);
|
||||
protected View getView(ViewGroup parent) {
|
||||
return LayoutInflater.from(ctx)
|
||||
.inflate(R.layout.list_item_invitations, parent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InvitationsViewHolder ui, int position) {
|
||||
final InvitationItem item = getItemAt(position);
|
||||
public void onBindViewHolder(VH ui, int position) {
|
||||
final I item = getItemAt(position);
|
||||
if (item == null) return;
|
||||
|
||||
Collection<String> names = new ArrayList<>();
|
||||
for (Contact c : item.getNewSharers())
|
||||
names.add(c.getAuthor().getName());
|
||||
ui.sharedBy.setText(ctx.getString(R.string.shared_by_format,
|
||||
StringUtils.join(names, ", ")));
|
||||
|
||||
if (item.isSubscribed()) {
|
||||
ui.subscribed.setVisibility(VISIBLE);
|
||||
} else {
|
||||
ui.subscribed.setVisibility(GONE);
|
||||
}
|
||||
|
||||
ui.accept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(item, true);
|
||||
}
|
||||
});
|
||||
ui.decline.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(item, false);
|
||||
}
|
||||
});
|
||||
ui.onBind(item, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(InvitationItem oldItem,
|
||||
InvitationItem newItem) {
|
||||
return oldItem.isSubscribed() == newItem.isSubscribed() &&
|
||||
oldItem.getNewSharers().equals(newItem.getNewSharers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(InvitationItem oldItem,
|
||||
InvitationItem newItem) {
|
||||
public boolean areItemsTheSame(I oldItem, I newItem) {
|
||||
return oldItem.getShareable().equals(newItem.getShareable());
|
||||
}
|
||||
|
||||
static class InvitationsViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
final TextAvatarView avatar;
|
||||
final TextView name;
|
||||
private final TextView sharedBy;
|
||||
final TextView subscribed;
|
||||
private final Button accept;
|
||||
private final Button decline;
|
||||
|
||||
private InvitationsViewHolder(View v) {
|
||||
super(v);
|
||||
|
||||
avatar = (TextAvatarView) v.findViewById(R.id.avatarView);
|
||||
name = (TextView) v.findViewById(R.id.forumNameView);
|
||||
sharedBy = (TextView) v.findViewById(R.id.sharedByView);
|
||||
subscribed = (TextView) v.findViewById(R.id.forumSubscribedView);
|
||||
accept = (Button) v.findViewById(R.id.acceptButton);
|
||||
decline = (Button) v.findViewById(R.id.declineButton);
|
||||
}
|
||||
@Override
|
||||
public int compare(I o1, I o2) {
|
||||
return String.CASE_INSENSITIVE_ORDER
|
||||
.compare((o1.getShareable()).getName(),
|
||||
(o2.getShareable()).getName());
|
||||
}
|
||||
|
||||
interface AvailableForumClickListener {
|
||||
void onItemClick(InvitationItem item, boolean accept);
|
||||
public interface InvitationClickListener<I> {
|
||||
void onItemClick(I item, boolean accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import org.briarproject.android.controller.ActivityLifecycleController;
|
||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface InvitationController<I extends InvitationItem>
|
||||
extends ActivityLifecycleController {
|
||||
|
||||
void loadInvitations(boolean clear,
|
||||
ResultExceptionHandler<Collection<I>, DbException> handler);
|
||||
|
||||
void respondToInvitation(I item, boolean accept,
|
||||
ResultExceptionHandler<Void, DbException> handler);
|
||||
|
||||
interface InvitationListener {
|
||||
|
||||
void loadInvitations(boolean clear);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.CallSuper;
|
||||
|
||||
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.event.ContactRemovedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.GroupAddedEvent;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
public abstract class InvitationControllerImpl<I extends InvitationItem>
|
||||
extends DbControllerImpl
|
||||
implements InvitationController<I>, EventListener {
|
||||
|
||||
protected static final Logger LOG =
|
||||
Logger.getLogger(InvitationControllerImpl.class.getName());
|
||||
|
||||
private final EventBus eventBus;
|
||||
protected InvitationListener listener;
|
||||
|
||||
public InvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, EventBus eventBus) {
|
||||
super(dbExecutor, lifecycleManager);
|
||||
this.eventBus = eventBus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreate(Activity activity) {
|
||||
listener = (InvitationListener) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStart() {
|
||||
eventBus.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStop() {
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroy() {
|
||||
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof ContactRemovedEvent) {
|
||||
LOG.info("Contact removed, reloading...");
|
||||
listener.loadInvitations(true);
|
||||
} else if (e instanceof GroupAddedEvent) {
|
||||
GroupAddedEvent g = (GroupAddedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(getShareableClientId())) {
|
||||
LOG.info("Group added, reloading");
|
||||
listener.loadInvitations(false);
|
||||
}
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(getShareableClientId())) {
|
||||
LOG.info("Group removed, reloading");
|
||||
listener.loadInvitations(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract ClientId getShareableClientId();
|
||||
|
||||
@Override
|
||||
public void loadInvitations(final boolean clear,
|
||||
final ResultExceptionHandler<Collection<I>, DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<I> invitations = new ArrayList<>();
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
invitations.addAll(getInvitations());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(
|
||||
"Loading invitations took " + duration + " ms");
|
||||
handler.onResult(invitations);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
protected abstract Collection<I> getInvitations() throws DbException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
|
||||
import org.briarproject.android.view.TextAvatarView;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
|
||||
public class InvitationViewHolder<I extends InvitationItem>
|
||||
extends RecyclerView.ViewHolder {
|
||||
|
||||
private final TextAvatarView avatar;
|
||||
private final TextView name;
|
||||
protected final TextView sharedBy;
|
||||
private final TextView subscribed;
|
||||
private final Button accept;
|
||||
private final Button decline;
|
||||
|
||||
public InvitationViewHolder(View v) {
|
||||
super(v);
|
||||
|
||||
avatar = (TextAvatarView) v.findViewById(R.id.avatarView);
|
||||
name = (TextView) v.findViewById(R.id.forumNameView);
|
||||
sharedBy = (TextView) v.findViewById(R.id.sharedByView);
|
||||
subscribed = (TextView) v.findViewById(R.id.forumSubscribedView);
|
||||
accept = (Button) v.findViewById(R.id.acceptButton);
|
||||
decline = (Button) v.findViewById(R.id.declineButton);
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
public void onBind(@Nullable final I item,
|
||||
final InvitationClickListener<I> listener) {
|
||||
if (item == null) return;
|
||||
|
||||
avatar.setText(item.getShareable().getName().substring(0, 1));
|
||||
avatar.setBackgroundBytes(item.getShareable().getId().getBytes());
|
||||
|
||||
name.setText(item.getShareable().getName());
|
||||
|
||||
if (item.isSubscribed()) {
|
||||
subscribed.setVisibility(VISIBLE);
|
||||
} else {
|
||||
subscribed.setVisibility(GONE);
|
||||
}
|
||||
|
||||
accept.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(item, true);
|
||||
}
|
||||
});
|
||||
decline.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onItemClick(item, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogSharingManager;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.BlogInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.GroupAddedEvent;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
|
||||
|
||||
public class InvitationsBlogActivity extends InvitationsActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
volatile BlogManager blogManager;
|
||||
@Inject
|
||||
volatile BlogSharingManager blogSharingManager;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof GroupAddedEvent) {
|
||||
GroupAddedEvent g = (GroupAddedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(blogManager.getClientId())) {
|
||||
LOG.info("Blog added, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(blogManager.getClientId())) {
|
||||
LOG.info("Blog removed, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
} else if (e instanceof BlogInvitationReceivedEvent) {
|
||||
LOG.info("Blog invitation received, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter getAdapter(Context ctx,
|
||||
AvailableForumClickListener listener) {
|
||||
return new BlogInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadInvitations(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
invitations.addAll(blogSharingManager.getInvitations());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayInvitations(revision, invitations, clear);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void respondToInvitation(final InvitationItem item,
|
||||
final boolean accept) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Blog b = (Blog) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
blogSharingManager.respondToInvitation(b, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.blogs_sharing_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.blogs_sharing_declined_toast;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.ActivityComponent;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.GroupAddedEvent;
|
||||
import org.briarproject.api.event.GroupRemovedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.sharing.InvitationAdapter.AvailableForumClickListener;
|
||||
|
||||
public class InvitationsForumActivity extends InvitationsActivity {
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
volatile ForumManager forumManager;
|
||||
@Inject
|
||||
volatile ForumSharingManager forumSharingManager;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
super.eventOccurred(e);
|
||||
|
||||
if (e instanceof GroupAddedEvent) {
|
||||
GroupAddedEvent g = (GroupAddedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(forumManager.getClientId())) {
|
||||
LOG.info("Forum added, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
} else if (e instanceof GroupRemovedEvent) {
|
||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||
ClientId cId = g.getGroup().getClientId();
|
||||
if (cId.equals(forumManager.getClientId())) {
|
||||
LOG.info("Forum removed, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||
LOG.info("Forum invitation received, reloading");
|
||||
loadInvitations(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationAdapter getAdapter(Context ctx,
|
||||
AvailableForumClickListener listener) {
|
||||
return new ForumInvitationAdapter(ctx, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadInvitations(final boolean clear) {
|
||||
final int revision = adapter.getRevision();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Collection<InvitationItem> invitations = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
invitations.addAll(forumSharingManager.getInvitations());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayInvitations(revision, invitations, clear);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void respondToInvitation(final InvitationItem item,
|
||||
final boolean accept) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Forum f = (Forum) item.getShareable();
|
||||
for (Contact c : item.getNewSharers()) {
|
||||
// TODO: What happens if a contact has been removed?
|
||||
forumSharingManager.respondToInvitation(f, c, accept);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getAcceptRes() {
|
||||
return R.string.forum_joined_toast;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getDeclineRes() {
|
||||
return R.string.forum_declined_toast;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
|
||||
class SharingInvitationAdapter extends
|
||||
InvitationAdapter<SharingInvitationItem, SharingInvitationViewHolder> {
|
||||
|
||||
SharingInvitationAdapter(Context ctx, InvitationClickListener listener) {
|
||||
super(ctx, SharingInvitationItem.class, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharingInvitationViewHolder onCreateViewHolder(
|
||||
ViewGroup parent,
|
||||
int viewType) {
|
||||
return new SharingInvitationViewHolder(getView(parent));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(SharingInvitationItem oldItem,
|
||||
SharingInvitationItem newItem) {
|
||||
return oldItem.isSubscribed() == newItem.isSubscribed() &&
|
||||
oldItem.getNewSharers().equals(newItem.getNewSharers());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.android.sharing;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class SharingInvitationViewHolder
|
||||
extends InvitationViewHolder<SharingInvitationItem> {
|
||||
|
||||
public SharingInvitationViewHolder(View v) {
|
||||
super(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(@Nullable final SharingInvitationItem item,
|
||||
final InvitationAdapter.InvitationClickListener<SharingInvitationItem> listener) {
|
||||
super.onBind(item, listener);
|
||||
if (item == null) return;
|
||||
|
||||
Collection<String> names = new ArrayList<>();
|
||||
for (Contact c : item.getNewSharers())
|
||||
names.add(c.getAuthor().getName());
|
||||
sharedBy.setText(
|
||||
sharedBy.getContext().getString(R.string.shared_by_format,
|
||||
StringUtils.join(names, ", ")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,26 @@
|
||||
package org.briarproject.api.clients;
|
||||
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class BaseMessage {
|
||||
|
||||
private final Message message;
|
||||
@Nullable
|
||||
private final MessageId parent;
|
||||
|
||||
public BaseMessage(@NotNull Message message, @Nullable MessageId parent) {
|
||||
public BaseMessage(Message message, @Nullable MessageId parent) {
|
||||
this.message = message;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -5,17 +5,11 @@ import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
|
||||
public class BlogInvitationReceivedEvent extends
|
||||
InvitationRequestReceivedEvent {
|
||||
|
||||
private final Blog blog;
|
||||
InvitationRequestReceivedEvent<Blog> {
|
||||
|
||||
public BlogInvitationReceivedEvent(Blog blog, ContactId contactId,
|
||||
InvitationRequest request) {
|
||||
super(contactId, request);
|
||||
this.blog = blog;
|
||||
super(blog, contactId, request);
|
||||
}
|
||||
|
||||
public Blog getBlog() {
|
||||
return blog;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,11 @@ import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
|
||||
public class ForumInvitationReceivedEvent extends
|
||||
InvitationRequestReceivedEvent {
|
||||
|
||||
private final Forum forum;
|
||||
InvitationRequestReceivedEvent<Forum> {
|
||||
|
||||
public ForumInvitationReceivedEvent(Forum forum, ContactId contactId,
|
||||
ForumInvitationRequest request) {
|
||||
super(contactId, request);
|
||||
this.forum = forum;
|
||||
}
|
||||
|
||||
public Forum getForum() {
|
||||
return forum;
|
||||
super(forum, contactId, request);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.api.event;
|
||||
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationRequest;
|
||||
|
||||
public class GroupInvitationReceivedEvent extends
|
||||
InvitationRequestReceivedEvent<PrivateGroup> {
|
||||
|
||||
public GroupInvitationReceivedEvent(PrivateGroup group, ContactId contactId,
|
||||
GroupInvitationRequest request) {
|
||||
super(group, contactId, request);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,14 +2,18 @@ package org.briarproject.api.event;
|
||||
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
|
||||
public abstract class InvitationRequestReceivedEvent extends Event {
|
||||
public abstract class InvitationRequestReceivedEvent<S extends Shareable>
|
||||
extends Event {
|
||||
|
||||
private final S shareable;
|
||||
private final ContactId contactId;
|
||||
private final InvitationRequest request;
|
||||
|
||||
InvitationRequestReceivedEvent(ContactId contactId,
|
||||
InvitationRequestReceivedEvent(S shareable, ContactId contactId,
|
||||
InvitationRequest request) {
|
||||
this.shareable = shareable;
|
||||
this.contactId = contactId;
|
||||
this.request = request;
|
||||
}
|
||||
@@ -21,4 +25,8 @@ public abstract class InvitationRequestReceivedEvent extends Event {
|
||||
public InvitationRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
public S getShareable() {
|
||||
return shareable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,15 @@ import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ForumInvitationRequest extends InvitationRequest {
|
||||
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, String forumName, String message,
|
||||
boolean available, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
GroupId groupId, ContactId contactId, String forumName,
|
||||
String message, boolean available, long time, boolean local,
|
||||
boolean sent, boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, message, available, time,
|
||||
local, sent, seen, read);
|
||||
|
||||
@@ -2,17 +2,22 @@ package org.briarproject.api.privategroup;
|
||||
|
||||
import org.briarproject.api.clients.BaseMessage;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupMessage extends BaseMessage {
|
||||
|
||||
private final Author author;
|
||||
|
||||
public GroupMessage(@NotNull Message message, @Nullable MessageId parent,
|
||||
@NotNull Author author) {
|
||||
public GroupMessage(Message message, @Nullable MessageId parent,
|
||||
Author author) {
|
||||
super(message, parent);
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.api.privategroup;
|
||||
import org.briarproject.api.clients.NamedGroup;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -10,7 +11,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class PrivateGroup extends NamedGroup {
|
||||
public class PrivateGroup extends NamedGroup implements Shareable {
|
||||
|
||||
private final Author author;
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.api.privategroup.invitation;
|
||||
|
||||
public interface GroupInvitationConstants {
|
||||
|
||||
// Group Metadata Keys
|
||||
String CONTACT_ID = "contactId";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.briarproject.api.privategroup.invitation;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationItem extends InvitationItem<PrivateGroup> {
|
||||
|
||||
private final Contact creator;
|
||||
|
||||
public GroupInvitationItem(PrivateGroup shareable, boolean subscribed,
|
||||
Contact creator) {
|
||||
super(shareable, subscribed);
|
||||
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public Contact getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.briarproject.api.privategroup.invitation;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface GroupInvitationManager extends MessageTracker {
|
||||
|
||||
/** Returns the unique ID of the private group invitation client. */
|
||||
ClientId getClientId();
|
||||
|
||||
/**
|
||||
* Sends an invitation to share the given forum with the given contact
|
||||
* and sends an optional message along with it.
|
||||
*/
|
||||
void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
String message) throws DbException;
|
||||
|
||||
/**
|
||||
* Responds to a pending private group invitation
|
||||
*/
|
||||
void respondToInvitation(PrivateGroup g, Contact c, boolean accept)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Responds to a pending private group invitation
|
||||
*/
|
||||
void respondToInvitation(SessionId id, boolean accept) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all private group invitation messages related to the contact
|
||||
* identified by contactId.
|
||||
*/
|
||||
Collection<InvitationMessage> getInvitationMessages(
|
||||
ContactId contactId) throws DbException;
|
||||
|
||||
/** Returns all private groups to which the user has been invited. */
|
||||
Collection<GroupInvitationItem> getInvitations() throws DbException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.briarproject.api.privategroup.invitation;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sharing.InvitationRequest;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationRequest extends InvitationRequest {
|
||||
|
||||
private final String groupName;
|
||||
private final Author creator;
|
||||
|
||||
public GroupInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, Author creator, ContactId contactId,
|
||||
String groupName, String message, boolean available, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, message, available, time,
|
||||
local, sent, seen, read);
|
||||
this.groupName = groupName;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public Author getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.api.privategroup.invitation;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sharing.InvitationResponse;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationResponse extends InvitationResponse {
|
||||
|
||||
private final String groupName;
|
||||
private final Author creator;
|
||||
|
||||
public GroupInvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, String groupName, Author creator,
|
||||
ContactId contactId, boolean accept, long time, boolean local,
|
||||
boolean sent, boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, accept, time, local, sent,
|
||||
seen, read);
|
||||
this.groupName = groupName;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public Author getCreator() {
|
||||
return creator;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +1,36 @@
|
||||
package org.briarproject.api.sharing;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.Collection;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
public class InvitationItem {
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationItem<S extends Shareable> {
|
||||
|
||||
private final Shareable shareable;
|
||||
private final S shareable;
|
||||
private final boolean subscribed;
|
||||
private final Collection<Contact> newSharers;
|
||||
|
||||
public InvitationItem(Shareable shareable, boolean subscribed,
|
||||
Collection<Contact> newSharers) {
|
||||
|
||||
public InvitationItem(S shareable, boolean subscribed) {
|
||||
this.shareable = shareable;
|
||||
this.subscribed = subscribed;
|
||||
this.newSharers = newSharers;
|
||||
}
|
||||
|
||||
public Shareable getShareable() {
|
||||
public S getShareable() {
|
||||
return shareable;
|
||||
}
|
||||
|
||||
public GroupId getId() {
|
||||
return shareable.getId();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return shareable.getName();
|
||||
}
|
||||
|
||||
public boolean isSubscribed() {
|
||||
return subscribed;
|
||||
}
|
||||
|
||||
public Collection<Contact> getNewSharers() {
|
||||
return newSharers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@ public interface Shareable {
|
||||
GroupId getId();
|
||||
|
||||
Group getGroup();
|
||||
|
||||
String getName();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.briarproject.api.sharing;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class SharingInvitationItem extends InvitationItem<Shareable> {
|
||||
|
||||
private final Collection<Contact> newSharers;
|
||||
|
||||
public SharingInvitationItem(Shareable shareable, boolean subscribed,
|
||||
Collection<Contact> newSharers) {
|
||||
super(shareable, subscribed);
|
||||
|
||||
this.newSharers = newSharers;
|
||||
}
|
||||
|
||||
public Collection<Contact> getNewSharers() {
|
||||
return newSharers;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.api.sharing;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
@@ -30,7 +31,14 @@ public interface SharingManager<S extends Shareable> extends MessageTracker {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all group sharing messages sent by the given contact.
|
||||
* Responds to a pending group invitation
|
||||
*/
|
||||
void respondToInvitation(SessionId id, boolean accept)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all group sharing messages sent by the Contact
|
||||
* identified by contactId.
|
||||
*/
|
||||
Collection<InvitationMessage> getInvitationMessages(
|
||||
ContactId contactId) throws DbException;
|
||||
@@ -38,7 +46,7 @@ public interface SharingManager<S extends Shareable> extends MessageTracker {
|
||||
/**
|
||||
* Returns all invitations to groups.
|
||||
*/
|
||||
Collection<InvitationItem> getInvitations() throws DbException;
|
||||
Collection<SharingInvitationItem> getInvitations() throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all contacts who are sharing the given group with us.
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package org.briarproject.privategroup;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.messaging.ConversationManager;
|
||||
import org.briarproject.api.privategroup.GroupMessageFactory;
|
||||
import org.briarproject.api.privategroup.PrivateGroupFactory;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.sync.ValidationManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import org.briarproject.privategroup.invitation.GroupInvitationManagerImpl;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -25,6 +27,8 @@ public class PrivateGroupModule {
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
GroupMessageValidator groupMessageValidator;
|
||||
@Inject
|
||||
GroupInvitationManager groupInvitationManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -65,4 +69,22 @@ public class PrivateGroupModule {
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
GroupInvitationManager provideGroupInvitationManager(
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
GroupInvitationManagerImpl groupInvitationManager,
|
||||
ConversationManager conversationManager,
|
||||
ValidationManager validationManager) {
|
||||
|
||||
validationManager.registerIncomingMessageHook(
|
||||
groupInvitationManager.getClientId(), groupInvitationManager);
|
||||
lifecycleManager.registerClient(groupInvitationManager);
|
||||
contactManager.registerAddContactHook(groupInvitationManager);
|
||||
contactManager.registerRemoveContactHook(groupInvitationManager);
|
||||
conversationManager.registerConversationClient(groupInvitationManager);
|
||||
|
||||
return groupInvitationManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package org.briarproject.privategroup.invitation;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.Client;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.ContactGroupFactory;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.messaging.ConversationManager;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.clients.ConversationClientImpl;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.privategroup.invitation.GroupInvitationConstants.CONTACT_ID;
|
||||
|
||||
public class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
implements GroupInvitationManager, Client,
|
||||
ContactManager.AddContactHook, ContactManager.RemoveContactHook,
|
||||
ConversationManager.ConversationClient {
|
||||
|
||||
private static final ClientId CLIENT_ID =
|
||||
new ClientId(StringUtils.fromHexString(
|
||||
"B55231ABFC4A10666CD93D649B1D7F4F"
|
||||
+ "016E65B87BB4C04F4E35613713DBCD13"));
|
||||
|
||||
private final ContactGroupFactory contactGroupFactory;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
protected GroupInvitationManagerImpl(DatabaseComponent db,
|
||||
ClientHelper clientHelper, MetadataParser metadataParser,
|
||||
ContactGroupFactory contactGroupFactory) {
|
||||
super(db, clientHelper, metadataParser);
|
||||
this.contactGroupFactory = contactGroupFactory;
|
||||
localGroup = contactGroupFactory.createLocalGroup(getClientId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
// Ensure we've set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
try {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Return if we've already set things up for this contact
|
||||
if (db.containsGroup(txn, g.getId())) return;
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(txn, g);
|
||||
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||
// Attach the contact ID to the group
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(CONTACT_ID, c.getId().getInt());
|
||||
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
// remove the contact group (all messages will be removed with it)
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Group getContactGroup(Contact c) {
|
||||
return contactGroupFactory.createContactGroup(getClientId(), c);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary meta) throws DbException, FormatException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
String message) throws DbException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(PrivateGroup g, Contact c, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InvitationMessage> getInvitationMessages(
|
||||
ContactId contactId) throws DbException {
|
||||
Collection<InvitationMessage> invitations =
|
||||
new ArrayList<InvitationMessage>();
|
||||
|
||||
return invitations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<GroupInvitationItem> getInvitations() throws DbException {
|
||||
Collection<GroupInvitationItem> invitations =
|
||||
new ArrayList<GroupInvitationItem>();
|
||||
|
||||
return invitations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.InvitationRequestReceivedEvent;
|
||||
import org.briarproject.api.event.InvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sharing.InvitationItem;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sharing.SharingManager;
|
||||
@@ -316,27 +316,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
|
||||
try {
|
||||
// find session state based on shareable
|
||||
IS localState = getSessionStateForResponse(txn, f, c);
|
||||
|
||||
// define action
|
||||
InviteeSessionState.Action localAction;
|
||||
if (accept) {
|
||||
localAction = InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
} else {
|
||||
localAction = InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory(), clock);
|
||||
StateUpdate<IS, BaseMessage> update =
|
||||
engine.onLocalAction(localState, localAction);
|
||||
processInviteeStateUpdate(txn, null, update);
|
||||
|
||||
// track message
|
||||
// TODO handle this properly without engine hacks (#376)
|
||||
long time = update.toSend.get(0).getTime();
|
||||
trackMessage(txn, localState.getGroupId(), time, true);
|
||||
|
||||
respondToInvitation(txn, localState, accept);
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
@@ -345,6 +325,45 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
IS localState = (IS) getSessionState(txn, id, true);
|
||||
respondToInvitation(txn, localState, accept);
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
private void respondToInvitation(Transaction txn, IS localState,
|
||||
boolean accept) throws DbException, FormatException {
|
||||
// define action
|
||||
InviteeSessionState.Action localAction;
|
||||
if (accept) {
|
||||
localAction = InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
} else {
|
||||
localAction = InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory(), clock);
|
||||
StateUpdate<IS, BaseMessage> update =
|
||||
engine.onLocalAction(localState, localAction);
|
||||
processInviteeStateUpdate(txn, null, update);
|
||||
|
||||
// track message
|
||||
// TODO handle this properly without engine hacks (#376)
|
||||
long time = update.toSend.get(0).getTime();
|
||||
trackMessage(txn, localState.getGroupId(), time, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InvitationMessage> getInvitationMessages(ContactId contactId)
|
||||
throws DbException {
|
||||
@@ -418,8 +437,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InvitationItem> getInvitations() throws DbException {
|
||||
List<InvitationItem> invitations = new ArrayList<InvitationItem>();
|
||||
public Collection<SharingInvitationItem> getInvitations() throws DbException {
|
||||
List<SharingInvitationItem> invitations = new ArrayList<SharingInvitationItem>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
Set<S> shareables = new HashSet<S>();
|
||||
@@ -445,8 +464,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
|
||||
for (S s : shareables) {
|
||||
Collection<Contact> newS = newSharers.get(s.getId());
|
||||
boolean subscribed = db.containsGroup(txn, s.getId());
|
||||
InvitationItem invitation =
|
||||
new InvitationItem(s, subscribed, newS);
|
||||
SharingInvitationItem invitation =
|
||||
new SharingInvitationItem(s, subscribed, newS);
|
||||
invitations.add(invitation);
|
||||
}
|
||||
txn.setComplete();
|
||||
|
||||
Reference in New Issue
Block a user