Merge branch '9-conversation-view' into 'master'

Overhauled Conversation View with Message Bubbles

The Conversation View now uses a RecyclerView with conversation bubbles
in alternating colors and vector drawables to indicate message state.

The conversation bubbles have been taken from Telegram
and can be replaced by a UX designer later.
There's also a special bubble for unread messages,
so they are not overlooked when they come in delayed.

This commit also addresses #9, because message text can
now be selected and copied. This is done by using

    android:textIsSelectable="true"

which only works for API level 11 or higher.

If we want copy and paste on lower API levels,
additional measures have to be implemented.

See merge request !36
This commit is contained in:
akwizgran
2015-12-31 11:20:29 +00:00
38 changed files with 350 additions and 160 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 957 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1014 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

View File

@@ -0,0 +1,5 @@
<vector android:alpha="0.56" android:height="16dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:alpha="0.56" android:height="16dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:alpha="0.56" android:height="16dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha=".9" android:fillColor="#FF000000" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>

View File

@@ -0,0 +1,5 @@
<vector android:alpha="0.56" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@@ -1,12 +1,18 @@
<?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:id="@+id/layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- ListView will get inserted here -->
<android.support.v7.widget.RecyclerView
android:id="@+id/conversationView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical"/>
<ProgressBar
android:id="@+id/listLoadingProgressBar"
@@ -15,7 +21,8 @@
android:layout_gravity="center"
android:gravity="center"
android:layout_weight="1"
android:indeterminate="true"/>
android:indeterminate="true"
android:visibility="gone"/>
<TextView
android:id="@+id/emptyView"
@@ -26,7 +33,8 @@
android:gravity="center"
android:padding="@dimen/margin_large"
android:textSize="@dimen/text_size_large"
android:text="@string/no_private_messages"/>
android:text="@string/no_private_messages"
android:visibility="gone"/>
<View
android:layout_width="match_parent"
@@ -39,9 +47,7 @@
android:layout_height="wrap_content"
android:background="@color/button_bar_background"
android:paddingLeft="@dimen/margin_medium"
android:paddingStart="@dimen/margin_medium"
android:paddingRight="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium">
android:paddingStart="@dimen/margin_medium">
<EditText
android:id="@+id/contentView"
@@ -53,12 +59,16 @@
<ImageButton
android:id="@+id/sendButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_gravity="bottom"
android:src="@drawable/social_send_now"
android:background="@color/button_bar_background"
android:background="?attr/selectableItemBackground"
android:scaleType="fitEnd"
android:contentDescription="@string/send"
android:layout_gravity="center"/>
android:paddingRight="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:paddingBottom="@dimen/margin_medium"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,43 @@
<?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"
android:paddingRight="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:paddingTop="@dimen/margin_small"
android:paddingBottom="@dimen/margin_small">
<RelativeLayout
android:id="@+id/msgLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|start"
android:background="@drawable/msg_in"
android:paddingLeft="17dp"
android:paddingTop="5dp"
android:paddingRight="7dp"
android:paddingBottom="5dp">
<TextView
android:id="@+id/msgBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="80dp"
android:textIsSelectable="true"
tools:text="Short message"/>
<TextView
android:id="@+id/msgTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10sp"
android:textColor="@color/private_message_date"
android:layout_below="@+id/msgBody"
tools:text="Dec 24, 13:37"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,55 @@
<?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"
android:paddingLeft="@dimen/margin_medium"
android:paddingStart="@dimen/margin_medium"
android:paddingTop="@dimen/margin_small"
android:paddingBottom="@dimen/margin_small">
<RelativeLayout
android:id="@+id/msgLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"
android:background="@drawable/msg_out"
android:paddingLeft="7dp"
android:paddingTop="5dp"
android:paddingRight="17dp"
android:paddingBottom="5dp">
<TextView
android:id="@+id/msgBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:minWidth="80dp"
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."/>
<TextView
android:id="@+id/msgTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/msgBody"
android:layout_toLeftOf="@+id/msgStatus"
android:textSize="10sp"
android:textColor="@color/private_message_date"
android:singleLine="true"
tools:text="Dec 24, 13:37"/>
<ImageView
android:id="@+id/msgStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/msgTime"
android:layout_alignRight="@+id/msgBody"
android:layout_alignEnd="@+id/msgBody"
android:layout_marginLeft="3dp"
tools:src="@drawable/message_delivered"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -5,7 +5,6 @@
<color name="action_bar_background">#2D3E50</color>
<color name="button_bar_background">#FFFFFF</color>
<color name="dashboard_background">#FFFFFF</color>
<color name="private_message_background">#FFFFFF</color>
<color name="private_message_date">#AAAAAA</color>
<color name="unread_background">#FFFFFF</color>
<color name="horizontal_border">#CCCCCC</color>

