diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index e56b8652a..468defb0a 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -6,6 +6,8 @@
Contacts
Quit
Contacts
+ Connected
+ Last connected <br /> %s
Add a contact
Add a Contact
Briar can add contacts via Wi-Fi or Bluetooth. For security reasons, you must be face-to-face to add someone as a contact. To use Wi-Fi you must both be connected to the same network.
diff --git a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
index c6db489c9..ee63c84bb 100644
--- a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
+++ b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
@@ -20,8 +20,8 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
import android.widget.ProgressBar;
-import android.widget.RelativeLayout.LayoutParams;
public class HomeScreenActivity extends BriarActivity
implements OnClickListener {
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
index c08ecde48..ec57d89e0 100644
--- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java
@@ -7,9 +7,7 @@ import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Comparator;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -25,6 +23,7 @@ import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DatabaseExecutor;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.event.ContactAddedEvent;
+import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.transport.ConnectionListener;
@@ -37,8 +36,8 @@ import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
-import android.widget.RelativeLayout.LayoutParams;
import com.google.inject.Inject;
@@ -47,7 +46,6 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
private static final Logger LOG =
Logger.getLogger(ContactListActivity.class.getName());
- private static final ItemComparator COMPARATOR = new ItemComparator();
private final BriarServiceConnection serviceConnection =
new BriarServiceConnection();
@@ -67,10 +65,10 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
- adapter = new ArrayAdapter(this,
- android.R.layout.simple_expandable_list_item_1,
- new ArrayList());
+ adapter = new ContactListAdapter(this);
ListView listView = new ListView(this);
+ listView.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT,
+ 1f));
listView.setAdapter(adapter);
layout.addView(listView);
@@ -85,14 +83,13 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
setContentView(layout);
- // Listen for database events and connection events
+ // Listen for contacts being added or removed
db.addListener(this);
+ // Listen for contacts connecting or disconnecting
connectionRegistry.addListener(this);
-
- // Bind to the service
+ // Bind to the service so we can wait for the DB to be opened
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
-
// Load the contact list from the DB
reloadContactList();
@@ -105,9 +102,12 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
IBinder binder = serviceConnection.waitForBinder();
((BriarBinder) binder).getService().waitForStartup();
if(LOG.isLoggable(INFO)) LOG.info("Service started");
- // Insert a couple of fake contacts
- db.addContact("Alice");
- db.addContact("Bob");
+ Collection contacts = db.getContacts();
+ if(contacts.isEmpty()) {
+ // Insert a couple of fake contacts
+ db.addContact("Alice");
+ db.addContact("Bob");
+ }
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -134,6 +134,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
public void eventOccurred(DatabaseEvent e) {
if(e instanceof ContactAddedEvent) reloadContactList();
+ else if(e instanceof ContactRemovedEvent) reloadContactList();
}
private void reloadContactList() {
@@ -148,11 +149,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
if(LOG.isLoggable(INFO))
LOG.info("Loaded " + contacts.size() + " contacts");
// Update the contact list
- runOnUiThread(new Runnable() {
- public void run() {
- updateContactList(contacts);
- }
- });
+ updateContactList(contacts);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -165,14 +162,17 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
});
}
- // UI thread
- private void updateContactList(Collection contacts) {
- adapter.clear();
- for(Contact c : contacts) {
- boolean connected = connectionRegistry.isConnected(c.getId());
- adapter.add(new ContactListItem(c, connected));
- }
- adapter.sort(COMPARATOR);
+ private void updateContactList(final Collection contacts) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ adapter.clear();
+ for(Contact c : contacts) {
+ boolean conn = connectionRegistry.isConnected(c.getId());
+ adapter.add(new ContactListItem(c, conn));
+ }
+ adapter.sort(ContactListItem.COMPARATOR);
+ }
+ });
}
public void contactConnected(final ContactId c) {
@@ -189,37 +189,12 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
int count = adapter.getCount();
for(int i = 0; i < count; i++) {
ContactListItem item = adapter.getItem(i);
- if(item.contact.getId().equals(c)) {
- item.connected = connected;
+ if(item.getContactId().equals(c)) {
+ item.setConnected(connected);
return;
}
}
}
});
}
-
- private static class ItemComparator implements Comparator {
-
- @Override
- public int compare(ContactListItem a, ContactListItem b) {
- return String.CASE_INSENSITIVE_ORDER.compare(a.contact.getName(),
- b.contact.getName());
- }
- }
-
- private static class ContactListItem {
-
- private final Contact contact;
- private boolean connected; // UI thread
-
- private ContactListItem(Contact contact, boolean connected) {
- this.contact = contact;
- this.connected = connected;
- }
-
- @Override
- public String toString() {
- return contact.getName() + " (" + connected + ")";
- }
- }
}
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
new file mode 100644
index 000000000..c9720ad77
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListAdapter.java
@@ -0,0 +1,64 @@
+package net.sf.briar.android.contact;
+
+import static android.view.Gravity.CENTER;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.widget.LinearLayout.HORIZONTAL;
+
+import java.util.ArrayList;
+
+import net.sf.briar.R;
+import android.content.Context;
+import android.text.Html;
+import android.text.format.DateUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+public class ContactListAdapter extends ArrayAdapter {
+
+ public ContactListAdapter(Context context) {
+ super(context, android.R.layout.simple_expandable_list_item_1,
+ new ArrayList());
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ContactListItem item = getItem(position);
+ Context ctx = getContext();
+ LinearLayout layout = new LinearLayout(ctx);
+ layout.setOrientation(HORIZONTAL);
+ layout.setGravity(CENTER);
+
+ ImageView bulb = new ImageView(ctx);
+ if(item.getConnected()) bulb.setImageResource(R.drawable.green_bulb);
+ else bulb.setImageResource(R.drawable.grey_bulb);
+ bulb.setPadding(5, 0, 5, 0);
+ layout.addView(bulb);
+
+ TextView name = new TextView(ctx);
+ name.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1f));
+ name.setTextSize(18);
+ name.setText(item.getName());
+ layout.addView(name);
+
+ TextView connected = new TextView(ctx);
+ connected.setTextSize(12);
+ connected.setPadding(5, 0, 5, 0);
+ if(item.getConnected()) {
+ connected.setText(R.string.contact_connected);
+ } else {
+ String format = ctx.getResources().getString(
+ R.string.contact_last_connected);
+ long then = item.getLastConnected();
+ CharSequence ago = DateUtils.getRelativeTimeSpanString(then);
+ connected.setText(Html.fromHtml(String.format(format, ago)));
+ }
+ layout.addView(connected);
+
+ return layout;
+ }
+}
diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListItem.java b/briar-android/src/net/sf/briar/android/contact/ContactListItem.java
new file mode 100644
index 000000000..99d2a79fc
--- /dev/null
+++ b/briar-android/src/net/sf/briar/android/contact/ContactListItem.java
@@ -0,0 +1,49 @@
+package net.sf.briar.android.contact;
+
+import java.util.Comparator;
+
+import net.sf.briar.api.Contact;
+import net.sf.briar.api.ContactId;
+
+// This class is not thread-safe
+class ContactListItem {
+
+ static Comparator COMPARATOR = new ItemComparator();
+
+ private final Contact contact;
+ private boolean connected;
+
+ ContactListItem(Contact contact, boolean connected) {
+ this.contact = contact;
+ this.connected = connected;
+ }
+
+ ContactId getContactId() {
+ return contact.getId();
+ }
+
+ String getName() {
+ return contact.getName();
+ }
+
+ long getLastConnected() {
+ return contact.getLastConnected();
+ }
+
+ boolean getConnected() {
+ return connected;
+ }
+
+ void setConnected(boolean connected) {
+ this.connected = connected;
+ }
+
+ private static class ItemComparator implements Comparator {
+
+ @Override
+ public int compare(ContactListItem a, ContactListItem b) {
+ return String.CASE_INSENSITIVE_ORDER.compare(a.contact.getName(),
+ b.contact.getName());
+ }
+ }
+}
\ No newline at end of file