mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
Replaced private messages with private groups.
Private messages are now the same as group messages, but groups can be private or public. When a contact is added, a private group is created and designated as the inbox for exchanging private messages with the contact.
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
package net.sf.briar.android;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
|
||||
public class DescendingHeaderComparator implements Comparator<MessageHeader> {
|
||||
|
||||
public static final DescendingHeaderComparator INSTANCE =
|
||||
new DescendingHeaderComparator();
|
||||
|
||||
public int compare(MessageHeader a, MessageHeader 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;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import static android.widget.LinearLayout.VERTICAL;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -188,13 +187,8 @@ public class SetupActivity extends RoboActivity implements OnClickListener {
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
final byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
final byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
LocalAuthor a;
|
||||
try {
|
||||
a = authorFactory.createLocalAuthor(nickname, publicKey,
|
||||
privateKey);
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
LocalAuthor a = authorFactory.createLocalAuthor(nickname,
|
||||
publicKey, privateKey);
|
||||
showHomeScreen(referenceManager.putReference(a,
|
||||
LocalAuthor.class));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.groups.NoContactsDialog;
|
||||
import net.sf.briar.android.invitation.AddContactActivity;
|
||||
import net.sf.briar.android.util.HorizontalBorder;
|
||||
import net.sf.briar.android.util.HorizontalSpace;
|
||||
@@ -34,32 +33,29 @@ import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
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.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
|
||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
||||
import net.sf.briar.api.transport.ConnectionListener;
|
||||
import net.sf.briar.api.transport.ConnectionRegistry;
|
||||
import roboguice.activity.RoboFragmentActivity;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
|
||||
public class ContactListActivity extends RoboFragmentActivity
|
||||
implements OnClickListener, DatabaseListener, ConnectionListener,
|
||||
NoContactsDialog.Listener {
|
||||
public class ContactListActivity extends RoboActivity
|
||||
implements OnClickListener, DatabaseListener, ConnectionListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ContactListActivity.class.getName());
|
||||
@@ -68,9 +64,7 @@ NoContactsDialog.Listener {
|
||||
private ContactListAdapter adapter = null;
|
||||
private ListView list = null;
|
||||
private ListLoadingProgressBar loading = null;
|
||||
private ImageButton addContactButton = null, composeButton = null;
|
||||
private ImageButton shareButton = null;
|
||||
private NoContactsDialog noContactsDialog = null;
|
||||
private ImageButton addContactButton = null, shareButton = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject private volatile DatabaseComponent db;
|
||||
@@ -113,13 +107,6 @@ NoContactsDialog.Listener {
|
||||
footer.addView(addContactButton);
|
||||
footer.addView(new HorizontalSpace(this));
|
||||
|
||||
composeButton = new ImageButton(this);
|
||||
composeButton.setBackgroundResource(0);
|
||||
composeButton.setImageResource(R.drawable.content_new_email);
|
||||
composeButton.setOnClickListener(this);
|
||||
footer.addView(composeButton);
|
||||
footer.addView(new HorizontalSpace(this));
|
||||
|
||||
shareButton = new ImageButton(this);
|
||||
shareButton.setBackgroundResource(0);
|
||||
shareButton.setImageResource(R.drawable.social_share);
|
||||
@@ -129,12 +116,6 @@ NoContactsDialog.Listener {
|
||||
layout.addView(footer);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
Fragment f = fm.findFragmentByTag("NoContactsDialog");
|
||||
if(f == null) noContactsDialog = new NoContactsDialog();
|
||||
else noContactsDialog = (NoContactsDialog) f;
|
||||
noContactsDialog.setListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,11 +123,11 @@ NoContactsDialog.Listener {
|
||||
super.onResume();
|
||||
db.addListener(this);
|
||||
connectionRegistry.addListener(this);
|
||||
loadHeaders();
|
||||
loadContacts();
|
||||
}
|
||||
|
||||
private void loadHeaders() {
|
||||
clearHeaders();
|
||||
private void loadContacts() {
|
||||
clearContacts();
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
@@ -157,9 +138,9 @@ NoContactsDialog.Listener {
|
||||
Long lastConnected = times.get(c.getId());
|
||||
if(lastConnected == null) continue;
|
||||
try {
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
db.getPrivateMessageHeaders(c.getId());
|
||||
displayHeaders(c, lastConnected, headers);
|
||||
Collection<MessageHeader> headers =
|
||||
db.getInboxMessageHeaders(c.getId());
|
||||
displayContact(c, lastConnected, headers);
|
||||
} catch(NoSuchContactException e) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Contact removed");
|
||||
@@ -181,7 +162,7 @@ NoContactsDialog.Listener {
|
||||
});
|
||||
}
|
||||
|
||||
private void clearHeaders() {
|
||||
private void clearContacts() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(GONE);
|
||||
@@ -192,8 +173,8 @@ NoContactsDialog.Listener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayHeaders(final Contact c, final long lastConnected,
|
||||
final Collection<PrivateMessageHeader> headers) {
|
||||
private void displayContact(final Contact c, final long lastConnected,
|
||||
final Collection<MessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(VISIBLE);
|
||||
@@ -239,14 +220,6 @@ NoContactsDialog.Listener {
|
||||
public void onClick(View view) {
|
||||
if(view == addContactButton) {
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
} else if(view == composeButton) {
|
||||
if(adapter.isEmpty()) {
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
noContactsDialog.show(fm, "NoContactsDialog");
|
||||
} else {
|
||||
startActivity(new Intent(this,
|
||||
WritePrivateMessageActivity.class));
|
||||
}
|
||||
} else if(view == shareButton) {
|
||||
String apkPath = getPackageCodePath();
|
||||
Intent i = new Intent(ACTION_SEND);
|
||||
@@ -259,28 +232,28 @@ NoContactsDialog.Listener {
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof ContactAddedEvent) {
|
||||
loadHeaders();
|
||||
loadContacts();
|
||||
} else if(e instanceof ContactRemovedEvent) {
|
||||
// Reload the conversation, expecting NoSuchContactException
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Contact removed, reloading");
|
||||
reloadHeaders(((ContactRemovedEvent) e).getContactId());
|
||||
reloadContact(((ContactRemovedEvent) e).getContactId());
|
||||
} else if(e instanceof MessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadContact(((MessageAddedEvent) e).getContactId());
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
||||
loadHeaders();
|
||||
} else if(e instanceof PrivateMessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
reloadHeaders(((PrivateMessageAddedEvent) e).getContactId());
|
||||
loadContacts();
|
||||
}
|
||||
}
|
||||
|
||||
private void reloadHeaders(final ContactId c) {
|
||||
private void reloadContact(final ContactId c) {
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
db.getPrivateMessageHeaders(c);
|
||||
Collection<MessageHeader> headers =
|
||||
db.getInboxMessageHeaders(c);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Partial load took " + duration + " ms");
|
||||
@@ -301,18 +274,11 @@ NoContactsDialog.Listener {
|
||||
}
|
||||
|
||||
private void updateItem(final ContactId c,
|
||||
final Collection<PrivateMessageHeader> headers) {
|
||||
final Collection<MessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ContactListItem item = findItem(c);
|
||||
if(item == null) return;
|
||||
// Replace the item with a new item containing the new headers
|
||||
adapter.remove(item);
|
||||
item = new ContactListItem(item.getContact(),
|
||||
item.isConnected(), item.getLastConnected(), headers);
|
||||
adapter.add(item);
|
||||
adapter.sort(ItemComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
if(item != null) item.setHeaders(headers);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -337,28 +303,16 @@ NoContactsDialog.Listener {
|
||||
private void setConnected(final ContactId c, final boolean connected) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
int count = adapter.getCount();
|
||||
for(int i = 0; i < count; i++) {
|
||||
ContactListItem item = adapter.getItem(i);
|
||||
if(item.getContactId().equals(c)) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Updating connection time");
|
||||
item.setConnected(connected);
|
||||
item.setLastConnected(System.currentTimeMillis());
|
||||
list.invalidateViews();
|
||||
return;
|
||||
}
|
||||
}
|
||||
ContactListItem item = findItem(c);
|
||||
if(item == null) return;
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Updating connection time");
|
||||
item.setConnected(connected);
|
||||
item.setLastConnected(System.currentTimeMillis());
|
||||
list.invalidateViews();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void contactCreationSelected() {
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
}
|
||||
|
||||
public void contactCreationCancelled() {}
|
||||
|
||||
private static class ItemComparator implements Comparator<ContactListItem> {
|
||||
|
||||
private static final ItemComparator INSTANCE = new ItemComparator();
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package net.sf.briar.android.contact;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.android.DescendingHeaderComparator;
|
||||
import net.sf.briar.api.AuthorId;
|
||||
import net.sf.briar.api.Contact;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ContactListItem {
|
||||
@@ -17,27 +13,27 @@ class ContactListItem {
|
||||
private final Contact contact;
|
||||
private boolean connected;
|
||||
private long lastConnected;
|
||||
private final boolean empty;
|
||||
private final long timestamp;
|
||||
private final int unread;
|
||||
private boolean empty;
|
||||
private long timestamp;
|
||||
private int unread;
|
||||
|
||||
ContactListItem(Contact contact, boolean connected, long lastConnected,
|
||||
Collection<PrivateMessageHeader> headers) {
|
||||
Collection<MessageHeader> headers) {
|
||||
this.contact = contact;
|
||||
this.connected = connected;
|
||||
this.lastConnected = lastConnected;
|
||||
setHeaders(headers);
|
||||
}
|
||||
|
||||
void setHeaders(Collection<MessageHeader> headers) {
|
||||
empty = headers.isEmpty();
|
||||
if(empty) {
|
||||
timestamp = 0;
|
||||
unread = 0;
|
||||
} else {
|
||||
List<PrivateMessageHeader> list =
|
||||
new ArrayList<PrivateMessageHeader>(headers);
|
||||
Collections.sort(list, DescendingHeaderComparator.INSTANCE);
|
||||
timestamp = list.get(0).getTimestamp();
|
||||
int unread = 0;
|
||||
for(PrivateMessageHeader h : list) if(!h.isRead()) unread++;
|
||||
this.unread = unread;
|
||||
timestamp = 0;
|
||||
unread = 0;
|
||||
if(!empty) {
|
||||
for(MessageHeader h : headers) {
|
||||
if(h.getTimestamp() > timestamp) timestamp = h.getTimestamp();
|
||||
if(!h.isRead()) unread++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package net.sf.briar.android.contact;
|
||||
|
||||
import static net.sf.briar.android.contact.ContactItem.NEW;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.SpinnerAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ContactSpinnerAdapter extends BaseAdapter
|
||||
implements SpinnerAdapter {
|
||||
|
||||
private final Context ctx;
|
||||
private final List<ContactItem> list = new ArrayList<ContactItem>();
|
||||
|
||||
public ContactSpinnerAdapter(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public void add(ContactItem item) {
|
||||
list.add(item);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return list.isEmpty() ? 0 : list.size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, View convertView,
|
||||
ViewGroup parent) {
|
||||
return getView(position, convertView, parent);
|
||||
}
|
||||
|
||||
public ContactItem getItem(int position) {
|
||||
if(position == list.size()) return NEW;
|
||||
return list.get(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return android.R.layout.simple_spinner_item;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
TextView name = new TextView(ctx);
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
Resources res = ctx.getResources();
|
||||
int pad = res.getInteger(R.integer.spinner_padding);
|
||||
name.setPadding(pad, pad, pad, pad);
|
||||
ContactItem item = getItem(position);
|
||||
if(item == NEW) name.setText(R.string.new_contact_item);
|
||||
else name.setText(item.getContact().getAuthor().getName());
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return list.isEmpty();
|
||||
}
|
||||
|
||||
public void sort(Comparator<ContactItem> comparator) {
|
||||
Collections.sort(list, comparator);
|
||||
}
|
||||
}
|
||||
@@ -16,22 +16,21 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.AscendingHeaderComparator;
|
||||
import net.sf.briar.android.util.HorizontalBorder;
|
||||
import net.sf.briar.android.util.ListLoadingProgressBar;
|
||||
import net.sf.briar.api.AuthorId;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
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.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
|
||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -53,13 +52,14 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
private ConversationAdapter adapter = null;
|
||||
private ListView list = null;
|
||||
private ListLoadingProgressBar loading = null;
|
||||
private ImageButton composeButton = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject private volatile DatabaseComponent db;
|
||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||
@Inject private volatile LifecycleManager lifecycleManager;
|
||||
private volatile ContactId contactId = null;
|
||||
private volatile AuthorId localAuthorId = null;
|
||||
private volatile GroupId groupId = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -72,9 +72,6 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
contactName = i.getStringExtra("net.sf.briar.CONTACT_NAME");
|
||||
if(contactName == null) throw new IllegalStateException();
|
||||
setTitle(contactName);
|
||||
byte[] b = i.getByteArrayExtra("net.sf.briar.LOCAL_AUTHOR_ID");
|
||||
if(b == null) throw new IllegalStateException();
|
||||
localAuthorId = new AuthorId(b);
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(MATCH_MATCH);
|
||||
@@ -96,9 +93,10 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
|
||||
layout.addView(new HorizontalBorder(this));
|
||||
|
||||
ImageButton composeButton = new ImageButton(this);
|
||||
composeButton = new ImageButton(this);
|
||||
composeButton.setBackgroundResource(0);
|
||||
composeButton.setImageResource(R.drawable.content_new_email);
|
||||
composeButton.setEnabled(false); // Enabled after loading the headers
|
||||
composeButton.setOnClickListener(this);
|
||||
layout.addView(composeButton);
|
||||
|
||||
@@ -118,8 +116,9 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
db.getPrivateMessageHeaders(contactId);
|
||||
groupId = db.getInboxGroup(contactId);
|
||||
Collection<MessageHeader> headers =
|
||||
db.getInboxMessageHeaders(contactId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
@@ -143,15 +142,16 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayHeaders(
|
||||
final Collection<PrivateMessageHeader> headers) {
|
||||
private void displayHeaders(final Collection<MessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(VISIBLE);
|
||||
loading.setVisibility(GONE);
|
||||
composeButton.setEnabled(true);
|
||||
adapter.clear();
|
||||
for(PrivateMessageHeader h : headers) adapter.add(h);
|
||||
adapter.sort(AscendingHeaderComparator.INSTANCE);
|
||||
for(MessageHeader h : headers)
|
||||
adapter.add(new ConversationItem(h));
|
||||
adapter.sort(ConversationItemComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
selectFirstUnread();
|
||||
}
|
||||
@@ -161,7 +161,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
private void selectFirstUnread() {
|
||||
int firstUnread = -1, count = adapter.getCount();
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(!adapter.getItem(i).isRead()) {
|
||||
if(!adapter.getItem(i).getHeader().isRead()) {
|
||||
firstUnread = i;
|
||||
break;
|
||||
}
|
||||
@@ -200,22 +200,21 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
||||
loadHeaders();
|
||||
} else if(e instanceof PrivateMessageAddedEvent) {
|
||||
PrivateMessageAddedEvent p = (PrivateMessageAddedEvent) e;
|
||||
if(p.getContactId().equals(contactId)) {
|
||||
} else if(e instanceof MessageAddedEvent) {
|
||||
if(((MessageAddedEvent) e).getContactId().equals(contactId)) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders();
|
||||
}
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
||||
loadHeaders();
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("net.sf.briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@@ -225,14 +224,15 @@ implements DatabaseListener, OnClickListener, OnItemClickListener {
|
||||
}
|
||||
|
||||
private void displayMessage(int position) {
|
||||
PrivateMessageHeader item = adapter.getItem(position);
|
||||
MessageHeader header = adapter.getItem(position).getHeader();
|
||||
Intent i = new Intent(this, ReadPrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("net.sf.briar.CONTACT_NAME", contactName);
|
||||
i.putExtra("net.sf.briar.AUTHOR_NAME", item.getAuthor().getName());
|
||||
i.putExtra("net.sf.briar.MESSAGE_ID", item.getId().getBytes());
|
||||
i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
|
||||
i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
|
||||
i.putExtra("net.sf.briar.GROUP_ID", header.getGroupId().getBytes());
|
||||
i.putExtra("net.sf.briar.AUTHOR_NAME", header.getAuthor().getName());
|
||||
i.putExtra("net.sf.briar.MESSAGE_ID", header.getId().getBytes());
|
||||
i.putExtra("net.sf.briar.CONTENT_TYPE", header.getContentType());
|
||||
i.putExtra("net.sf.briar.TIMESTAMP", header.getTimestamp());
|
||||
startActivityForResult(i, position);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP_1;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.format.DateUtils;
|
||||
@@ -17,21 +17,21 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> {
|
||||
class ConversationAdapter extends ArrayAdapter<ConversationItem> {
|
||||
|
||||
ConversationAdapter(Context ctx) {
|
||||
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||
new ArrayList<PrivateMessageHeader>());
|
||||
new ArrayList<ConversationItem>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
PrivateMessageHeader item = getItem(position);
|
||||
MessageHeader header = getItem(position).getHeader();
|
||||
Context ctx = getContext();
|
||||
|
||||
LinearLayout layout = new LinearLayout(ctx);
|
||||
layout.setOrientation(HORIZONTAL);
|
||||
if(!item.isRead()) {
|
||||
if(!header.isRead()) {
|
||||
Resources res = ctx.getResources();
|
||||
layout.setBackgroundColor(res.getColor(R.color.unread_background));
|
||||
}
|
||||
@@ -42,13 +42,13 @@ class ConversationAdapter extends ArrayAdapter<PrivateMessageHeader> {
|
||||
name.setTextSize(18);
|
||||
name.setMaxLines(1);
|
||||
name.setPadding(10, 10, 10, 10);
|
||||
name.setText(item.getAuthor().getName());
|
||||
name.setText(header.getAuthor().getName());
|
||||
layout.addView(name);
|
||||
|
||||
TextView date = new TextView(ctx);
|
||||
date.setTextSize(14);
|
||||
date.setPadding(0, 10, 10, 10);
|
||||
long then = item.getTimestamp(), now = System.currentTimeMillis();
|
||||
long then = header.getTimestamp(), now = System.currentTimeMillis();
|
||||
date.setText(DateUtils.formatSameDayTime(then, now, SHORT, SHORT));
|
||||
layout.addView(date);
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.sf.briar.android.contact;
|
||||
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
|
||||
// This class is not thread-safe
|
||||
class ConversationItem {
|
||||
|
||||
private final MessageHeader header;
|
||||
private boolean expanded;
|
||||
private byte[] body;
|
||||
|
||||
ConversationItem(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.sf.briar.android.contact;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class ConversationItemComparator
|
||||
implements Comparator<ConversationItem> {
|
||||
|
||||
public static final ConversationItemComparator INSTANCE =
|
||||
new ConversationItemComparator();
|
||||
|
||||
public int compare(ConversationItem a, ConversationItem 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;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.NoSuchMessageException;
|
||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
@@ -60,6 +61,7 @@ implements OnClickListener {
|
||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||
@Inject private volatile LifecycleManager lifecycleManager;
|
||||
private volatile MessageId messageId = null;
|
||||
private volatile GroupId groupId = null;
|
||||
private volatile long timestamp = -1;
|
||||
|
||||
@Override
|
||||
@@ -78,6 +80,9 @@ implements OnClickListener {
|
||||
byte[] b = i.getByteArrayExtra("net.sf.briar.MESSAGE_ID");
|
||||
if(b == null) throw new IllegalStateException();
|
||||
messageId = new MessageId(b);
|
||||
b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
||||
if(b == null) throw new IllegalStateException();
|
||||
groupId = new GroupId(b);
|
||||
String contentType = i.getStringExtra("net.sf.briar.CONTENT_TYPE");
|
||||
if(contentType == null) throw new IllegalStateException();
|
||||
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||
@@ -262,6 +267,7 @@ implements OnClickListener {
|
||||
} else if(view == replyButton) {
|
||||
Intent i = new Intent(this, WritePrivateMessageActivity.class);
|
||||
i.putExtra("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("net.sf.briar.PARENT_ID", messageId.getBytes());
|
||||
i.putExtra("net.sf.briar.TIMESTAMP", timestamp);
|
||||
startActivity(i);
|
||||
|
||||
@@ -12,49 +12,46 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.invitation.AddContactActivity;
|
||||
import net.sf.briar.android.util.HorizontalSpace;
|
||||
import net.sf.briar.api.AuthorId;
|
||||
import net.sf.briar.api.Contact;
|
||||
import net.sf.briar.api.ContactId;
|
||||
import net.sf.briar.api.LocalAuthor;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.NoSuchContactException;
|
||||
import net.sf.briar.api.db.NoSuchSubscriptionException;
|
||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageFactory;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.text.InputType;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemSelectedListener;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class WritePrivateMessageActivity extends RoboActivity
|
||||
implements OnItemSelectedListener, OnClickListener {
|
||||
implements OnClickListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(WritePrivateMessageActivity.class.getName());
|
||||
|
||||
private TextView from = null;
|
||||
private ContactSpinnerAdapter adapter = null;
|
||||
private Spinner spinner = null;
|
||||
private TextView from = null, to = null;
|
||||
private ImageButton sendButton = null;
|
||||
private EditText content = null;
|
||||
|
||||
@@ -63,10 +60,13 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||
@Inject private volatile LifecycleManager lifecycleManager;
|
||||
@Inject private volatile MessageFactory messageFactory;
|
||||
private volatile LocalAuthor localAuthor = null;
|
||||
private volatile ContactId contactId = null;
|
||||
private volatile GroupId groupId = null;
|
||||
private volatile MessageId parentId = null;
|
||||
private volatile long timestamp = -1;
|
||||
private volatile Contact contact = null;
|
||||
private volatile LocalAuthor localAuthor = null;
|
||||
private volatile Group group = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -74,16 +74,15 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
|
||||
Intent i = getIntent();
|
||||
int id = i.getIntExtra("net.sf.briar.CONTACT_ID", -1);
|
||||
if(id != -1) contactId = new ContactId(id);
|
||||
byte[] b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
||||
if(id == -1) throw new IllegalStateException();
|
||||
contactId = new ContactId(id);
|
||||
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
|
||||
if(b == null) throw new IllegalStateException();
|
||||
groupId = new GroupId(b);
|
||||
b = i.getByteArrayExtra("net.sf.briar.PARENT_ID");
|
||||
if(b != null) parentId = new MessageId(b);
|
||||
timestamp = i.getLongExtra("net.sf.briar.TIMESTAMP", -1);
|
||||
|
||||
if(state != null) {
|
||||
id = state.getInt("net.sf.briar.CONTACT_ID", -1);
|
||||
if(id != -1) contactId = new ContactId(id);
|
||||
}
|
||||
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(MATCH_WRAP);
|
||||
layout.setOrientation(VERTICAL);
|
||||
@@ -104,28 +103,16 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
sendButton = new ImageButton(this);
|
||||
sendButton.setBackgroundResource(0);
|
||||
sendButton.setImageResource(R.drawable.social_send_now);
|
||||
sendButton.setEnabled(false); // Enabled after loading the local author
|
||||
sendButton.setEnabled(false); // Enabled after loading the group
|
||||
sendButton.setOnClickListener(this);
|
||||
header.addView(sendButton);
|
||||
layout.addView(header);
|
||||
|
||||
header = new LinearLayout(this);
|
||||
header.setLayoutParams(MATCH_WRAP);
|
||||
header.setOrientation(HORIZONTAL);
|
||||
header.setGravity(CENTER_VERTICAL);
|
||||
|
||||
TextView to = new TextView(this);
|
||||
to = new TextView(this);
|
||||
to.setTextSize(18);
|
||||
to.setPadding(10, 0, 0, 10);
|
||||
to.setPadding(10, 10, 10, 10);
|
||||
to.setText(R.string.to);
|
||||
header.addView(to);
|
||||
|
||||
adapter = new ContactSpinnerAdapter(this);
|
||||
spinner = new Spinner(this);
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setOnItemSelectedListener(this);
|
||||
header.addView(spinner);
|
||||
layout.addView(header);
|
||||
layout.addView(to);
|
||||
|
||||
content = new EditText(this);
|
||||
content.setId(1);
|
||||
@@ -140,20 +127,26 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
loadContacts();
|
||||
loadAuthorsAndGroup();
|
||||
}
|
||||
|
||||
private void loadContacts() {
|
||||
private void loadAuthorsAndGroup() {
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<Contact> contacts = db.getContacts();
|
||||
contact = db.getContact(contactId);
|
||||
localAuthor = db.getLocalAuthor(contact.getLocalAuthorId());
|
||||
group = db.getGroup(groupId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading contacts took " + duration + " ms");
|
||||
displayContacts(contacts);
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayAuthors();
|
||||
} catch(NoSuchContactException e) {
|
||||
finish();
|
||||
} catch(NoSuchSubscriptionException e) {
|
||||
finish();
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -165,101 +158,33 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayContacts(final Collection<Contact> contacts) {
|
||||
private void displayAuthors() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if(contacts.isEmpty()) finish();
|
||||
adapter.clear();
|
||||
for(Contact c : contacts) adapter.add(new ContactItem(c));
|
||||
adapter.sort(ContactItemComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
int count = adapter.getCount();
|
||||
for(int i = 0; i < count; i++) {
|
||||
ContactItem item = adapter.getItem(i);
|
||||
if(item == ContactItem.NEW) continue;
|
||||
if(item.getContact().getId().equals(contactId)) {
|
||||
spinner.setSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle state) {
|
||||
super.onSaveInstanceState(state);
|
||||
if(contactId != null)
|
||||
state.putInt("net.sf.briar.CONTACT_ID", contactId.getInt());
|
||||
}
|
||||
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
ContactItem item = adapter.getItem(position);
|
||||
if(item == ContactItem.NEW) {
|
||||
contactId = null;
|
||||
localAuthor = null;
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
} else {
|
||||
Contact c = item.getContact();
|
||||
contactId = c.getId();
|
||||
localAuthor = null;
|
||||
loadLocalAuthor(c.getLocalAuthorId());
|
||||
}
|
||||
sendButton.setEnabled(false);
|
||||
}
|
||||
|
||||
private void loadLocalAuthor(final AuthorId a) {
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
localAuthor = db.getLocalAuthor(a);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading author took " + duration + " ms");
|
||||
displayLocalAuthor();
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} catch(InterruptedException e) {
|
||||
LOG.info("Interrupted while waiting for database");
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayLocalAuthor() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
String format = getResources().getString(R.string.format_from);
|
||||
from.setText(String.format(format, localAuthor.getName()));
|
||||
Resources res = getResources();
|
||||
String format = res.getString(R.string.format_from);
|
||||
String name = localAuthor.getName();
|
||||
from.setText(String.format(format, name));
|
||||
format = res.getString(R.string.format_to);
|
||||
name = contact.getAuthor().getName();
|
||||
to.setText(String.format(format, name));
|
||||
sendButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
contactId = null;
|
||||
sendButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
if(localAuthor == null || contactId == null)
|
||||
if(contact == null || localAuthor == null)
|
||||
throw new IllegalStateException();
|
||||
try {
|
||||
byte[] b = content.getText().toString().getBytes("UTF-8");
|
||||
storeMessage(localAuthor, contactId, b);
|
||||
storeMessage(content.getText().toString().getBytes("UTF-8"));
|
||||
} catch(UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
private void storeMessage(final LocalAuthor localAuthor,
|
||||
final ContactId contactId, final byte[] body) {
|
||||
private void storeMessage(final byte[] body) {
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
@@ -267,10 +192,10 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
// Don't use an earlier timestamp than the parent
|
||||
long time = System.currentTimeMillis();
|
||||
time = Math.max(time, timestamp + 1);
|
||||
Message m = messageFactory.createPrivateMessage(parentId,
|
||||
"text/plain", time, body);
|
||||
Message m = messageFactory.createAnonymousMessage(parentId,
|
||||
group, "text/plain", time, body);
|
||||
long now = System.currentTimeMillis();
|
||||
db.addLocalPrivateMessage(m, contactId);
|
||||
db.addLocalMessage(m);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Storing message took " + duration + " ms");
|
||||
|
||||
@@ -77,7 +77,7 @@ SelectContactsDialog.Listener {
|
||||
setTitle(name);
|
||||
b = i.getByteArrayExtra("net.sf.briar.GROUP_SALT");
|
||||
if(b == null) throw new IllegalStateException();
|
||||
group = new Group(id, name, b);
|
||||
group = new Group(id, name, b, false);
|
||||
subscribed = i.getBooleanExtra("net.sf.briar.SUBSCRIBED", false);
|
||||
boolean all = i.getBooleanExtra("net.sf.briar.VISIBLE_TO_ALL", false);
|
||||
|
||||
@@ -207,11 +207,11 @@ SelectContactsDialog.Listener {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
if(subscribe) {
|
||||
if(!wasSubscribed) db.subscribe(group);
|
||||
if(!wasSubscribed) db.addGroup(group);
|
||||
db.setVisibleToAll(group.getId(), all);
|
||||
if(!all) db.setVisibility(group.getId(), visible);
|
||||
} else if(wasSubscribed) {
|
||||
db.unsubscribe(group);
|
||||
db.removeGroup(group);
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
|
||||
@@ -13,7 +13,6 @@ import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -174,9 +173,9 @@ SelectContactsDialog.Listener {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
Group g = groupFactory.createGroup(name);
|
||||
Group g = groupFactory.createGroup(name, false);
|
||||
long now = System.currentTimeMillis();
|
||||
db.subscribe(g);
|
||||
db.addGroup(g);
|
||||
if(all) db.setVisibleToAll(g.getId(), true);
|
||||
else db.setVisibility(g.getId(), visible);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
@@ -189,8 +188,6 @@ SelectContactsDialog.Listener {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Interrupted while waiting for database");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
|
||||
@@ -25,11 +25,11 @@ import net.sf.briar.api.Author;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchSubscriptionException;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
|
||||
import net.sf.briar.api.lifecycle.LifecycleManager;
|
||||
@@ -116,8 +116,8 @@ OnClickListener, OnItemClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<GroupMessageHeader> headers =
|
||||
db.getGroupMessageHeaders(groupId);
|
||||
Collection<MessageHeader> headers =
|
||||
db.getMessageHeaders(groupId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
@@ -141,13 +141,13 @@ OnClickListener, OnItemClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayHeaders(final Collection<GroupMessageHeader> headers) {
|
||||
private void displayHeaders(final Collection<MessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(VISIBLE);
|
||||
loading.setVisibility(GONE);
|
||||
adapter.clear();
|
||||
for(GroupMessageHeader h : headers) adapter.add(h);
|
||||
for(MessageHeader h : headers) adapter.add(h);
|
||||
adapter.sort(AscendingHeaderComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
selectFirstUnread();
|
||||
@@ -187,9 +187,8 @@ OnClickListener, OnItemClickListener {
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof GroupMessageAddedEvent) {
|
||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||
if(g.getGroup().getId().equals(groupId)) {
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
if(((MessageAddedEvent) e).getGroup().getId().equals(groupId)) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders();
|
||||
}
|
||||
@@ -221,7 +220,7 @@ OnClickListener, OnItemClickListener {
|
||||
}
|
||||
|
||||
private void displayMessage(int position) {
|
||||
GroupMessageHeader item = adapter.getItem(position);
|
||||
MessageHeader item = adapter.getItem(position);
|
||||
Intent i = new Intent(this, ReadGroupPostActivity.class);
|
||||
i.putExtra("net.sf.briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", groupName);
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.ArrayList;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.api.Author;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.format.DateUtils;
|
||||
@@ -18,16 +18,16 @@ import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
class GroupAdapter extends ArrayAdapter<GroupMessageHeader> {
|
||||
class GroupAdapter extends ArrayAdapter<MessageHeader> {
|
||||
|
||||
GroupAdapter(Context ctx) {
|
||||
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||
new ArrayList<GroupMessageHeader>());
|
||||
new ArrayList<MessageHeader>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
GroupMessageHeader item = getItem(position);
|
||||
MessageHeader item = getItem(position);
|
||||
Context ctx = getContext();
|
||||
Resources res = ctx.getResources();
|
||||
|
||||
|
||||
@@ -27,11 +27,11 @@ import net.sf.briar.android.util.ListLoadingProgressBar;
|
||||
import net.sf.briar.api.android.DatabaseUiExecutor;
|
||||
import net.sf.briar.api.db.DatabaseComponent;
|
||||
import net.sf.briar.api.db.DbException;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.db.NoSuchSubscriptionException;
|
||||
import net.sf.briar.api.db.event.DatabaseEvent;
|
||||
import net.sf.briar.api.db.event.DatabaseListener;
|
||||
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageAddedEvent;
|
||||
import net.sf.briar.api.db.event.MessageExpiredEvent;
|
||||
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
|
||||
import net.sf.briar.api.db.event.SubscriptionAddedEvent;
|
||||
@@ -140,10 +140,11 @@ OnItemClickListener {
|
||||
long now = System.currentTimeMillis();
|
||||
for(GroupStatus s : db.getAvailableGroups()) {
|
||||
Group g = s.getGroup();
|
||||
if(g.isPrivate()) continue;
|
||||
if(s.isSubscribed()) {
|
||||
try {
|
||||
Collection<GroupMessageHeader> headers =
|
||||
db.getGroupMessageHeaders(g.getId());
|
||||
Collection<MessageHeader> headers =
|
||||
db.getMessageHeaders(g.getId());
|
||||
displayHeaders(g, headers);
|
||||
} catch(NoSuchSubscriptionException e) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
@@ -181,7 +182,7 @@ OnItemClickListener {
|
||||
}
|
||||
|
||||
private void displayHeaders(final Group g,
|
||||
final Collection<GroupMessageHeader> headers) {
|
||||
final Collection<MessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(VISIBLE);
|
||||
@@ -240,9 +241,12 @@ OnItemClickListener {
|
||||
}
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof GroupMessageAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders(((GroupMessageAddedEvent) e).getGroup());
|
||||
if(e instanceof MessageAddedEvent) {
|
||||
Group g = ((MessageAddedEvent) e).getGroup();
|
||||
if(!g.isPrivate()) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders(g);
|
||||
}
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
||||
loadHeaders();
|
||||
@@ -254,9 +258,12 @@ OnItemClickListener {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadHeaders();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
// Reload the group, expecting NoSuchSubscriptionException
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadHeaders(((SubscriptionRemovedEvent) e).getGroup());
|
||||
Group g = ((SubscriptionRemovedEvent) e).getGroup();
|
||||
if(!g.isPrivate()) {
|
||||
// Reload the group, expecting NoSuchSubscriptionException
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadHeaders(g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,8 +273,8 @@ OnItemClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<GroupMessageHeader> headers =
|
||||
db.getGroupMessageHeaders(g.getId());
|
||||
Collection<MessageHeader> headers =
|
||||
db.getMessageHeaders(g.getId());
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Partial load took " + duration + " ms");
|
||||
@@ -307,8 +314,10 @@ OnItemClickListener {
|
||||
lifecycleManager.waitForDatabase();
|
||||
int available = 0;
|
||||
long now = System.currentTimeMillis();
|
||||
for(GroupStatus s : db.getAvailableGroups())
|
||||
if(!s.isSubscribed()) available++;
|
||||
for(GroupStatus s : db.getAvailableGroups()) {
|
||||
if(!s.isSubscribed() && !s.getGroup().isPrivate())
|
||||
available++;
|
||||
}
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading available took " + duration + " ms");
|
||||
@@ -354,9 +363,9 @@ OnItemClickListener {
|
||||
startActivity(new Intent(this, ManageGroupsActivity.class));
|
||||
} else {
|
||||
Intent i = new Intent(this, GroupActivity.class);
|
||||
i.putExtra("net.sf.briar.GROUP_ID",
|
||||
item.getGroup().getId().getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", item.getGroup().getName());
|
||||
Group g = item.getGroup();
|
||||
i.putExtra("net.sf.briar.GROUP_ID", g.getId().getBytes());
|
||||
i.putExtra("net.sf.briar.GROUP_NAME", g.getName());
|
||||
startActivity(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package net.sf.briar.android.groups;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.briar.android.DescendingHeaderComparator;
|
||||
import net.sf.briar.api.Author;
|
||||
import net.sf.briar.api.db.GroupMessageHeader;
|
||||
import net.sf.briar.api.db.MessageHeader;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
|
||||
class GroupListItem {
|
||||
|
||||
static final GroupListItem MANAGE = new GroupListItem(null,
|
||||
Collections.<GroupMessageHeader>emptyList());
|
||||
Collections.<MessageHeader>emptyList());
|
||||
|
||||
private final Group group;
|
||||
private final boolean empty;
|
||||
@@ -21,7 +18,7 @@ class GroupListItem {
|
||||
private final long timestamp;
|
||||
private final int unread;
|
||||
|
||||
GroupListItem(Group group, Collection<GroupMessageHeader> headers) {
|
||||
GroupListItem(Group group, Collection<MessageHeader> headers) {
|
||||
this.group = group;
|
||||
empty = headers.isEmpty();
|
||||
if(empty) {
|
||||
@@ -30,17 +27,21 @@ class GroupListItem {
|
||||
timestamp = 0;
|
||||
unread = 0;
|
||||
} else {
|
||||
List<GroupMessageHeader> list =
|
||||
new ArrayList<GroupMessageHeader>(headers);
|
||||
Collections.sort(list, DescendingHeaderComparator.INSTANCE);
|
||||
GroupMessageHeader newest = list.get(0);
|
||||
MessageHeader newest = null;
|
||||
long timestamp = 0;
|
||||
int unread = 0;
|
||||
for(MessageHeader h : headers) {
|
||||
if(h.getTimestamp() > timestamp) {
|
||||
timestamp = h.getTimestamp();
|
||||
newest = h;
|
||||
}
|
||||
if(!h.isRead()) unread++;
|
||||
}
|
||||
Author a = newest.getAuthor();
|
||||
if(a == null) authorName = null;
|
||||
else authorName = a.getName();
|
||||
contentType = newest.getContentType();
|
||||
timestamp = newest.getTimestamp();
|
||||
int unread = 0;
|
||||
for(GroupMessageHeader h : list) if(!h.isRead()) unread++;
|
||||
this.timestamp = newest.getTimestamp();
|
||||
this.unread = unread;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.android.groups.ManageGroupsItem.NONE;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -66,20 +67,23 @@ implements DatabaseListener, OnItemClickListener {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
db.addListener(this);
|
||||
loadAvailableGroups();
|
||||
loadGroups();
|
||||
}
|
||||
|
||||
private void loadAvailableGroups() {
|
||||
private void loadGroups() {
|
||||
dbUiExecutor.execute(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<GroupStatus> available = db.getAvailableGroups();
|
||||
Collection<GroupStatus> available =
|
||||
new ArrayList<GroupStatus>();
|
||||
for(GroupStatus s : db.getAvailableGroups())
|
||||
if(!s.getGroup().isPrivate()) available.add(s);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
displayAvailableGroups(available);
|
||||
displayGroups(available);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -92,14 +96,13 @@ implements DatabaseListener, OnItemClickListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void displayAvailableGroups(
|
||||
final Collection<GroupStatus> available) {
|
||||
private void displayGroups(final Collection<GroupStatus> available) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
setContentView(list);
|
||||
adapter.clear();
|
||||
for(GroupStatus g : available)
|
||||
adapter.add(new ManageGroupsItem(g));
|
||||
for(GroupStatus s : available)
|
||||
adapter.add(new ManageGroupsItem(s));
|
||||
adapter.sort(ItemComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
@@ -116,13 +119,13 @@ implements DatabaseListener, OnItemClickListener {
|
||||
if(e instanceof RemoteSubscriptionsUpdatedEvent) {
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Remote subscriptions changed, reloading");
|
||||
loadAvailableGroups();
|
||||
loadGroups();
|
||||
} else if(e instanceof SubscriptionAddedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadAvailableGroups();
|
||||
loadGroups();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadAvailableGroups();
|
||||
loadGroups();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
@@ -71,10 +72,10 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
@Inject private volatile DatabaseComponent db;
|
||||
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
|
||||
@Inject private volatile LifecycleManager lifecycleManager;
|
||||
private volatile LocalAuthor localAuthor = null;
|
||||
private volatile Group group = null;
|
||||
private volatile MessageId parentId = null;
|
||||
private volatile long timestamp = -1;
|
||||
private volatile LocalAuthor localAuthor = null;
|
||||
private volatile Group group = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
@@ -213,7 +214,9 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
Collection<Group> groups = db.getSubscriptions();
|
||||
Collection<Group> groups = new ArrayList<Group>();
|
||||
for(Group g : db.getGroups())
|
||||
if(!g.isPrivate()) groups.add(g);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading groups took " + duration + " ms");
|
||||
@@ -341,7 +344,7 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
db.addLocalGroupMessage(m);
|
||||
db.addLocalMessage(m);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Storing message took " + duration + " ms");
|
||||
|
||||
@@ -13,7 +13,6 @@ import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -123,13 +122,8 @@ implements OnEditorActionListener, OnClickListener {
|
||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||
final byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
final byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||
LocalAuthor a;
|
||||
try {
|
||||
a = authorFactory.createLocalAuthor(nickname, publicKey,
|
||||
privateKey);
|
||||
} catch(IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
LocalAuthor a = authorFactory.createLocalAuthor(nickname,
|
||||
publicKey, privateKey);
|
||||
storeLocalAuthor(a);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user