From 97fb9c8c3ebe3d6e9580dad732ceea5a9092f945 Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Sat, 8 Feb 2014 17:36:17 +0000 Subject: [PATCH] Updated group conversation view to resemble private conversation view. --- .../android/contact/ConversationActivity.java | 6 +- .../android/contact/ConversationAdapter.java | 1 + .../contact/ConversationItemComparator.java | 5 +- .../android/groups/GroupActivity.java | 104 +++++++++++++----- .../android/groups/GroupAdapter.java | 55 ++++++--- .../android/groups/GroupItem.java | 37 +++++++ .../android/groups/GroupItemComparator.java | 17 +++ 7 files changed, 181 insertions(+), 44 deletions(-) create mode 100644 briar-android/src/org/briarproject/android/groups/GroupItem.java create mode 100644 briar-android/src/org/briarproject/android/groups/GroupItemComparator.java diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index 990e67efc8..0498107143 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -50,7 +50,7 @@ import android.widget.ListView; public class ConversationActivity extends BriarActivity implements EventListener, OnClickListener, OnItemClickListener { - private static final int REQUEST_READ_MESSAGE = 2; + private static final int REQUEST_READ = 2; private static final Logger LOG = Logger.getLogger(ConversationActivity.class.getName()); @@ -239,7 +239,7 @@ implements EventListener, OnClickListener, OnItemClickListener { @Override protected void onActivityResult(int request, int result, Intent data) { super.onActivityResult(request, result, data); - if(request == REQUEST_READ_MESSAGE && result == RESULT_PREV_NEXT) { + if(request == REQUEST_READ && result == RESULT_PREV_NEXT) { int position = data.getIntExtra("briar.POSITION", -1); if(position >= 0 && position < adapter.getCount()) displayMessage(position); @@ -296,6 +296,6 @@ implements EventListener, OnClickListener, OnItemClickListener { i.putExtra("briar.CONTENT_TYPE", header.getContentType()); i.putExtra("briar.TIMESTAMP", header.getTimestamp()); i.putExtra("briar.POSITION", position); - startActivityForResult(i, REQUEST_READ_MESSAGE); + startActivityForResult(i, REQUEST_READ); } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java index 62f4eebc53..a48ed35b83 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java @@ -56,6 +56,7 @@ class ConversationAdapter extends ArrayAdapter<ConversationItem> { authorView.init(header.getAuthor().getName(), VERIFIED); headerLayout.addView(authorView); + // FIXME: Factor this out into a TimestampView TextView date = new TextView(ctx); date.setTextSize(14); date.setPadding(0, pad, pad, pad); diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItemComparator.java b/briar-android/src/org/briarproject/android/contact/ConversationItemComparator.java index 1bf7a39055..6b5f3ec77e 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationItemComparator.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationItemComparator.java @@ -2,10 +2,9 @@ package org.briarproject.android.contact; import java.util.Comparator; -public class ConversationItemComparator -implements Comparator<ConversationItem> { +class ConversationItemComparator implements Comparator<ConversationItem> { - public static final ConversationItemComparator INSTANCE = + static final ConversationItemComparator INSTANCE = new ConversationItemComparator(); public int compare(ConversationItem a, ConversationItem b) { diff --git a/briar-android/src/org/briarproject/android/groups/GroupActivity.java b/briar-android/src/org/briarproject/android/groups/GroupActivity.java index ff9970b7c0..a6d124dcc4 100644 --- a/briar-android/src/org/briarproject/android/groups/GroupActivity.java +++ b/briar-android/src/org/briarproject/android/groups/GroupActivity.java @@ -17,7 +17,6 @@ import java.util.logging.Logger; import javax.inject.Inject; import org.briarproject.R; -import org.briarproject.android.AscendingHeaderComparator; import org.briarproject.android.BriarActivity; import org.briarproject.android.util.HorizontalBorder; import org.briarproject.android.util.ListLoadingProgressBar; @@ -26,6 +25,7 @@ import org.briarproject.api.android.DatabaseUiExecutor; import org.briarproject.api.db.DatabaseComponent; import org.briarproject.api.db.DbException; import org.briarproject.api.db.MessageHeader; +import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.db.NoSuchSubscriptionException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventListener; @@ -34,6 +34,7 @@ import org.briarproject.api.event.MessageExpiredEvent; import org.briarproject.api.event.SubscriptionRemovedEvent; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.messaging.GroupId; +import org.briarproject.api.messaging.MessageId; import android.content.Intent; import android.os.Bundle; @@ -48,7 +49,7 @@ import android.widget.ListView; public class GroupActivity extends BriarActivity implements EventListener, OnClickListener, OnItemClickListener { - private static final int REQUEST_READ_POST = 2; + private static final int REQUEST_READ = 2; private static final Logger LOG = Logger.getLogger(GroupActivity.class.getName()); @@ -125,11 +126,7 @@ OnClickListener, OnItemClickListener { displayHeaders(headers); } catch(NoSuchSubscriptionException e) { if(LOG.isLoggable(INFO)) LOG.info("Subscription removed"); - runOnUiThread(new Runnable() { - public void run() { - finish(); - } - }); + finishOnUiThread(); } catch(DbException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -148,30 +145,91 @@ OnClickListener, OnItemClickListener { list.setVisibility(VISIBLE); loading.setVisibility(GONE); adapter.clear(); - for(MessageHeader h : headers) adapter.add(h); - adapter.sort(AscendingHeaderComparator.INSTANCE); + for(MessageHeader h : headers) adapter.add(new GroupItem(h)); + adapter.sort(GroupItemComparator.INSTANCE); adapter.notifyDataSetChanged(); - selectFirstUnread(); + expandMessages(); } }); } - private void selectFirstUnread() { - int firstUnread = -1, count = adapter.getCount(); + private void expandMessages() { + // Expand unread messages and the last three messages + int firstExpanded = -1, count = adapter.getCount(); + if(count == 0) return; for(int i = 0; i < count; i++) { - if(!adapter.getItem(i).isRead()) { - firstUnread = i; - break; + GroupItem item = adapter.getItem(i); + if(!item.getHeader().isRead() || i >= count - 3) { + if(firstExpanded == -1) firstExpanded = i; + item.setExpanded(true); + loadMessage(item.getHeader()); } } - if(firstUnread == -1) list.setSelection(count - 1); - else list.setSelection(firstUnread); + // Scroll to the first expanded message + list.setSelection(firstExpanded); + } + + private void loadMessage(final MessageHeader h) { + dbUiExecutor.execute(new Runnable() { + public void run() { + try { + lifecycleManager.waitForDatabase(); + long now = System.currentTimeMillis(); + byte[] body = db.getMessageBody(h.getId()); + long duration = System.currentTimeMillis() - now; + if(LOG.isLoggable(INFO)) + LOG.info("Loading message took " + duration + " ms"); + displayMessage(h.getId(), body); + if(!h.isRead()) { + now = System.currentTimeMillis(); + db.setReadFlag(h.getId(), true); + duration = System.currentTimeMillis() - now; + if(LOG.isLoggable(INFO)) + LOG.info("Setting read took " + duration + " ms"); + } + } catch(NoSuchMessageException e) { + if(LOG.isLoggable(INFO)) LOG.info("Message expired"); + // The item will be removed when we get the event + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } catch(InterruptedException e) { + if(LOG.isLoggable(INFO)) + LOG.info("Interrupted while waiting for database"); + Thread.currentThread().interrupt(); + } + } + }); + } + + private void displayMessage(final MessageId m, final byte[] body) { + runOnUiThread(new Runnable() { + public void run() { + int count = adapter.getCount(); + for(int i = 0; i < count; i++) { + GroupItem item = adapter.getItem(i); + if(item.getHeader().getId().equals(m)) { + item.setBody(body); + adapter.notifyDataSetChanged(); + return; + } + } + } + }); + } + + private void finishOnUiThread() { + runOnUiThread(new Runnable() { + public void run() { + finish(); + } + }); } @Override protected void onActivityResult(int request, int result, Intent data) { super.onActivityResult(request, result, data); - if(request == REQUEST_READ_POST && result == RESULT_PREV_NEXT) { + if(request == REQUEST_READ && result == RESULT_PREV_NEXT) { int position = data.getIntExtra("briar.POSITION", -1); if(position >= 0 && position < adapter.getCount()) displayMessage(position); @@ -197,11 +255,7 @@ OnClickListener, OnItemClickListener { SubscriptionRemovedEvent s = (SubscriptionRemovedEvent) e; if(s.getGroup().getId().equals(groupId)) { if(LOG.isLoggable(INFO)) LOG.info("Subscription removed"); - runOnUiThread(new Runnable() { - public void run() { - finish(); - } - }); + finishOnUiThread(); } } } @@ -218,7 +272,7 @@ OnClickListener, OnItemClickListener { } private void displayMessage(int position) { - MessageHeader item = adapter.getItem(position); + MessageHeader item = adapter.getItem(position).getHeader(); Intent i = new Intent(this, ReadGroupPostActivity.class); i.putExtra("briar.GROUP_ID", groupId.getBytes()); i.putExtra("briar.GROUP_NAME", groupName); @@ -229,6 +283,6 @@ OnClickListener, OnItemClickListener { i.putExtra("briar.CONTENT_TYPE", item.getContentType()); i.putExtra("briar.TIMESTAMP", item.getTimestamp()); i.putExtra("briar.POSITION", position); - startActivityForResult(i, REQUEST_READ_POST); + startActivityForResult(i, REQUEST_READ); } } diff --git a/briar-android/src/org/briarproject/android/groups/GroupAdapter.java b/briar-android/src/org/briarproject/android/groups/GroupAdapter.java index 09c2ad732c..1953666774 100644 --- a/briar-android/src/org/briarproject/android/groups/GroupAdapter.java +++ b/briar-android/src/org/briarproject/android/groups/GroupAdapter.java @@ -1,7 +1,9 @@ package org.briarproject.android.groups; +import static android.view.Gravity.CENTER_HORIZONTAL; import static android.view.Gravity.CENTER_VERTICAL; import static android.widget.LinearLayout.HORIZONTAL; +import static android.widget.LinearLayout.VERTICAL; import static java.text.DateFormat.SHORT; import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1; @@ -12,6 +14,7 @@ import org.briarproject.android.util.AuthorView; import org.briarproject.android.util.LayoutUtils; import org.briarproject.api.Author; import org.briarproject.api.db.MessageHeader; +import org.briarproject.util.StringUtils; import android.content.Context; import android.content.res.Resources; @@ -20,45 +23,71 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.TextView; -class GroupAdapter extends ArrayAdapter<MessageHeader> { +class GroupAdapter extends ArrayAdapter<GroupItem> { private final int pad; GroupAdapter(Context ctx) { super(ctx, android.R.layout.simple_expandable_list_item_1, - new ArrayList<MessageHeader>()); + new ArrayList<GroupItem>()); pad = LayoutUtils.getPadding(ctx); } @Override public View getView(int position, View convertView, ViewGroup parent) { - MessageHeader header = getItem(position); + GroupItem item = getItem(position); + MessageHeader header = item.getHeader(); Context ctx = getContext(); + Resources res = ctx.getResources(); - LinearLayout layout = new LinearLayout(ctx); - layout.setOrientation(HORIZONTAL); - layout.setGravity(CENTER_VERTICAL); - if(!header.isRead()) { - Resources res = ctx.getResources(); - layout.setBackgroundColor(res.getColor(R.color.unread_background)); - } + LinearLayout headerLayout = new LinearLayout(ctx); + headerLayout.setOrientation(HORIZONTAL); + headerLayout.setGravity(CENTER_VERTICAL); + int background; + if(header.isRead()) background = res.getColor(R.color.read_background); + else background = res.getColor(R.color.unread_background); + headerLayout.setBackgroundColor(background); AuthorView authorView = new AuthorView(ctx); authorView.setLayoutParams(WRAP_WRAP_1); Author author = header.getAuthor(); if(author == null) authorView.init(null, header.getAuthorStatus()); else authorView.init(author.getName(), header.getAuthorStatus()); - layout.addView(authorView); + headerLayout.addView(authorView); + // FIXME: Factor this out into a TimestampView TextView date = new TextView(ctx); date.setTextSize(14); date.setPadding(0, pad, pad, pad); long then = header.getTimestamp(), now = System.currentTimeMillis(); date.setText(DateUtils.formatSameDayTime(then, now, SHORT, SHORT)); - layout.addView(date); + headerLayout.addView(date); + + if(!item.isExpanded()) return headerLayout; + + LinearLayout expanded = new LinearLayout(ctx); + expanded.setOrientation(VERTICAL); + expanded.setGravity(CENTER_HORIZONTAL); + expanded.setBackgroundColor(background); + expanded.addView(headerLayout); + + byte[] body = item.getBody(); + if(body == null) { + ProgressBar progress = new ProgressBar(ctx); + progress.setPadding(pad, 0, pad, pad); + progress.setIndeterminate(true); + expanded.addView(progress); + } else if(header.getContentType().equals("text/plain")) { + TextView text = new TextView(ctx); + text.setPadding(pad, 0, pad, pad); + text.setBackgroundColor(background); + text.setText(StringUtils.fromUtf8(body)); + expanded.addView(text); + } - return layout; + return expanded; } } diff --git a/briar-android/src/org/briarproject/android/groups/GroupItem.java b/briar-android/src/org/briarproject/android/groups/GroupItem.java new file mode 100644 index 0000000000..d3ec3cf03b --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/GroupItem.java @@ -0,0 +1,37 @@ +package org.briarproject.android.groups; + +import org.briarproject.api.db.MessageHeader; + +// This class is not thread-safe +class GroupItem { + + private final MessageHeader header; + private boolean expanded; + private byte[] body; + + GroupItem(MessageHeader header) { + this.header = header; + expanded = false; + body = null; + } + + MessageHeader getHeader() { + return header; + } + + boolean isExpanded() { + return expanded; + } + + void setExpanded(boolean expanded) { + this.expanded = expanded; + } + + byte[] getBody() { + return body; + } + + void setBody(byte[] body) { + this.body = body; + } +} diff --git a/briar-android/src/org/briarproject/android/groups/GroupItemComparator.java b/briar-android/src/org/briarproject/android/groups/GroupItemComparator.java new file mode 100644 index 0000000000..858c0b2c59 --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/GroupItemComparator.java @@ -0,0 +1,17 @@ +package org.briarproject.android.groups; + +import java.util.Comparator; + +class GroupItemComparator implements Comparator<GroupItem> { + + static final GroupItemComparator INSTANCE = new GroupItemComparator(); + + public int compare(GroupItem a, GroupItem 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; + } +} -- GitLab