View File

@@ -2,30 +2,27 @@ package org.briarproject.android.contact;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.LayoutUtils;
import org.briarproject.api.android.AndroidNotificationManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
@@ -77,12 +74,11 @@ 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.ReadPrivateMessageActivity.RESULT_PREV_NEXT;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1;
import static org.briarproject.api.messaging.PrivateMessageHeader.Status.DELIVERED;
import static org.briarproject.api.messaging.PrivateMessageHeader.Status.SENT;
public class ConversationActivity extends BriarActivity
implements EventListener, OnClickListener, OnItemClickListener {
implements EventListener, OnClickListener {
private static final int REQUEST_READ = 2;
private static final Logger LOG =
@@ -95,7 +91,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
private TextView empty = null;
private ProgressBar loading = null;
private ConversationAdapter adapter = null;
private ListView list = null;
private RecyclerView list = null;
private EditText content = null;
private ImageButton sendButton = null;
@@ -133,27 +129,29 @@ implements EventListener, OnClickListener, OnItemClickListener {
loading.setVisibility(VISIBLE);
adapter = new ConversationAdapter(this);
list = new ListView(this) {
@Override
public void onSizeChanged(int w, int h, int oldw, int oldh) {
// Scroll to the bottom when the keyboard is shown
super.onSizeChanged(w, h, oldw, oldh);
setSelection(getCount() - 1);
}
};
list.setLayoutParams(MATCH_WRAP_1);
int pad = LayoutUtils.getPadding(this);
list.setPadding(0, pad, 0, pad);
list.setClipToPadding(false);
// Make the dividers the same colour as the background
Resources res = getResources();
int background = res.getColor(android.R.color.transparent);
list.setDivider(new ColorDrawable(background));
list.setDividerHeight(pad);
list = (RecyclerView) findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
list.setOnItemClickListener(this);
list.setEmptyView(loading);
layout.addView(list, 0);
list.setVisibility(GONE);
// scroll down when opening keyboard
if (Build.VERSION.SDK_INT >= 11) {
list.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v,
int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (bottom < oldBottom) {
list.postDelayed(new Runnable() {
@Override
public void run() {
list.scrollToPosition(
adapter.getItemCount() - 1);
}
}, 100);
}
}
});
}
content = (EditText) findViewById(R.id.contentView);
sendButton = (ImageButton) findViewById(R.id.sendButton);
@@ -267,12 +265,10 @@ implements EventListener, OnClickListener, OnItemClickListener {
runOnUiThread(new Runnable() {
public void run() {
loading.setVisibility(GONE);
empty.setVisibility(VISIBLE);
list.setEmptyView(empty);
displayContactDetails();
sendButton.setEnabled(true);
adapter.clear();
if (!headers.isEmpty()) {
list.setVisibility(VISIBLE);
empty.setVisibility(GONE);
for (PrivateMessageHeader h : headers) {
ConversationItem item = new ConversationItem(h);
byte[] body = bodyCache.get(h.getId());
@@ -280,11 +276,12 @@ implements EventListener, OnClickListener, OnItemClickListener {
else item.setBody(body);
adapter.add(item);
}
adapter.sort(ConversationItemComparator.INSTANCE);
// Scroll to the bottom
list.setSelection(adapter.getCount() - 1);
list.scrollToPosition(adapter.getItemCount() - 1);
} else {
empty.setVisibility(VISIBLE);
list.setVisibility(GONE);
}
adapter.notifyDataSetChanged();
}
});
}
@@ -313,14 +310,18 @@ implements EventListener, OnClickListener, OnItemClickListener {
runOnUiThread(new Runnable() {
public void run() {
bodyCache.put(m, body);
int count = adapter.getCount();
int count = adapter.getItemCount();
for (int i = 0; i < count; i++) {
ConversationItem item = adapter.getItem(i);
if (item.getHeader().getId().equals(m)) {
item.setBody(body);
adapter.notifyDataSetChanged();
adapter.notifyItemChanged(i);
// Scroll to the bottom
list.setSelection(count - 1);
list.scrollToPosition(count - 1);
return;
}
}
@@ -333,7 +334,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
super.onActivityResult(request, result, data);
if (request == REQUEST_READ && result == RESULT_PREV_NEXT) {
int position = data.getIntExtra("briar.POSITION", -1);
if (position >= 0 && position < adapter.getCount())
if (position >= 0 && position < adapter.getItemCount())
displayMessage(position);
}
}
@@ -348,7 +349,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
private void markMessagesRead() {
notificationManager.clearPrivateMessageNotification(contactId);
List<MessageId> unread = new ArrayList<MessageId>();
int count = adapter.getCount();
int count = adapter.getItemCount();
for (int i = 0; i < count; i++) {
PrivateMessageHeader h = adapter.getItem(i).getHeader();
if (!h.isRead()) unread.add(h.getId());
@@ -388,6 +389,8 @@ implements EventListener, OnClickListener, OnItemClickListener {
GroupId g = ((MessageAddedEvent) e).getGroupId();
if (g.equals(groupId)) {
LOG.info("Message added, reloading");
// TODO: find a way of not needing to reload the entire
// conversation just because one message was added
loadHeaders();
}
} else if (e instanceof MessagesSentEvent) {
@@ -424,16 +427,14 @@ implements EventListener, OnClickListener, OnItemClickListener {
runOnUiThread(new Runnable() {
public void run() {
Set<MessageId> messages = new HashSet<MessageId>(messageIds);
boolean changed = false;
int count = adapter.getCount();
int count = adapter.getItemCount();
for (int i = 0; i < count; i++) {
ConversationItem item = adapter.getItem(i);
if (messages.contains(item.getHeader().getId())) {
item.setStatus(status);
changed = true;
adapter.notifyItemChanged(i);
}
}
if (changed) adapter.notifyDataSetChanged();
}
});
}
@@ -451,7 +452,7 @@ implements EventListener, OnClickListener, OnItemClickListener {
private long getMinTimestampForNewMessage() {
// Don't use an earlier timestamp than the newest message
long timestamp = 0;
int count = adapter.getCount();
int count = adapter.getItemCount();
for (int i = 0; i < count; i++) {
long t = adapter.getItem(i).getHeader().getTimestamp();
if (t > timestamp) timestamp = t;
@@ -492,11 +493,6 @@ implements EventListener, OnClickListener, OnItemClickListener {
});
}
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
displayMessage(position);
}
private void displayMessage(int position) {
ConversationItem item = adapter.getItem(position);
PrivateMessageHeader header = item.getHeader();

View File

@@ -1,115 +1,199 @@
package org.briarproject.android.contact;
import android.content.Context;
import android.content.res.Resources;
import android.support.v7.util.SortedList;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.util.ElasticHorizontalSpace;
import org.briarproject.android.util.LayoutUtils;
import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.util.StringUtils;
import java.util.ArrayList;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP;
import static org.briarproject.api.messaging.PrivateMessageHeader.Status.DELIVERED;
import static org.briarproject.api.messaging.PrivateMessageHeader.Status.SENT;
class ConversationAdapter extends ArrayAdapter<ConversationItem> {
class ConversationAdapter extends
RecyclerView.Adapter<ConversationAdapter.MessageHolder> {
private final int pad;
private static final int MSG_OUT = 0;
private static final int MSG_IN = 1;
private static final int MSG_IN_UNREAD = 2;
ConversationAdapter(Context ctx) {
super(ctx, android.R.layout.simple_expandable_list_item_1,
new ArrayList<ConversationItem>());
pad = LayoutUtils.getPadding(ctx);
private SortedList<ConversationItem> messages =
new SortedList<ConversationItem>(ConversationItem.class,
new SortedList.Callback<ConversationItem>() {
@Override
public void onInserted(int position, int count) {
notifyItemRangeInserted(position, count);
}
@Override
public void onChanged(int position, int count) {
notifyItemRangeChanged(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onRemoved(int position, int count) {
notifyItemRangeRemoved(position, count);
}
@Override
public int compare(ConversationItem c1,
ConversationItem c2) {
long time1 = c1.getHeader().getTimestamp();
long time2 = c2.getHeader().getTimestamp();
if (time1 < time2) return -1;
if (time1 > time2) return 1;
return 0;
}
@Override
public boolean areItemsTheSame(ConversationItem c1,
ConversationItem c2) {
return c1.getHeader().getId()
.equals(c2.getHeader().getId());
}
@Override
public boolean areContentsTheSame(ConversationItem c1,
ConversationItem c2) {
return c1.equals(c2);
}
});
private Context ctx;
public ConversationAdapter(Context context) {
ctx = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
public int getItemViewType(int position) {
// return different type for incoming and outgoing (local) messages
PrivateMessageHeader header = getItem(position).getHeader();
if (header.isLocal()) {
return MSG_OUT;
} else if (header.isRead()) {
return MSG_IN;
} else {
return MSG_IN_UNREAD;
}
}
@Override
public MessageHolder 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);
}
// 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(final MessageHolder ui, final int position) {
ConversationItem item = getItem(position);
PrivateMessageHeader header = item.getHeader();
Context ctx = getContext();
Resources res = ctx.getResources();
LinearLayout layout = new LinearLayout(ctx);
layout.setOrientation(VERTICAL);
if (header.isLocal()) layout.setPadding(3 * pad, 0, 0, 0);
else layout.setPadding(0, 0, 3 * pad, 0);
int background = res.getColor(R.color.private_message_background);
View content;
if (item.getBody() == null) {
TextView ellipsis = new TextView(ctx);
ellipsis.setText("\u2026");
content = ellipsis;
} else if (header.getContentType().equals("text/plain")) {
TextView text = new TextView(ctx);
text.setText(StringUtils.fromUtf8(item.getBody()));
content = text;
} else {
ImageButton attachment = new ImageButton(ctx);
attachment.setImageResource(R.drawable.content_attachment);
content = attachment;
}
content.setLayoutParams(MATCH_WRAP);
content.setBackgroundColor(background);
content.setPadding(pad, pad, pad, 0);
layout.addView(content);
if (header.isLocal()) {
LinearLayout footer = new LinearLayout(ctx);
footer.setLayoutParams(MATCH_WRAP);
footer.setOrientation(HORIZONTAL);
footer.setGravity(BOTTOM);
footer.setPadding(pad, 0, pad, pad);
footer.setBackgroundColor(background);
footer.addView(new ElasticHorizontalSpace(ctx));
ImageView status = new ImageView(ctx);
status.setPadding(0, 0, pad, 0);
if (item.getStatus() == DELIVERED) {
status.setImageResource(R.drawable.message_delivered);
ui.status.setImageResource(R.drawable.message_delivered);
} else if (item.getStatus() == SENT) {
status.setImageResource(R.drawable.message_sent);
ui.status.setImageResource(R.drawable.message_sent);
} else {
status.setImageResource(R.drawable.message_stored);
ui.status.setImageResource(R.drawable.message_stored);
}
footer.addView(status);
} else if (!header.isRead()) {
int bottom = ui.layout.getPaddingBottom();
int top = ui.layout.getPaddingTop();
int right = ui.layout.getPaddingRight();
int left = ui.layout.getPaddingLeft();
TextView date = new TextView(ctx);
date.setTextColor(res.getColor(R.color.private_message_date));
long timestamp = header.getTimestamp();
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
footer.addView(date);
// show unread messages in different color to not miss them
ui.layout.setBackgroundResource(R.drawable.msg_in_unread);
layout.addView(footer);
} else {
TextView date = new TextView(ctx);
date.setLayoutParams(MATCH_WRAP);
date.setGravity(LEFT);
date.setTextColor(res.getColor(R.color.private_message_date));
date.setBackgroundColor(background);
date.setPadding(pad, 0, pad, pad);
long timestamp = header.getTimestamp();
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
layout.addView(date);
// 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);
}
return layout;
if (item.getBody() == null) {
ui.body.setText("\u2026");
} else if (header.getContentType().equals("text/plain")) {
ui.body.setText(StringUtils.fromUtf8(item.getBody()));
} else {
// TODO support other content types
}
long timestamp = header.getTimestamp();
ui.date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
}
@Override
public int getItemCount() {
return messages == null ? 0 : messages.size();
}
public boolean isEmpty() {
return messages == null || messages.size() == 0;
}
public ConversationItem getItem(int position) {
return messages.get(position);
}
public void add(final ConversationItem message) {
this.messages.add(message);
}
public void remove(final ConversationItem message) {
this.messages.remove(message);
}
public void clear() {
this.messages.beginBatchedUpdates();
while(messages.size() != 0) {
messages.removeItemAt(0);
}
this.messages.endBatchedUpdates();
}
public static class MessageHolder extends RecyclerView.ViewHolder {
public ViewGroup layout;
public TextView body;
public TextView date;
public ImageView status;
public 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);
}
}
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.android.contact;
import java.util.Comparator;
class ConversationItemComparator implements Comparator<ConversationItem> {
static final ConversationItemComparator INSTANCE =
new ConversationItemComparator();
public int compare(ConversationItem a, ConversationItem b) {
// The oldest message comes first
long aTime = a.getHeader().getTimestamp();
long bTime = b.getHeader().getTimestamp();
if (aTime < bTime) return -1;
if (aTime > bTime) return 1;
return 0;
}
}

View File

@@ -40,6 +40,7 @@ import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1;
import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
@Deprecated
public class ReadPrivateMessageActivity extends BriarActivity
implements OnClickListener {