diff --git a/briar-android/res/layout/list_item_msg_in.xml b/briar-android/res/layout/list_item_conversation_msg_in.xml similarity index 92% rename from briar-android/res/layout/list_item_msg_in.xml rename to briar-android/res/layout/list_item_conversation_msg_in.xml index 3c18ca1a88c15d4e3f2c22a97c109343c68c6f43..ef5c7a953b04987859556f9939050b6e02a5c897 100644 --- a/briar-android/res/layout/list_item_msg_in.xml +++ b/briar-android/res/layout/list_item_conversation_msg_in.xml @@ -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" diff --git a/briar-android/res/layout/list_item_msg_out.xml b/briar-android/res/layout/list_item_conversation_msg_out.xml similarity index 84% rename from briar-android/res/layout/list_item_msg_out.xml rename to briar-android/res/layout/list_item_conversation_msg_out.xml index 7b7f4f65cee607313a5bee6079cd26806337d3e3..dd9c7b1f8ec0b288ad6bbef262ea6773db1a03eb 100644 --- a/briar-android/res/layout/list_item_msg_out.xml +++ b/briar-android/res/layout/list_item_conversation_msg_out.xml @@ -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"/> diff --git a/briar-android/res/layout/list_item_shareable_invitation_in.xml b/briar-android/res/layout/list_item_conversation_notice_in.xml similarity index 71% rename from briar-android/res/layout/list_item_shareable_invitation_in.xml rename to briar-android/res/layout/list_item_conversation_notice_in.xml index b7cc1bb36655ec47bffc0306847ca1f341ec3c5b..073edf537c67a9d86d955d59e7a581dd3544240a 100644 --- a/briar-android/res/layout/list_item_shareable_invitation_in.xml +++ b/briar-android/res/layout/list_item_conversation_notice_in.xml @@ -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> \ No newline at end of file diff --git a/briar-android/res/layout/list_item_msg_notice_out.xml b/briar-android/res/layout/list_item_conversation_notice_out.xml similarity index 84% rename from briar-android/res/layout/list_item_msg_notice_out.xml rename to briar-android/res/layout/list_item_conversation_notice_out.xml index 0d328fe5c02963444f80853a6bbe3d160e5632a8..9ea5bd96772f6ffedf371bc7a70700e6a8ed68b3 100644 --- a/briar-android/res/layout/list_item_msg_notice_out.xml +++ b/briar-android/res/layout/list_item_conversation_notice_out.xml @@ -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"/> diff --git a/briar-android/res/layout/list_item_introduction_in.xml b/briar-android/res/layout/list_item_conversation_request.xml similarity index 84% rename from briar-android/res/layout/list_item_introduction_in.xml rename to briar-android/res/layout/list_item_conversation_request.xml index 9094a1fc3c53203cd07fbf153735c4cbb33c608d..3932e82e535394583f6956d405f60281d95df874 100644 --- a/briar-android/res/layout/list_item_introduction_in.xml +++ b/briar-android/res/layout/list_item_conversation_request.xml @@ -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" diff --git a/briar-android/res/layout/list_item_notice_in.xml b/briar-android/res/layout/list_item_notice_in.xml deleted file mode 100644 index 13994b47422388050c8f289ea309b5982cee43b8..0000000000000000000000000000000000000000 --- a/briar-android/res/layout/list_item_notice_in.xml +++ /dev/null @@ -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> \ No newline at end of file diff --git a/briar-android/res/layout/list_item_notice_out.xml b/briar-android/res/layout/list_item_notice_out.xml deleted file mode 100644 index ae327bb9959cdeea6e2e338ce17d1e5be5a092e5..0000000000000000000000000000000000000000 --- a/briar-android/res/layout/list_item_notice_out.xml +++ /dev/null @@ -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> \ No newline at end of file diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 2b04c8b8cf816d3560bd1e6247d65ae71e3232f7..bd35788a27f2bcfc2936ab94eeeac6bdf3450447 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -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> diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java index c7114857c62297168ea4ddc3757996d58b9d44c7..deb7944037c494ab3f7890e4013305247a47c103 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java @@ -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)); } } diff --git a/briar-android/src/org/briarproject/android/contact/ContactListItem.java b/briar-android/src/org/briarproject/android/contact/ContactListItem.java index e90cdf939b14f6a582594bcc36dd36d4b2aa7909..d764266380f5043389798a7ac5abe864abc5e43f 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListItem.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListItem.java @@ -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++; } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index a5496d9c709b464113bfc49999552d9427ab8b2f..5eac456b6e33eb6cdb00a2716beafa69d8a237a3 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -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 diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java index 3fa184bf2f8cd47da961d30e0bd083e65c7e814e..85711eb6b2f682f5c80bcd624b6f4c799a2ebe3d 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java @@ -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); - } else { - throw new IllegalArgumentException("Unhandled Conversation 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()))); + 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 { - // 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; - } + throw new IllegalArgumentException("Unknown ConversationItem"); } - - // 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; - } - } - - // 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.bind(item); } - 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); - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationInItem.java new file mode 100644 index 0000000000000000000000000000000000000000..3d3ab2a1b3d337c6732b6d6f6f43f4e3d9360a73 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationInItem.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java deleted file mode 100644 index 6959d214b10abb5599c5d1e04d439ecc7ae1f296..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionInItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionItem.java b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionItem.java deleted file mode 100644 index d2cdef596ffdadd4c4b38a280f5536ce0d4ead6a..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java deleted file mode 100644 index 2729fea0cd549748c7cfccceb43f12813503505d..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationIntroductionOutItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationItem.java index d8c5e5aa746900f212744ae1423f811e5e09f818..9d1749ae8796e9fcc5e055e9e3a6dfb495fba538 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationItem.java @@ -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; - -// This class is not thread-safe -public abstract class ConversationItem { - - // 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 org.jetbrains.annotations.Nullable; + +import javax.annotation.concurrent.NotThreadSafe; + +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()); - } - } - - public static ConversationShareableInvitationItem from( - InvitationRequest fim) { - if (fim.isLocal()) { - return new ConversationShareableInvitationOutItem(fim); - } else { - return new ConversationShareableInvitationInItem(fim); - } - } - - public static ConversationNoticeItem 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); - } else { - throw new IllegalArgumentException("Unknown Invitation Response."); + ir.getGroupId(), text, null, ir.getTimestamp(), + ir.isRead()); } } - private static ConversationNoticeItem from(Context ctx, String contactName, - ForumInvitationResponse fir) { - - if (fir.isLocal()) { + static ConversationItem from(Context ctx, String contactName, + InvitationRequest ir) { + if (ir.isLocal()) { String text; - if (fir.wasAccepted()) { - text = ctx.getString( - R.string.forum_invitation_response_accepted_sent, + if (ir instanceof ForumInvitationRequest) { + text = ctx.getString(R.string.forum_invitation_sent, + ((ForumInvitationRequest) ir).getForumName(), contactName); } else { - text = ctx.getString( - R.string.forum_invitation_response_declined_sent, + text = ctx.getString(R.string.blogs_sharing_invitation_sent, + ((BlogInvitationRequest) ir).getBlogAuthorName(), contactName); } - return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(), - text, fir.getTimestamp(), fir.isSent(), fir.isSeen()); + return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(), + text, ir.getMessage(), ir.getTimestamp(), ir.isSent(), + ir.isSeen()); } else { String text; - if (fir.wasAccepted()) { - text = ctx.getString( - R.string.forum_invitation_response_accepted_received, - contactName); + 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.forum_invitation_response_declined_received, - contactName); + 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 ConversationNoticeInItem(fir.getId(), fir.getGroupId(), - text, fir.getTimestamp(), fir.isRead()); + return new ConversationRequestItem(ir.getId(), + ir.getGroupId(), type, ir.getSessionId(), text, + ir.getMessage(), ir.getTimestamp(), ir.isRead(), + !ir.isAvailable()); } } - 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); + static ConversationItem from(Context ctx, String contactName, + InvitationResponse 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 { - text = ctx.getString( - R.string.blogs_sharing_response_declined_sent, - contactName); + if (ir instanceof ForumInvitationResponse) { + res = R.string.forum_invitation_response_declined_sent; + } else { + res = R.string.blogs_sharing_response_declined_sent; + } } - return new ConversationNoticeOutItem(fir.getId(), fir.getGroupId(), - text, fir.getTimestamp(), fir.isSent(), fir.isSeen()); + String text = ctx.getString(res, contactName); + return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(), + text, null, ir.getTimestamp(), ir.isSent(), ir.isSeen()); } else { - String text; - if (fir.wasAccepted()) { - text = ctx.getString( - R.string.blogs_sharing_response_accepted_received, - contactName); + 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 { - text = ctx.getString( - R.string.blogs_sharing_response_declined_received, - contactName); + if (ir instanceof ForumInvitationResponse) { + res = R.string.forum_invitation_response_declined_received; + } else { + res = R.string.blogs_sharing_response_declined_received; + } } - 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 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 PartialItem { - interface OutgoingItem { + @Nullable + String getText(); - @NotNull - MessageId getId(); + void setText(String text); - 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); - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItemViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationItemViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..45713cdecdc2df9e627900941d2fa8c62ccad727 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationItemViewHolder.java @@ -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)); + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationMessageInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationMessageInItem.java index eefef09b4ef4dc3c38d8d2896ce405af9948e61b..96388b7ae5ce42c939998ff9373c79111c8d3228 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationMessageInItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationMessageInItem.java @@ -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(); - } - - @Override - int getType() { - return MSG_IN; + ConversationMessageInItem(PrivateMessageHeader h) { + super(h.getId(), h.getGroupId(), null, h.getTimestamp(), h.isRead()); } - @Override - public boolean isRead() { - return read; + public void setText(String body) { + text = body; } - @Override - public void setRead(boolean read) { - this.read = read; - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationMessageItem.java b/briar-android/src/org/briarproject/android/contact/ConversationMessageItem.java deleted file mode 100644 index b4dc64b6cc8c2aa86e729f545b19a7a78776b04d..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationMessageItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationMessageOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationMessageOutItem.java index 550219e9b3e5cd9aadc71405bb0488b9513fbc4b..9253fd7fa3caa9d1038eca89a19228c6e250e674 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationMessageOutItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationMessageOutItem.java @@ -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(); - } - - @Override - int getType() { - return MSG_OUT; - } - - @Override - public boolean isSent() { - return sent; - } - - @Override - public void setSent(boolean sent) { - this.sent = sent; + ConversationMessageOutItem(PrivateMessageHeader h) { + super(h.getId(), h.getGroupId(), null, h.getTimestamp(), h.isSent(), + h.isSeen()); } - @Override - public boolean isSeen() { - return seen; + public void setText(String body) { + text = body; } - @Override - public void setSeen(boolean seen) { - this.seen = seen; - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationMessageOutViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationMessageOutViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..83d58dcee413315435edef53f126f4e86d8366ed --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationMessageOutViewHolder.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java index 6e8fbfc8bcb3331b817bea7793f3295eb30b1cfa..df377d2d77eab2f230f4db1411d788b59516acc8 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInItem.java @@ -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; - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeInViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..7e5a60824db789619a1ca6323dfd9ffc867a425c --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeInViewHolder.java @@ -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); + } + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeItem.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeItem.java deleted file mode 100644 index 758c311d01f57e9611e1a5b7411c7dcc41d4d689..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationNoticeItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java index 95ed78510fba0d585b51044422d660a0f1d3a7ad..19ab79c64a159947be001c71edfd5325a11c7cbb 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutItem.java @@ -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; - } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..c0bdf7fcf758c42956bacd0b76c4c0b4869af560 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationNoticeOutViewHolder.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationOutItem.java new file mode 100644 index 0000000000000000000000000000000000000000..f8cbaec23aad74e83426a539c01ca4481917f253 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationOutItem.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationOutItemViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationOutItemViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..4c10ba402499c155e48d70fa40202b96ef1b018e --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationOutItemViewHolder.java @@ -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(); + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java b/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java new file mode 100644 index 0000000000000000000000000000000000000000..9999350d42f59e5c7ad72f6360c0ff0ddfd1945f --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationRequestItem.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationRequestViewHolder.java b/briar-android/src/org/briarproject/android/contact/ConversationRequestViewHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..972884ada22976df853dce81c8b5651df13d62e1 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationRequestViewHolder.java @@ -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); + } + }); + } + } + +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java deleted file mode 100644 index 6e46b7b4160174929712bf959921830e9c12f88a..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java deleted file mode 100644 index 60f8a70b1ca4bd1f331925cb771c35813e1f422e..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java +++ /dev/null @@ -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; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java deleted file mode 100644 index 426403ab36c488cb94af91a76ef0f52b010be671..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java +++ /dev/null @@ -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; - } -}