diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index e56b8652ad0ec137eb3e2e7598ae9b54b7d32c8d..468defb0a0cf2550e89af0c2bf1b90cf495f1a76 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -6,6 +6,8 @@ <string name="contact_list_button">Contacts</string> <string name="quit_button">Quit</string> <string name="contact_list_title">Contacts</string> + <string name="contact_connected">Connected</string> + <string name="contact_last_connected">Last connected <br /> %s</string> <string name="add_contact_button">Add a contact</string> <string name="add_contact_title">Add a Contact</string> <string name="same_network">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.</string> diff --git a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java index c6db489c9340f4e32bf8256c36f8b963c5bb0214..ee63c84bb5144bec6823ec11c1c7b8c4d9b53873 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 c08ecde48851b9a974f610387db6b76147b68b21..ec57d89e0d60756bbdccf9778c7b41c1b8eb3e1c 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<ContactListItem>(this, - android.R.layout.simple_expandable_list_item_1, - new ArrayList<ContactListItem>()); + 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<Contact> 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<Contact> 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<Contact> 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<ContactListItem> { - - @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 0000000000000000000000000000000000000000..c9720ad7710fd7fa4d82680f6ca61a81e8212587 --- /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<ContactListItem> { + + public ContactListAdapter(Context context) { + super(context, android.R.layout.simple_expandable_list_item_1, + new ArrayList<ContactListItem>()); + } + + @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 0000000000000000000000000000000000000000..99d2a79fcc707dfc4b6be43eaf24ee4d86f44eac --- /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<ContactListItem> 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<ContactListItem> { + + @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