Refactor ConversationAdapter and its ConversationItems

This commit is contained in:
Torsten Grote
2016-10-18 09:38:13 -02:00
parent e00219c15f
commit 5ffcdc4e46
34 changed files with 769 additions and 1181 deletions

View File

@@ -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"

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>
@@ -215,7 +214,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>
@@ -279,7 +277,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>

View File

@@ -276,24 +276,28 @@ public class ContactListFragment extends BaseFragment implements EventListener {
IntroductionRequestReceivedEvent m =
(IntroductionRequestReceivedEvent) e;
IntroductionRequest ir = m.getIntroductionRequest();
updateItem(m.getContactId(), ConversationItem.from(ir));
updateItem(m.getContactId(),
ConversationItem.from(getContext(), "", 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(),
ConversationItem.from(getContext(), "", ir));
} else if (e instanceof InvitationRequestReceivedEvent) {
LOG.info("Invitation request received, updating item");
InvitationRequestReceivedEvent m = (InvitationRequestReceivedEvent) e;
InvitationRequest ir = m.getRequest();
updateItem(m.getContactId(), ConversationItem.from(ir));
updateItem(m.getContactId(),
ConversationItem.from(getContext(), "", 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(),
ConversationItem.from(getContext(), "", ir));
}
}

View File

@@ -6,8 +6,6 @@ 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;
// This class is NOT thread-safe
public class ContactListItem {
@@ -34,8 +32,8 @@ 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 instanceof ConversationInItem &&
!((ConversationInItem) message).isRead())
unread++;
}
}

View File

