Added conversation list screen, minor tweaks to contact list screen.
@@ -40,5 +40,9 @@
|
|||||||
android:name=".android.invitation.AddContactActivity"
|
android:name=".android.invitation.AddContactActivity"
|
||||||
android:label="@string/add_contact_title" >
|
android:label="@string/add_contact_title" >
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".android.messages.ConversationListActivity"
|
||||||
|
android:label="@string/messages_title" >
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
BIN
briar-android/res/drawable-hdpi/rating_important.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
briar-android/res/drawable-hdpi/rating_not_important.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
briar-android/res/drawable-mdpi/rating_important.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
briar-android/res/drawable-mdpi/rating_not_important.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
briar-android/res/drawable-xhdpi/content_new_email.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
briar-android/res/drawable-xhdpi/rating_important.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
briar-android/res/drawable-xhdpi/rating_not_important.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
@@ -12,7 +12,7 @@
|
|||||||
<string name="contact_list_title">Contacts</string>
|
<string name="contact_list_title">Contacts</string>
|
||||||
<string name="contact_connected">Connected</string>
|
<string name="contact_connected">Connected</string>
|
||||||
<string name="contact_last_connected">Last connected <br /> %s</string>
|
<string name="contact_last_connected">Last connected <br /> %s</string>
|
||||||
<string name="add_contact_button">Add a contact</string>
|
<string name="add_contact_button">New Contact</string>
|
||||||
<string name="add_contact_title">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>
|
<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>
|
||||||
<string name="wifi_not_available">Wi-Fi is not available on this device</string>
|
<string name="wifi_not_available">Wi-Fi is not available on this device</string>
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
<string name="interfering">This could mean that someone is trying to interfere with your connection.</string>
|
<string name="interfering">This could mean that someone is trying to interfere with your connection.</string>
|
||||||
<string name="contact_added">Contact added</string>
|
<string name="contact_added">Contact added</string>
|
||||||
<string name="enter_nickname">Please enter a nickname for this contact:</string>
|
<string name="enter_nickname">Please enter a nickname for this contact:</string>
|
||||||
<string name="add_another_contact_button">Add another contact</string>
|
<string name="messages_title">Messages</string>
|
||||||
|
<string name="compose_button">New Message</string>
|
||||||
<string name="done_button">Done</string>
|
<string name="done_button">Done</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import net.sf.briar.R;
|
|||||||
import net.sf.briar.android.BriarService.BriarBinder;
|
import net.sf.briar.android.BriarService.BriarBinder;
|
||||||
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||||
import net.sf.briar.android.contact.ContactListActivity;
|
import net.sf.briar.android.contact.ContactListActivity;
|
||||||
|
import net.sf.briar.android.messages.ConversationListActivity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -78,7 +79,8 @@ public class HomeScreenActivity extends BriarActivity {
|
|||||||
messagesButton.setText(R.string.messages_button);
|
messagesButton.setText(R.string.messages_button);
|
||||||
messagesButton.setOnClickListener(new OnClickListener() {
|
messagesButton.setOnClickListener(new OnClickListener() {
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
// FIXME: Hook this button up to an activity
|
startActivity(new Intent(HomeScreenActivity.this,
|
||||||
|
ConversationListActivity.class));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
buttons.add(messagesButton);
|
buttons.add(messagesButton);
|
||||||
|
|||||||
@@ -59,26 +59,25 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
super.onCreate(null);
|
super.onCreate(null);
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Created");
|
|
||||||
LinearLayout layout = new LinearLayout(this);
|
LinearLayout layout = new LinearLayout(this);
|
||||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||||
layout.setOrientation(VERTICAL);
|
layout.setOrientation(VERTICAL);
|
||||||
layout.setGravity(CENTER_HORIZONTAL);
|
layout.setGravity(CENTER_HORIZONTAL);
|
||||||
|
|
||||||
adapter = new ContactListAdapter(this);
|
adapter = new ContactListAdapter(this);
|
||||||
ListView listView = new ListView(this);
|
ListView list = new ListView(this);
|
||||||
// Give me all the width and all the unused height
|
// Give me all the width and all the unused height
|
||||||
listView.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT,
|
list.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT, 1f));
|
||||||
1f));
|
list.setAdapter(adapter);
|
||||||
listView.setAdapter(adapter);
|
layout.addView(list);
|
||||||
layout.addView(listView);
|
|
||||||
|
|
||||||
Button addContactButton = new Button(this);
|
Button addContactButton = new Button(this);
|
||||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
addContactButton.setBackgroundResource(0);
|
||||||
addContactButton.setLayoutParams(lp);
|
addContactButton.setLayoutParams(new LayoutParams(MATCH_PARENT,
|
||||||
|
WRAP_CONTENT));
|
||||||
|
addContactButton.setCompoundDrawablesWithIntrinsicBounds(0,
|
||||||
|
R.drawable.social_add_person, 0, 0);
|
||||||
addContactButton.setText(R.string.add_contact_button);
|
addContactButton.setText(R.string.add_contact_button);
|
||||||
addContactButton.setCompoundDrawablesWithIntrinsicBounds(
|
|
||||||
R.drawable.social_add_person, 0, 0, 0);
|
|
||||||
addContactButton.setOnClickListener(this);
|
addContactButton.setOnClickListener(this);
|
||||||
layout.addView(addContactButton);
|
layout.addView(addContactButton);
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
|
|||||||
IBinder binder = serviceConnection.waitForBinder();
|
IBinder binder = serviceConnection.waitForBinder();
|
||||||
((BriarBinder) binder).getService().waitForStartup();
|
((BriarBinder) binder).getService().waitForStartup();
|
||||||
// Load the contacts from the database
|
// Load the contacts from the database
|
||||||
final Collection<Contact> contacts = db.getContacts();
|
Collection<Contact> contacts = db.getContacts();
|
||||||
if(LOG.isLoggable(INFO))
|
if(LOG.isLoggable(INFO))
|
||||||
LOG.info("Loaded " + contacts.size() + " contacts");
|
LOG.info("Loaded " + contacts.size() + " contacts");
|
||||||
// Update the contact list
|
// Update the contact list
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
class ContactListAdapter extends ArrayAdapter<ContactListItem> {
|
class ContactListAdapter extends ArrayAdapter<ContactListItem> {
|
||||||
|
|
||||||
ContactListAdapter(Context context) {
|
ContactListAdapter(Context ctx) {
|
||||||
super(context, android.R.layout.simple_expandable_list_item_1,
|
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||||
new ArrayList<ContactListItem>());
|
new ArrayList<ContactListItem>());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,14 +34,14 @@ class ContactListAdapter extends ArrayAdapter<ContactListItem> {
|
|||||||
layout.setGravity(CENTER);
|
layout.setGravity(CENTER);
|
||||||
|
|
||||||
ImageView bulb = new ImageView(ctx);
|
ImageView bulb = new ImageView(ctx);
|
||||||
if(item.getConnected()) bulb.setImageResource(R.drawable.green_bulb);
|
|
||||||
else bulb.setImageResource(R.drawable.grey_bulb);
|
|
||||||
bulb.setPadding(5, 5, 5, 5);
|
bulb.setPadding(5, 5, 5, 5);
|
||||||
|
if(item.isConnected()) bulb.setImageResource(R.drawable.green_bulb);
|
||||||
|
else bulb.setImageResource(R.drawable.grey_bulb);
|
||||||
layout.addView(bulb);
|
layout.addView(bulb);
|
||||||
|
|
||||||
TextView name = new TextView(ctx);
|
TextView name = new TextView(ctx);
|
||||||
// Give me all the unused width
|
// Give me all the unused width
|
||||||
name.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1f));
|
name.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1));
|
||||||
name.setTextSize(18);
|
name.setTextSize(18);
|
||||||
name.setText(item.getName());
|
name.setText(item.getName());
|
||||||
layout.addView(name);
|
layout.addView(name);
|
||||||
@@ -49,7 +49,7 @@ class ContactListAdapter extends ArrayAdapter<ContactListItem> {
|
|||||||
TextView connected = new TextView(ctx);
|
TextView connected = new TextView(ctx);
|
||||||
connected.setTextSize(12);
|
connected.setTextSize(12);
|
||||||
connected.setPadding(5, 0, 5, 0);
|
connected.setPadding(5, 0, 5, 0);
|
||||||
if(item.getConnected()) {
|
if(item.isConnected()) {
|
||||||
connected.setText(R.string.contact_connected);
|
connected.setText(R.string.contact_connected);
|
||||||
} else {
|
} else {
|
||||||
String format = ctx.getResources().getString(
|
String format = ctx.getResources().getString(
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class ContactListItem {
|
|||||||
return contact.getLastConnected();
|
return contact.getLastConnected();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean getConnected() {
|
boolean isConnected() {
|
||||||
return connected;
|
return connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,6 @@ class ContactListItem {
|
|||||||
|
|
||||||
private static class ItemComparator implements Comparator<ContactListItem> {
|
private static class ItemComparator implements Comparator<ContactListItem> {
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(ContactListItem a, ContactListItem b) {
|
public int compare(ContactListItem a, ContactListItem b) {
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(a.contact.getName(),
|
return String.CASE_INSENSITIVE_ORDER.compare(a.contact.getName(),
|
||||||
b.contact.getName());
|
b.contact.getName());
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
package net.sf.briar.android.messages;
|
||||||
|
|
||||||
|
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||||
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
import static android.widget.LinearLayout.VERTICAL;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import net.sf.briar.R;
|
||||||
|
import net.sf.briar.android.BriarActivity;
|
||||||
|
import net.sf.briar.android.BriarService;
|
||||||
|
import net.sf.briar.android.BriarService.BriarBinder;
|
||||||
|
import net.sf.briar.android.BriarService.BriarServiceConnection;
|
||||||
|
import net.sf.briar.api.Contact;
|
||||||
|
import net.sf.briar.api.ContactId;
|
||||||
|
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.PrivateMessageHeader;
|
||||||
|
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||||
|
import net.sf.briar.api.db.event.DatabaseListener;
|
||||||
|
import net.sf.briar.api.db.event.MessageAddedEvent;
|
||||||
|
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||||
|
import net.sf.briar.api.messaging.Message;
|
||||||
|
import net.sf.briar.api.messaging.MessageFactory;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.view.View;
|
||||||
|
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 com.google.inject.Inject;
|
||||||
|
|
||||||
|
public class ConversationListActivity extends BriarActivity
|
||||||
|
implements OnClickListener, DatabaseListener {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(ConversationListActivity.class.getName());
|
||||||
|
|
||||||
|
private final BriarServiceConnection serviceConnection =
|
||||||
|
new BriarServiceConnection();
|
||||||
|
|
||||||
|
@Inject private DatabaseComponent db;
|
||||||
|
@Inject @DatabaseExecutor private Executor dbExecutor;
|
||||||
|
@Inject private MessageFactory messageFactory;
|
||||||
|
|
||||||
|
private ArrayAdapter<ConversationListItem> adapter = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle state) {
|
||||||
|
super.onCreate(null);
|
||||||
|
LinearLayout layout = new LinearLayout(this);
|
||||||
|
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||||
|
layout.setOrientation(VERTICAL);
|
||||||
|
layout.setGravity(CENTER_HORIZONTAL);
|
||||||
|
|
||||||
|
adapter = new ConversationListAdapter(this);
|
||||||
|
ListView list = new ListView(this);
|
||||||
|
// Give me all the width and all the unused height
|
||||||
|
list.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT, 1f));
|
||||||
|
list.setAdapter(adapter);
|
||||||
|
layout.addView(list);
|
||||||
|
|
||||||
|
Button composeButton = new Button(this);
|
||||||
|
composeButton.setBackgroundResource(0);
|
||||||
|
composeButton.setLayoutParams(new LayoutParams(MATCH_PARENT,
|
||||||
|
WRAP_CONTENT));
|
||||||
|
composeButton.setCompoundDrawablesWithIntrinsicBounds(0,
|
||||||
|
R.drawable.content_new_email, 0, 0);
|
||||||
|
composeButton.setText(R.string.compose_button);
|
||||||
|
composeButton.setOnClickListener(this);
|
||||||
|
layout.addView(composeButton);
|
||||||
|
|
||||||
|
setContentView(layout);
|
||||||
|
|
||||||
|
// Listen for messages being added or removed
|
||||||
|
db.addListener(this);
|
||||||
|
// Bind to the service so we can wait for the DB to be opened
|
||||||
|
bindService(new Intent(BriarService.class.getName()),
|
||||||
|
serviceConnection, 0);
|
||||||
|
// Load the message headers from the DB
|
||||||
|
reloadMessageHeaders();
|
||||||
|
|
||||||
|
// Add some fake messages to the database in a background thread
|
||||||
|
// FIXME: Remove this
|
||||||
|
dbExecutor.execute(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Wait for the service to be bound and started
|
||||||
|
IBinder binder = serviceConnection.waitForBinder();
|
||||||
|
((BriarBinder) binder).getService().waitForStartup();
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Service started");
|
||||||
|
Collection<PrivateMessageHeader> headers =
|
||||||
|
db.getPrivateMessageHeaders();
|
||||||
|
if(headers.isEmpty()) {
|
||||||
|
// Insert a fake contact
|
||||||
|
ContactId contactId = db.addContact("Carol");
|
||||||
|
// Insert some messages to the contact
|
||||||
|
Message m = messageFactory.createPrivateMessage(null,
|
||||||
|
"First message's subject",
|
||||||
|
"First message's body".getBytes("UTF-8"));
|
||||||
|
db.addLocalPrivateMessage(m, contactId);
|
||||||
|
db.setStarredFlag(m.getId(), true);
|
||||||
|
Thread.sleep(2000);
|
||||||
|
m = messageFactory.createPrivateMessage(m.getId(),
|
||||||
|
"Second message's subject",
|
||||||
|
"Second message's body".getBytes("UTF-8"));
|
||||||
|
db.addLocalPrivateMessage(m, contactId);
|
||||||
|
db.setReadFlag(m.getId(), true);
|
||||||
|
}
|
||||||
|
} catch(DbException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
} catch(GeneralSecurityException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Interrupted while waiting for service");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
db.removeListener(this);
|
||||||
|
unbindService(serviceConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick(View view) {
|
||||||
|
// FIXME: Hook this button up to an activity
|
||||||
|
}
|
||||||
|
|
||||||
|
public void eventOccurred(DatabaseEvent e) {
|
||||||
|
if(e instanceof MessageAddedEvent) reloadMessageHeaders();
|
||||||
|
else if(e instanceof MessageExpiredEvent) reloadMessageHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reloadMessageHeaders() {
|
||||||
|
dbExecutor.execute(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
// Wait for the service to be bound and started
|
||||||
|
IBinder binder = serviceConnection.waitForBinder();
|
||||||
|
((BriarBinder) binder).getService().waitForStartup();
|
||||||
|
// Load the contact list from the database
|
||||||
|
Collection<Contact> contacts = db.getContacts();
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loaded " + contacts.size() + " contacts");
|
||||||
|
// Load the message headers from the database
|
||||||
|
Collection<PrivateMessageHeader> headers =
|
||||||
|
db.getPrivateMessageHeaders();
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loaded " + headers.size() + " headers");
|
||||||
|
// Update the conversation list
|
||||||
|
updateConversationList(contacts, headers);
|
||||||
|
} 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 service");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConversationList(final Collection<Contact> contacts,
|
||||||
|
final Collection<PrivateMessageHeader> headers) {
|
||||||
|
runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
adapter.clear();
|
||||||
|
for(ConversationListItem i : sortHeaders(contacts, headers))
|
||||||
|
adapter.add(i);
|
||||||
|
adapter.sort(ConversationListItem.COMPARATOR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ConversationListItem> sortHeaders(Collection<Contact> contacts,
|
||||||
|
Collection<PrivateMessageHeader> headers) {
|
||||||
|
// Group the headers into conversations, one per contact
|
||||||
|
Map<ContactId, List<PrivateMessageHeader>> map =
|
||||||
|
new HashMap<ContactId, List<PrivateMessageHeader>>();
|
||||||
|
for(Contact c : contacts)
|
||||||
|
map.put(c.getId(), new ArrayList<PrivateMessageHeader>());
|
||||||
|
for(PrivateMessageHeader h : headers) {
|
||||||
|
ContactId id = h.getContactId();
|
||||||
|
List<PrivateMessageHeader> conversation = map.get(id);
|
||||||
|
// Ignore header if the contact was added after db.getContacts()
|
||||||
|
if(conversation != null) conversation.add(h);
|
||||||
|
}
|
||||||
|
// Create a list item for each non-empty conversation
|
||||||
|
List<ConversationListItem> list = new ArrayList<ConversationListItem>();
|
||||||
|
for(Contact c : contacts) {
|
||||||
|
List<PrivateMessageHeader> conversation = map.get(c.getId());
|
||||||
|
if(!conversation.isEmpty())
|
||||||
|
list.add(new ConversationListItem(c, conversation));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package net.sf.briar.android.messages;
|
||||||
|
|
||||||
|
import static android.graphics.Typeface.BOLD;
|
||||||
|
import static android.view.Gravity.CENTER;
|
||||||
|
import static android.view.Gravity.LEFT;
|
||||||
|
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||||
|
import static android.widget.LinearLayout.HORIZONTAL;
|
||||||
|
import static android.widget.LinearLayout.VERTICAL;
|
||||||
|
import static java.text.DateFormat.SHORT;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import net.sf.briar.R;
|
||||||
|
import android.content.Context;
|
||||||
|
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;
|
||||||
|
|
||||||
|
class ConversationListAdapter extends ArrayAdapter<ConversationListItem> {
|
||||||
|
|
||||||
|
ConversationListAdapter(Context ctx) {
|
||||||
|
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||||
|
new ArrayList<ConversationListItem>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
ConversationListItem item = getItem(position);
|
||||||
|
Context ctx = getContext();
|
||||||
|
LinearLayout layout = new LinearLayout(ctx);
|
||||||
|
layout.setOrientation(HORIZONTAL);
|
||||||
|
layout.setGravity(CENTER);
|
||||||
|
|
||||||
|
ImageView star = new ImageView(ctx);
|
||||||
|
star.setPadding(5, 5, 5, 5);
|
||||||
|
if(item.getStarred())
|
||||||
|
star.setImageResource(R.drawable.rating_important);
|
||||||
|
else star.setImageResource(R.drawable.rating_not_important);
|
||||||
|
layout.addView(star);
|
||||||
|
|
||||||
|
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||||
|
// Give me all the unused width
|
||||||
|
innerLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT,
|
||||||
|
WRAP_CONTENT, 1));
|
||||||
|
innerLayout.setOrientation(VERTICAL);
|
||||||
|
innerLayout.setGravity(LEFT);
|
||||||
|
innerLayout.setPadding(0, 5, 0, 5);
|
||||||
|
|
||||||
|
TextView name = new TextView(ctx);
|
||||||
|
name.setTextSize(18);
|
||||||
|
name.setText(item.getName() + " (" + item.getLength() + ")");
|
||||||
|
innerLayout.addView(name);
|
||||||
|
|
||||||
|
TextView subject = new TextView(ctx);
|
||||||
|
subject.setTextSize(14);
|
||||||
|
if(!item.getRead()) subject.setTypeface(null, BOLD);
|
||||||
|
subject.setText(item.getSubject());
|
||||||
|
innerLayout.addView(subject);
|
||||||
|
layout.addView(innerLayout);
|
||||||
|
|
||||||
|
TextView date = new TextView(ctx);
|
||||||
|
date.setTextSize(14);
|
||||||
|
date.setPadding(5, 0, 10, 0);
|
||||||
|
long then = item.getTimestamp(), now = System.currentTimeMillis();
|
||||||
|
date.setText(DateUtils.formatSameDayTime(then, now, SHORT, SHORT));
|
||||||
|
layout.addView(date);
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package net.sf.briar.android.messages;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.sf.briar.api.Contact;
|
||||||
|
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||||
|
|
||||||
|
class ConversationListItem {
|
||||||
|
|
||||||
|
static final Comparator<ConversationListItem> COMPARATOR =
|
||||||
|
new ItemComparator();
|
||||||
|
|
||||||
|
private static final Comparator<PrivateMessageHeader> HEADER_COMPARATOR =
|
||||||
|
new HeaderComparator();
|
||||||
|
|
||||||
|
private final Contact contact;
|
||||||
|
private final List<PrivateMessageHeader> headers;
|
||||||
|
private final boolean read, starred;
|
||||||
|
|
||||||
|
ConversationListItem(Contact contact, List<PrivateMessageHeader> headers) {
|
||||||
|
if(headers.isEmpty()) throw new IllegalArgumentException();
|
||||||
|
Collections.sort(headers, HEADER_COMPARATOR);
|
||||||
|
boolean read = false, starred = false;
|
||||||
|
for(PrivateMessageHeader h : headers) {
|
||||||
|
read &= h.getRead();
|
||||||
|
starred |= h.getStarred();
|
||||||
|
}
|
||||||
|
this.contact = contact;
|
||||||
|
this.headers = headers;
|
||||||
|
this.read = read;
|
||||||
|
this.starred = starred;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return contact.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getSubject() {
|
||||||
|
return headers.get(0).getSubject();
|
||||||
|
}
|
||||||
|
|
||||||
|
long getTimestamp() {
|
||||||
|
return headers.get(0).getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getRead() {
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getStarred() {
|
||||||
|
return starred;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLength() {
|
||||||
|
return headers.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HeaderComparator
|
||||||
|
implements Comparator<PrivateMessageHeader> {
|
||||||
|
|
||||||
|
public int compare(PrivateMessageHeader a, PrivateMessageHeader b) {
|
||||||
|
// The newest message comes first
|
||||||
|
long aTime = a.getTimestamp(), bTime = b.getTimestamp();
|
||||||
|
if(aTime > bTime) return -1;
|
||||||
|
if(aTime < bTime) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ItemComparator
|
||||||
|
implements Comparator<ConversationListItem> {
|
||||||
|
|
||||||
|
public int compare(ConversationListItem a, ConversationListItem b) {
|
||||||
|
// The item with the newest message comes first
|
||||||
|
return HEADER_COMPARATOR.compare(a.headers.get(0),
|
||||||
|
b.headers.get(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||