diff --git a/briar-android/build.gradle b/briar-android/build.gradle
index 3e6d17c84..f06ca62b1 100644
--- a/briar-android/build.gradle
+++ b/briar-android/build.gradle
@@ -7,6 +7,7 @@ dependencies {
compile project(':briar-core')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:appcompat-v7:23.1.1"
+ compile 'com.android.support:recyclerview-v7:23.1.1'
}
android {
diff --git a/briar-android/res/drawable-hdpi/contact_connected.png b/briar-android/res/drawable-hdpi/contact_connected.png
deleted file mode 100644
index 9fa452b33..000000000
Binary files a/briar-android/res/drawable-hdpi/contact_connected.png and /dev/null differ
diff --git a/briar-android/res/drawable-hdpi/contact_disconnected.png b/briar-android/res/drawable-hdpi/contact_disconnected.png
deleted file mode 100644
index 9de496849..000000000
Binary files a/briar-android/res/drawable-hdpi/contact_disconnected.png and /dev/null differ
diff --git a/briar-android/res/drawable-hdpi/social_add_person.png b/briar-android/res/drawable-hdpi/social_add_person.png
deleted file mode 100644
index 38b91cc7c..000000000
Binary files a/briar-android/res/drawable-hdpi/social_add_person.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/contact_connected.png b/briar-android/res/drawable-mdpi/contact_connected.png
deleted file mode 100644
index d91970da5..000000000
Binary files a/briar-android/res/drawable-mdpi/contact_connected.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/contact_disconnected.png b/briar-android/res/drawable-mdpi/contact_disconnected.png
deleted file mode 100644
index bef15c40b..000000000
Binary files a/briar-android/res/drawable-mdpi/contact_disconnected.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/social_add_person.png b/briar-android/res/drawable-mdpi/social_add_person.png
deleted file mode 100644
index 2c9899ec8..000000000
Binary files a/briar-android/res/drawable-mdpi/social_add_person.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/contact_connected.png b/briar-android/res/drawable-xhdpi/contact_connected.png
deleted file mode 100644
index 4341f01a1..000000000
Binary files a/briar-android/res/drawable-xhdpi/contact_connected.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/contact_disconnected.png b/briar-android/res/drawable-xhdpi/contact_disconnected.png
deleted file mode 100644
index d0499ddf1..000000000
Binary files a/briar-android/res/drawable-xhdpi/contact_disconnected.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/social_add_person.png b/briar-android/res/drawable-xhdpi/social_add_person.png
deleted file mode 100644
index 2cefca645..000000000
Binary files a/briar-android/res/drawable-xhdpi/social_add_person.png and /dev/null differ
diff --git a/briar-android/res/drawable/contact_connected.xml b/briar-android/res/drawable/contact_connected.xml
new file mode 100644
index 000000000..95e7a0bc0
--- /dev/null
+++ b/briar-android/res/drawable/contact_connected.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/drawable/contact_disconnected.xml b/briar-android/res/drawable/contact_disconnected.xml
new file mode 100644
index 000000000..d8ee38c1e
--- /dev/null
+++ b/briar-android/res/drawable/contact_disconnected.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/briar-android/res/drawable/social_add_person.xml b/briar-android/res/drawable/social_add_person.xml
new file mode 100644
index 000000000..982183dea
--- /dev/null
+++ b/briar-android/res/drawable/social_add_person.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/briar-android/res/drawable/social_remove_person.xml b/briar-android/res/drawable/social_remove_person.xml
new file mode 100644
index 000000000..4adacc148
--- /dev/null
+++ b/briar-android/res/drawable/social_remove_person.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/briar-android/res/layout/activity_contact_list.xml b/briar-android/res/layout/activity_contact_list.xml
new file mode 100644
index 000000000..acde6fdc9
--- /dev/null
+++ b/briar-android/res/layout/activity_contact_list.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/list_item_contact.xml b/briar-android/res/layout/list_item_contact.xml
new file mode 100644
index 000000000..69ec0383e
--- /dev/null
+++ b/briar-android/res/layout/list_item_contact.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/transports_list.xml b/briar-android/res/layout/transports_list.xml
index c1ec632f6..c359d7c1d 100644
--- a/briar-android/res/layout/transports_list.xml
+++ b/briar-android/res/layout/transports_list.xml
@@ -5,10 +5,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
+
+
\ No newline at end of file
diff --git a/briar-android/res/menu/contact_list_actions.xml b/briar-android/res/menu/contact_list_actions.xml
new file mode 100644
index 000000000..718200a65
--- /dev/null
+++ b/briar-android/res/menu/contact_list_actions.xml
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index fe43fd7c2..0a7c7b1f0 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -126,4 +126,6 @@
Lost password
Password recovery is not possible. Do you wish to delete your user, all contacts, and re-register ?
+ Confirm Contact Deletion
+ Are you sure that you want to remove this contact and all messages exchanged with this contact?
diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml
index f40b8b541..0e0c2c0b9 100644
--- a/briar-android/res/values/styles.xml
+++ b/briar-android/res/values/styles.xml
@@ -33,4 +33,13 @@
- @dimen/text_size_small
- @android:color/primary_text_light
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
index fff550fc4..1edbd2d2a 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java
@@ -1,28 +1,20 @@
package org.briarproject.android.contact;
import android.content.Intent;
-import android.content.res.Resources;
+import android.graphics.PorterDuff;
import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
+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.View.OnCreateContextMenuListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-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.invitation.AddContactActivity;
-import org.briarproject.android.util.HorizontalBorder;
-import org.briarproject.android.util.ListLoadingProgressBar;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
@@ -36,43 +28,34 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageAddedEvent;
-import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageHeader;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sync.GroupId;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
-import static android.view.Gravity.CENTER;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Menu.NONE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
-import static android.widget.LinearLayout.VERTICAL;
-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.util.CommonLayoutParams.MATCH_MATCH;
-import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP;
-import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1;
public class ContactListActivity extends BriarActivity
-implements OnClickListener, OnItemClickListener, OnCreateContextMenuListener,
-EventListener {
+ implements OnCreateContextMenuListener, EventListener {
- private static final int MENU_ITEM_DELETE = 1;
private static final Logger LOG =
Logger.getLogger(ContactListActivity.class.getName());
@Inject private ConnectionRegistry connectionRegistry;
private TextView empty = null;
private ContactListAdapter adapter = null;
- private ListView list = null;
- private ListLoadingProgressBar loading = null;
+ private RecyclerView list = null;
+ private ProgressBar loading = null;
// Fields that are accessed from background threads must be volatile
@Inject private volatile ContactManager contactManager;
@@ -82,47 +65,28 @@ EventListener {
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
- LinearLayout layout = new LinearLayout(this);
- layout.setLayoutParams(MATCH_MATCH);
- layout.setOrientation(VERTICAL);
- layout.setGravity(CENTER_HORIZONTAL);
- empty = new TextView(this);
- empty.setLayoutParams(MATCH_WRAP_1);
- empty.setGravity(CENTER);
- empty.setTextSize(18);
- empty.setText(R.string.no_contacts);
- empty.setVisibility(GONE);
- layout.addView(empty);
+ setContentView(R.layout.activity_contact_list);
adapter = new ContactListAdapter(this);
- list = new ListView(this);
- list.setLayoutParams(MATCH_WRAP_1);
+ list = (RecyclerView) findViewById(R.id.contactList);
+ list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
- list.setOnItemClickListener(this);
list.setOnCreateContextMenuListener(this);
list.setVisibility(GONE);
- layout.addView(list);
+
+ // Show a notice when there are no contacts
+ empty = (TextView) findViewById(R.id.emptyView);
// Show a progress bar while the list is loading
- loading = new ListLoadingProgressBar(this);
- layout.addView(loading);
+ loading = (ProgressBar) findViewById(R.id.progressBar);
+ loading.setVisibility(VISIBLE);
+ }
- layout.addView(new HorizontalBorder(this));
-
- LinearLayout footer = new LinearLayout(this);
- footer.setLayoutParams(MATCH_WRAP);
- footer.setGravity(CENTER);
- Resources res = getResources();
- footer.setBackgroundColor(res.getColor(R.color.button_bar_background));
- ImageButton addContactButton = new ImageButton(this);
- addContactButton.setBackgroundResource(0);
- addContactButton.setImageResource(R.drawable.social_add_person);
- addContactButton.setOnClickListener(this);
- footer.addView(addContactButton);
- layout.addView(footer);
-
- setContentView(layout);
+ @Override
+ public void onPause() {
+ super.onPause();
+ eventBus.removeListener(this);
}
@Override
@@ -132,12 +96,47 @@ EventListener {
loadContacts();
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu items for use in the action bar
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.contact_list_actions, menu);
+
+ // adapt icon color to dark action bar
+ menu.findItem(R.id.action_social_add_person).getIcon().setColorFilter(
+ getResources().getColor(R.color.action_bar_text),
+ PorterDuff.Mode.SRC_IN);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.action_social_add_person:
+ startActivity(new Intent(this, AddContactActivity.class));
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+
private void loadContacts() {
- clearContacts();
+ runOnUiThread(new Runnable() {
+ public void run() {
+ empty.setVisibility(GONE);
+ list.setVisibility(GONE);
+ loading.setVisibility(VISIBLE);
+ }
+ });
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
+ List contacts =
+ new ArrayList();
for (Contact c : contactManager.getContacts()) {
try {
ContactId id = c.getId();
@@ -145,15 +144,20 @@ EventListener {
messagingManager.getConversationId(id);
Collection headers =
messagingManager.getMessageHeaders(id);
- displayContact(c, conversation, headers);
+
+ boolean connected =
+ connectionRegistry.isConnected(c.getId());
+ contacts.add(new ContactListItem(c, connected,
+ conversation,
+ headers));
} catch (NoSuchContactException e) {
// Continue
}
}
+ displayContacts(contacts);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Full load took " + duration + " ms");
- hideProgressBar();
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -162,110 +166,19 @@ EventListener {
});
}
- private void clearContacts() {
+ private void displayContacts(final List contacts) {
runOnUiThread(new Runnable() {
public void run() {
- empty.setVisibility(GONE);
- list.setVisibility(GONE);
- loading.setVisibility(VISIBLE);
- adapter.clear();
- adapter.notifyDataSetChanged();
- }
- });
- }
-
- private void displayContact(final Contact c, final GroupId conversation,
- final Collection headers) {
- runOnUiThread(new Runnable() {
- public void run() {
- list.setVisibility(VISIBLE);
- loading.setVisibility(GONE);
- boolean connected = connectionRegistry.isConnected(c.getId());
- // Remove the old item, if any
- ContactListItem item = findItem(c.getId());
- if (item != null) adapter.remove(item);
- // Add a new item
- adapter.add(new ContactListItem(c, connected, conversation,
- headers));
- adapter.sort(ContactListItemComparator.INSTANCE);
- adapter.notifyDataSetChanged();
- }
- });
- }
-
- private void hideProgressBar() {
- runOnUiThread(new Runnable() {
- public void run() {
- if (adapter.isEmpty()) empty.setVisibility(VISIBLE);
- else list.setVisibility(VISIBLE);
- loading.setVisibility(GONE);
- }
- });
- }
-
- private ContactListItem findItem(ContactId c) {
- int count = adapter.getCount();
- for (int i = 0; i < count; i++) {
- ContactListItem item = adapter.getItem(i);
- if (item.getContact().getId().equals(c)) return item;
- }
- return null; // Not found
- }
-
- @Override
- public void onPause() {
- super.onPause();
- eventBus.removeListener(this);
- }
-
- public void onClick(View view) {
- startActivity(new Intent(this, AddContactActivity.class));
- }
-
- public void onItemClick(AdapterView> parent, View view, int position,
- long id) {
- ContactListItem item = adapter.getItem(position);
- ContactId contactId = item.getContact().getId();
- String contactName = item.getContact().getAuthor().getName();
- GroupId groupId = item.getConversationId();
- AuthorId localAuthorId = item.getContact().getLocalAuthorId();
- Intent i = new Intent(this, ConversationActivity.class);
- i.putExtra("briar.CONTACT_ID", contactId.getInt());
- i.putExtra("briar.CONTACT_NAME", contactName);
- i.putExtra("briar.GROUP_ID", groupId.getBytes());
- i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
- startActivity(i);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view,
- ContextMenu.ContextMenuInfo info) {
- String delete = getString(R.string.delete_contact);
- menu.add(NONE, MENU_ITEM_DELETE, NONE, delete);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- if (menuItem.getItemId() == MENU_ITEM_DELETE) {
- ContextMenuInfo info = menuItem.getMenuInfo();
- int position = ((AdapterContextMenuInfo) info).position;
- ContactListItem item = adapter.getItem(position);
- removeContact(item.getContact().getId());
- String deleted = getString(R.string.contact_deleted_toast);
- Toast.makeText(this, deleted, LENGTH_SHORT).show();
- }
- return true;
- }
-
- private void removeContact(final ContactId c) {
- runOnDbThread(new Runnable() {
- public void run() {
- try {
- contactManager.removeContact(c);
- } catch (DbException e) {
- if (LOG.isLoggable(WARNING))
- LOG.log(WARNING, e.toString(), e);
+ if(contacts.size() > 0) {
+ list.setVisibility(VISIBLE);
+ empty.setVisibility(GONE);
+ } else {
+ list.setVisibility(GONE);
+ empty.setVisibility(VISIBLE);
}
+ loading.setVisibility(GONE);
+
+ adapter.addAll(contacts);
}
});
}
@@ -314,7 +227,7 @@ EventListener {
final Collection headers) {
runOnUiThread(new Runnable() {
public void run() {
- ContactListItem item = findItem(c);
+ ContactListItem item = adapter.findItem(c);
if (item != null) {
item.setHeaders(headers);
adapter.notifyDataSetChanged();
@@ -326,10 +239,10 @@ EventListener {
private void removeItem(final ContactId c) {
runOnUiThread(new Runnable() {
public void run() {
- ContactListItem item = findItem(c);
+ ContactListItem item = adapter.findItem(c);
if (item != null) {
adapter.remove(item);
- adapter.notifyDataSetChanged();
+
if (adapter.isEmpty()) {
empty.setVisibility(VISIBLE);
list.setVisibility(GONE);
@@ -342,7 +255,7 @@ EventListener {
private void setConnected(final ContactId c, final boolean connected) {
runOnUiThread(new Runnable() {
public void run() {
- ContactListItem item = findItem(c);
+ ContactListItem item = adapter.findItem(c);
if (item != null) {
item.setConnected(connected);
adapter.notifyDataSetChanged();
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
index 1acb52435..7e511ea9a 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
@@ -1,80 +1,194 @@
package org.briarproject.android.contact;
-import static android.text.TextUtils.TruncateAt.END;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.widget.LinearLayout.HORIZONTAL;
-import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1;
-
-import java.util.ArrayList;
-
-import org.briarproject.R;
-import org.briarproject.android.util.LayoutUtils;
-
import android.content.Context;
+import android.content.Intent;
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.ImageView;
-import android.widget.LinearLayout;
import android.widget.TextView;
-class ContactListAdapter extends ArrayAdapter {
+import org.briarproject.R;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.sync.GroupId;
- private final int pad;
+import java.util.List;
- ContactListAdapter(Context ctx) {
- super(ctx, android.R.layout.simple_expandable_list_item_1,
- new ArrayList());
- pad = LayoutUtils.getPadding(ctx);
+public class ContactListAdapter
+ extends RecyclerView.Adapter {
+
+ private SortedList contacts =
+ new SortedList(ContactListItem.class,
+ new SortedList.Callback() {
+ @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(ContactListItem c1,
+ ContactListItem c2) {
+ return (int) (c1.getTimestamp() -
+ c2.getTimestamp());
+ }
+
+ @Override
+ public boolean areItemsTheSame(ContactListItem c1,
+ ContactListItem c2) {
+ return c1.getContact().getId().equals(c2.getContact().getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(ContactListItem c1,
+ ContactListItem c2) {
+ return c1.equals(c2);
+ }
+ });
+ private Context ctx;
+
+ public ContactListAdapter(Context context) {
+ ctx = context;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ContactListItem item = getItem(position);
- Context ctx = getContext();
+ public ContactHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ View v = LayoutInflater.from(viewGroup.getContext())
+ .inflate(R.layout.list_item_contact, viewGroup, false);
+
+ return new ContactHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(final ContactHolder ui, final int position) {
+ final ContactListItem item = getItem(position);
Resources res = ctx.getResources();
- LinearLayout layout = new LinearLayout(ctx);
- layout.setOrientation(HORIZONTAL);
- layout.setGravity(CENTER_VERTICAL);
int unread = item.getUnreadCount();
- if (unread > 0)
- layout.setBackgroundColor(res.getColor(R.color.unread_background));
-
- ImageView bulb = new ImageView(ctx);
- bulb.setPadding(pad, pad, pad, pad);
- if (item.isConnected())
- bulb.setImageResource(R.drawable.contact_connected);
- else bulb.setImageResource(R.drawable.contact_disconnected);
- layout.addView(bulb);
-
- TextView name = new TextView(ctx);
- name.setLayoutParams(WRAP_WRAP_1);
- name.setTextSize(18);
- name.setSingleLine();
- name.setEllipsize(END);
- name.setPadding(0, pad, pad, pad);
- String contactName = item.getContact().getAuthor().getName();
- if (unread > 0) name.setText(contactName + " (" + unread + ")");
- else name.setText(contactName);
- layout.addView(name);
-
- if (item.isEmpty()) {
- TextView noMessages = new TextView(ctx);
- noMessages.setPadding(pad, pad, pad, pad);
- noMessages.setTextColor(res.getColor(R.color.no_private_messages));
- noMessages.setText(R.string.no_private_messages);
- layout.addView(noMessages);
- } else {
- TextView date = new TextView(ctx);
- date.setPadding(pad, pad, pad, pad);
- long timestamp = item.getTimestamp();
- date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
- layout.addView(date);
+ if (unread > 0) {
+ ui.layout.setBackgroundColor(
+ res.getColor(R.color.unread_background));
}
- return layout;
+ if (item.isConnected()) {
+ ui.bulb.setImageResource(R.drawable.contact_connected);
+ } else {
+ ui.bulb.setImageResource(R.drawable.contact_disconnected);
+ }
+
+ String contactName = item.getContact().getAuthor().getName();
+ if (unread > 0) {
+ ui.name.setText(contactName + " (" + unread + ")");
+ } else {
+ ui.name.setText(contactName);
+ }
+
+ if (item.isEmpty()) {
+ ui.date.setText(R.string.no_private_messages);
+ } else {
+ long timestamp = item.getTimestamp();
+ ui.date.setText(
+ DateUtils.getRelativeTimeSpanString(ctx, timestamp));
+ }
+
+ ui.layout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ContactId contactId = item.getContact().getId();
+ String contactName = item.getContact().getAuthor().getName();
+ GroupId groupId = item.getConversationId();
+ AuthorId localAuthorId = item.getContact().getLocalAuthorId();
+
+ Intent i = new Intent(ctx, ConversationActivity.class);
+ i.putExtra("briar.CONTACT_ID", contactId.getInt());
+ i.putExtra("briar.CONTACT_NAME", contactName);
+ i.putExtra("briar.GROUP_ID", groupId.getBytes());
+ i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
+
+ ctx.startActivity(i);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return contacts == null ? 0 : contacts.size();
+ }
+
+ public boolean isEmpty() {
+ return contacts == null || contacts.size() == 0;
+ }
+
+ public ContactListItem getItem(int position) {
+ if (position == -1 || contacts.size() <= position) {
+ return null; // Not found
+ }
+ return contacts.get(position);
+ }
+
+ public ContactListItem findItem(ContactId c) {
+ int count = getItemCount();
+ for (int i = 0; i < count; i++) {
+ ContactListItem item = getItem(i);
+ if (item.getContact().getId().equals(c)) return item;
+ }
+ return null; // Not found
+ }
+
+ public void addAll(final List contacts) {
+ this.contacts.addAll(contacts);
+ }
+
+ public void add(final ContactListItem contact) {
+ this.contacts.add(contact);
+ }
+
+ public void remove(final ContactListItem contact) {
+ this.contacts.remove(contact);
+ }
+
+ public void clear() {
+ contacts.beginBatchedUpdates();
+
+ while(contacts.size() != 0) {
+ contacts.removeItemAt(0);
+ }
+
+ contacts.endBatchedUpdates();
+ }
+
+ public static class ContactHolder extends RecyclerView.ViewHolder {
+ public ViewGroup layout;
+ public ImageView bulb;
+ public TextView name;
+ public TextView date;
+
+ public ContactHolder(View v) {
+ super(v);
+
+ layout = (ViewGroup) v;
+ bulb = (ImageView) v.findViewById(R.id.bulbView);
+ name = (TextView) v.findViewById(R.id.nameView);
+ date = (TextView) v.findViewById(R.id.dateView);
+ }
}
}
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index ff90470fe..98dcd179f 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -1,10 +1,16 @@
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.Bundle;
import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+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;
@@ -15,6 +21,7 @@ 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;
@@ -66,6 +73,7 @@ import javax.inject.Inject;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
+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;
@@ -161,6 +169,33 @@ implements EventListener, OnClickListener, OnItemClickListener {
loadHeaders();
}
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu items for use in the action bar
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.contact_actions, menu);
+
+ // adapt icon color to dark action bar
+ menu.findItem(R.id.action_social_remove_person).getIcon().setColorFilter(
+ getResources().getColor(R.color.action_bar_text),
+ PorterDuff.Mode.SRC_IN);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case R.id.action_social_remove_person:
+ askToRemoveContact();
+
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
private void loadContactAndGroup() {
runOnDbThread(new Runnable() {
public void run() {
@@ -478,4 +513,59 @@ implements EventListener, OnClickListener, OnItemClickListener {
i.putExtra("briar.POSITION", position);
startActivityForResult(i, REQUEST_READ);
}
+
+ private void askToRemoveContact() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ DialogInterface.OnClickListener okListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog,
+ int which) {
+ removeContact();
+ }
+ };
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(ConversationActivity.this);
+ builder.setTitle(
+ getString(R.string.dialog_title_delete_contact));
+ builder.setMessage(
+ getString(R.string.dialog_message_delete_contact));
+ builder.setPositiveButton(android.R.string.ok, okListener);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.show();
+ }
+ });
+ }
+
+ private void removeContact() {
+ runOnDbThread(new Runnable() {
+ public void run() {
+ try {
+ contactManager.removeContact(contactId);
+ } catch (DbException e) {
+ if (LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ } finally {
+ finishAfterContactRemoved();
+ }
+ }
+ });
+ }
+
+ private void finishAfterContactRemoved() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String deleted = getString(R.string.contact_deleted_toast);
+ Toast.makeText(ConversationActivity.this, deleted, LENGTH_SHORT)
+ .show();
+
+ finish();
+ }
+ });
+ }
+
}