@@ -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,8 @@ 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.contact.ConversationItem.PartialItem;
import org.briarproject.android.introduction.IntroductionActivity;
import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.android.view.TextInputView;
@@ -39,6 +41,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;
@@ -92,16 +95,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 +119,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;
@@ -325,7 +327,6 @@ public class ConversationActivity extends BriarActivity
toolbarStatus
.setContentDescription(getString(R.string.offline));
}
adapter.setContactName(contactName);
}
});
}
@@ -399,33 +400,42 @@ public class ConversationActivity extends BriarActivity
Collection<InvitationMessage> invitations) {
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());
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));
} else {
IntroductionResponse ir = (IntroductionResponse) im;
items.add(ConversationItem.from(ConversationActivity.this,
contactName, ir));
for (PrivateMessageHeader h : headers) {
ConversationItem item = ConversationItem.from(h);
String body = bodyCache.get(h.getId());
if (body == null) loadMessageBody(h.getId());
else ((PartialItem) item).setText(body);
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 (IntroductionMessage m : introductions) {
ConversationItem item;
if (m instanceof IntroductionRequest) {
item = ConversationItem
.from(ConversationActivity.this,
contactName,
(IntroductionRequest) m);
} else {
item = ConversationItem
.from(ConversationActivity.this,
contactName,
(IntroductionResponse) m);
}
items.add(item);
}
for (InvitationMessage i : invitations) {
if (i instanceof InvitationRequest) {
InvitationRequest r = (InvitationRequest) i;
items.add(ConversationItem
.from(ConversationActivity.this,
contactName, r));
} else if (i instanceof InvitationResponse) {
InvitationResponse r = (InvitationResponse) i;
items.add(ConversationItem
.from(ConversationActivity.this,
contactName, r));
}
}
}
return items;
}
@@ -439,7 +449,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,17 +458,17 @@ 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);
((PartialItem) item).setText(body);
adapter.notifyItemChanged(messages.keyAt(i));
list.scrollToPosition(adapter.getItemCount() - 1);
return;
@@ -482,9 +492,9 @@ public class ConversationActivity extends BriarActivity
private void markMessagesRead() {
Map<MessageId, GroupId> unread = new HashMap<>();
SparseArray<IncomingItem> list = adapter.getIncomingMessages();
SparseArray<ConversationInItem> list = adapter.getIncomingMessages();
for (int i = 0; i < list.size(); i++) {
IncomingItem item = list.valueAt(i);
ConversationInItem item = list.valueAt(i);
if (!item.isRead())
unread.put(item.getId(), item.getGroupId());
}
@@ -561,7 +571,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 =
ConversationRequestItem.from(this, contactName, ir);
addConversationItem(item);
}
} else if (e instanceof IntroductionResponseReceivedEvent) {
@@ -580,7 +591,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 +615,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 +635,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 +645,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 +660,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,8 +674,8 @@ 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);
item.setBody(body);
ConversationItem item = ConversationItem.from(h);
((PartialItem) item).setText(body);
bodyCache.put(id, body);
addConversationItem(item);
} catch (DbException e) {
@@ -812,23 +825,35 @@ public class ConversationActivity extends BriarActivity
});
}
@UiThread
@Override
public void respondToIntroduction(final SessionId sessionId,
public void respondToRequest(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;
default:
throw new IllegalArgumentException(
"Unknown Request Type");
}
loadMessages();
} catch (DbException | FormatException e) {
introductionResponseError();
if (LOG.isLoggable(WARNING))
@@ -839,6 +864,31 @@ 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);
}
loadMessages();
}
@DatabaseExecutor
private void respondToForumRequest(SessionId id, boolean accept)
throws DbException {
forumSharingManager.respondToInvitation(id, accept);
loadMessages();
}
@DatabaseExecutor
private void respondToBlogRequest(SessionId id, boolean accept)
throws DbException {
blogSharingManager.respondToInvitation(id, accept);
loadMessages();
}
private void introductionResponseError() {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override

View File

@@ -1,365 +1,74 @@
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.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 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();
}
@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);
}
}
@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);
ConversationItem item = items.get(position);
if (item instanceof ConversationRequestItem) {
return R.layout.list_item_conversation_request;
} else if (item instanceof ConversationNoticeOutItem) {
bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item);
return R.layout.list_item_conversation_notice_out;
} 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);
return R.layout.list_item_conversation_notice_in;
} else if (item instanceof ConversationMessageOutItem) {
return R.layout.list_item_conversation_msg_out;
} else if (item instanceof ConversationMessageInItem) {
return R.layout.list_item_conversation_msg_in;
} else {
throw new IllegalArgumentException("Unhandled Conversation Item");
throw new IllegalArgumentException("Unknown ConversationItem");
}
}
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);
}
@Override
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");
}
}
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;
}
@Override
public void onBindViewHolder(ConversationItemViewHolder ui, int position) {
ConversationItem item = items.get(position);
if (item instanceof ConversationRequestItem) {
((ConversationRequestViewHolder) ui).bind(item, listener);
} 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;
}
ui.bind(item);
}
// 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
@@ -393,138 +102,46 @@ class ConversationAdapter extends BriarAdapter<ConversationItem, ViewHolder> {
}
}
SparseArray<IncomingItem> getIncomingMessages() {
SparseArray<IncomingItem> messages = new SparseArray<>();
SparseArray<ConversationInItem> getIncomingMessages() {
SparseArray<ConversationInItem> 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 instanceof ConversationInItem) {
messages.put(i, (ConversationInItem) 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);
}
}
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);
}
}

View File

@@ -0,0 +1,31 @@
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 ConversationInItem extends ConversationItem {
private boolean read;
ConversationInItem(MessageId id, GroupId groupId, @Nullable String text,
long time, boolean read) {
super(id, groupId, text, time);
this.read = read;
}
public boolean isRead() {
return read;
}
public void setRead(boolean read) {
this.read = read;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,65 +1,64 @@
package org.briarproject.android.contact;
import android.content.Context;
import android.support.annotation.StringRes;
import org.briarproject.R;
import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.android.contact.ConversationRequestItem.RequestType;
import org.briarproject.api.blogs.BlogInvitationRequest;
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.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.INTRODUCTION;
@NotThreadSafe
@NotNullByDefault
abstract class ConversationItem {
final private MessageId id;
final private GroupId groupId;
protected @Nullable String text;
final private long time;
public ConversationItem(@NotNull MessageId id, @NotNull GroupId groupId,
long time) {
ConversationItem(MessageId id, GroupId groupId,
@Nullable String text, long time) {
this.id = id;
this.groupId = groupId;
this.text = text;
this.time = time;
}
abstract int getType();
@NotNull
public MessageId getId() {
MessageId getId() {
return id;
}
@NotNull
public GroupId getGroupId() {
GroupId getGroupId() {
return groupId;
}
@Nullable
public String getText() {
return text;
}
long getTime() {
return time;
}
public static ConversationMessageItem from(PrivateMessageHeader h) {
static ConversationItem from(PrivateMessageHeader h) {
if (h.isLocal()) {
return new ConversationMessageOutItem(h);
} else {
@@ -67,17 +66,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 +112,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 +132,99 @@ 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 {
text = ctx.getString(R.string.blogs_sharing_invitation_sent,
((BlogInvitationRequest) ir).getBlogAuthorName(),
contactName);
}
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 {
text = ctx.getString(R.string.blogs_sharing_invitation_received,
contactName,
((BlogInvitationRequest) ir).getBlogAuthorName());
type = BLOG;
}
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 {
res = R.string.blogs_sharing_response_accepted_sent;
}
} else {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_declined_sent;
} else {
res = R.string.blogs_sharing_response_declined_sent;
}
}
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.");
if (ir.wasAccepted()) {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_accepted_received;
} else {
res = R.string.blogs_sharing_response_accepted_received;
}
} else {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_declined_received;
} else {
res = R.string.blogs_sharing_response_declined_received;
}
}
String text = ctx.getString(res, contactName);
return new ConversationNoticeInItem(ir.getId(), ir.getGroupId(),
text, null, ir.getTimestamp(), ir.isRead());
}
}
private static ConversationNoticeItem from(Context ctx, String contactName,
ForumInvitationResponse fir) {
interface PartialItem {
@Nullable
String getText();
void setText(String text);
if (fir.isLocal()) {
String text;
if (fir.wasAccepted()) {
text = ctx.getString(
R.string.forum_invitation_response_accepted_sent,
contactName);
} else {
text = ctx.getString(
R.string.forum_invitation_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.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());
}
}
/**
* 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 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);
}
}

View File

@@ -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.getText() == null) {
text.setText("\u2026");
} else {
text.setText(StringUtils.trim(item.getText()));
}
long timestamp = item.getTime();
time.setText(AndroidUtils.formatDate(time.getContext(), timestamp));
}
}

View File

@@ -1,31 +1,22 @@
package org.briarproject.android.contact;
import org.briarproject.android.contact.ConversationItem.PartialItem;
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 ConversationInItem
implements PartialItem {
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 void setText(String body) {
text = body;
}
@Override
public boolean isRead() {
return read;
}
@Override
public void setRead(boolean read) {
this.read = read;
}
}

View File

@@ -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;
}
}

View File

@@ -1,42 +1,23 @@
package org.briarproject.android.contact;
import org.briarproject.android.contact.ConversationItem.PartialItem;
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
implements PartialItem {
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());
}
@Override
int getType() {
return MSG_OUT;
public void setText(String body) {
text = body;
}
@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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,33 +1,29 @@
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;
// 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 ConversationInItem {
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;
}
@Override
int getType() {
return NOTICE_IN;
@Nullable
public String getMsgText() {
return msgText;
}
@Override
public boolean isRead() {
return read;
}
@Override
public void setRead(boolean read) {
this.read = read;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,44 +1,29 @@
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;
// 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;
}
@Override
int getType() {
return NOTICE_OUT;
@Nullable
public String getMsgText() {
return msgText;
}
@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;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,40 @@
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);
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;
}
}

View File

@@ -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();
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.android.contact;
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 };
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;
}
}

View File

@@ -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);
}
});
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}