Massive refactoring to use pseudonyms instead of nicknames for contacts.

The invitation and private messaging UIs are currently broken. Some key
rotation bugs were fixed; others may have been created (unit tests
needed). An encoding for private keys was added. Pseudonyms were moved
out of the messaging package and ratings were moved in.
This commit is contained in:
akwizgran
2013-03-29 19:48:23 +00:00
parent 4a40de957c
commit 3309938467
131 changed files with 2094 additions and 1398 deletions

View File

@@ -19,8 +19,8 @@ import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.api.Contact;
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.DatabaseExecutor;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.event.ContactAddedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent;
@@ -52,7 +52,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
// Fields that are accessed from DB threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseExecutor private volatile Executor dbExecutor;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
@Override
public void onCreate(Bundle state) {
@@ -87,36 +87,6 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
// Bind to the service so we can wait for the DB to be opened
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
// Add some fake contacts to the database in a background thread
insertFakeContacts();
}
// FIXME: Remove this
private void insertFakeContacts() {
dbExecutor.execute(new Runnable() {
public void run() {
try {
// Wait for the service to be bound and started
serviceConnection.waitForStartup();
// If there are no contacts in the DB, create some fake ones
Collection<Contact> contacts = db.getContacts();
if(contacts.isEmpty()) {
if(LOG.isLoggable(INFO))
LOG.info("Inserting fake contacts");
db.addContact("Alice");
db.addContact("Bob");
}
} 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();
}
}
});
}
@Override
@@ -126,7 +96,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
}
private void loadContacts() {
dbExecutor.execute(new Runnable() {
dbUiExecutor.execute(new Runnable() {
public void run() {
try {
// Wait for the service to be bound and started

View File

@@ -19,7 +19,7 @@ class ContactListItem {
}
String getContactName() {
return contact.getName();
return contact.getAuthor().getName();
}
long getLastConnected() {

View File

@@ -17,6 +17,7 @@ import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
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;
@@ -28,7 +29,6 @@ import net.sf.briar.api.db.event.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
import net.sf.briar.api.messaging.Author;
import net.sf.briar.api.messaging.GroupId;
import android.content.Intent;
import android.os.Bundle;

View File

@@ -6,17 +6,17 @@ import static android.view.View.INVISIBLE;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.text.DateFormat.SHORT;
import static net.sf.briar.api.Rating.GOOD;
import static net.sf.briar.api.Rating.UNRATED;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import java.util.ArrayList;
import net.sf.briar.R;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.Rating;
import net.sf.briar.api.Author;
import net.sf.briar.api.db.GroupMessageHeader;
import net.sf.briar.api.messaging.Author;
import net.sf.briar.api.messaging.Rating;
import net.sf.briar.util.StringUtils;
import android.content.Context;
import android.content.res.Resources;

View File

@@ -6,15 +6,8 @@ import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.sf.briar.api.Rating.BAD;
import static net.sf.briar.api.Rating.GOOD;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@@ -29,11 +22,8 @@ import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.android.DatabaseUiExecutor;
import net.sf.briar.api.crypto.CryptoComponent;
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.GroupMessageHeader;
import net.sf.briar.api.db.NoSuchSubscriptionException;
@@ -42,13 +32,8 @@ import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
import net.sf.briar.api.messaging.Author;
import net.sf.briar.api.messaging.AuthorFactory;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupFactory;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageFactory;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@@ -73,13 +58,8 @@ implements OnClickListener, DatabaseListener {
private ImageButton newGroupButton = null, composeButton = null;
// Fields that are accessed from DB threads must be volatile
@Inject private volatile CryptoComponent crypto;
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseExecutor private volatile Executor dbExecutor;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
@Inject private volatile AuthorFactory authorFactory;
@Inject private volatile GroupFactory groupFactory;
@Inject private volatile MessageFactory messageFactory;
private volatile boolean restricted = false;
@Override
@@ -134,154 +114,6 @@ implements OnClickListener, DatabaseListener {
// Bind to the service so we can wait for the DB to be opened
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
// Add some fake messages to the database in a background thread
insertFakeMessages();
}
// FIXME: Remove this
private void insertFakeMessages() {
dbExecutor.execute(new Runnable() {
public void run() {
try {
// Wait for the service to be bound and started
serviceConnection.waitForStartup();
// If there are no groups in the DB, create some fake ones
Collection<Group> groups = db.getSubscriptions();
if(!groups.isEmpty()) return;
if(LOG.isLoggable(INFO))
LOG.info("Inserting fake groups and messages");
// We'll also need a contact to receive messages from
ContactId contactId = db.addContact("Dave");
// Finally, we'll need some authors for the messages
long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair();
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Key generation took " + duration + " ms");
byte[] publicKey = keyPair.getPublic().getEncoded();
PrivateKey privateKey = keyPair.getPrivate();
Author author = authorFactory.createAuthor("Batman",
publicKey);
db.setRating(author.getId(), BAD);
Author author1 = authorFactory.createAuthor("Duckman",
publicKey);
db.setRating(author1.getId(), GOOD);
// Insert some fake groups and make them visible
Group group = groupFactory.createGroup("DisneyLeaks");
db.subscribe(group);
db.setVisibility(group.getId(), Arrays.asList(contactId));
Group group1 = groupFactory.createGroup("Godwin's Lore");
db.subscribe(group1);
db.setVisibility(group1.getId(), Arrays.asList(contactId));
Group group2 = groupFactory.createGroup(
"All Kids Love Blog", publicKey);
db.subscribe(group2);
db.setVisibility(group2.getId(), Arrays.asList(contactId));
// Insert some text messages to the unrestricted groups
for(int i = 0; i < 20; i++) {
String body;
if(i % 3 == 0) {
body = "Message " + i + " is short.";
} else {
body = "Message " + i + " is long enough to wrap"
+ " onto a second line on some screens.";
}
Group g = i % 2 == 0 ? group : group1;
Message m;
now = System.currentTimeMillis();
if(i % 5 == 0) {
m = messageFactory.createAnonymousMessage(null, g,
"text/plain", body.getBytes("UTF-8"));
} else if(i % 5 == 2) {
m = messageFactory.createPseudonymousMessage(null,
g, author, privateKey, "text/plain",
body.getBytes("UTF-8"));
} else {
m = messageFactory.createPseudonymousMessage(null,
g, author1, privateKey, "text/plain",
body.getBytes("UTF-8"));
}
duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) {
LOG.info("Message creation took " +
duration + " ms");
}
now = System.currentTimeMillis();
if(Math.random() < 0.5) db.addLocalGroupMessage(m);
else db.receiveMessage(contactId, m);
db.setReadFlag(m.getId(), i % 4 == 0);
duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) {
LOG.info("Message storage took " +
duration + " ms");
}
}
// Insert a non-text message
Message m = messageFactory.createAnonymousMessage(null,
group, "image/jpeg", new byte[1000]);
db.receiveMessage(contactId, m);
// Insert a long text message
StringBuilder s = new StringBuilder();
for(int i = 0; i < 100; i++)
s.append("This is a very tedious message. ");
String body = s.toString();
m = messageFactory.createAnonymousMessage(m.getId(),
group1, "text/plain", body.getBytes("UTF-8"));
db.addLocalGroupMessage(m);
// Insert some text messages to the restricted group
for(int i = 0; i < 20; i++) {
if(i % 3 == 0) {
body = "Message " + i + " is short.";
} else {
body = "Message " + i + " is long enough to wrap"
+ " onto a second line on some screens.";
}
now = System.currentTimeMillis();
if(i % 5 == 0) {
m = messageFactory.createAnonymousMessage(null,
group2, privateKey, "text/plain",
body.getBytes("UTF-8"));
} else if(i % 5 == 2) {
m = messageFactory.createPseudonymousMessage(null,
group2, privateKey, author, privateKey,
"text/plain", body.getBytes("UTF-8"));
} else {
m = messageFactory.createPseudonymousMessage(null,
group2, privateKey, author1, privateKey,
"text/plain", body.getBytes("UTF-8"));
}
duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) {
LOG.info("Message creation took " +
duration + " ms");
}
now = System.currentTimeMillis();
if(Math.random() < 0.5) db.addLocalGroupMessage(m);
else db.receiveMessage(contactId, m);
db.setReadFlag(m.getId(), i % 4 == 0);
duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) {
LOG.info("Message storage took " +
duration + " ms");
}
}
} 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

View File

@@ -4,8 +4,8 @@ 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.messaging.Author;
import net.sf.briar.api.messaging.Group;
import net.sf.briar.api.messaging.GroupId;

View File

@@ -9,9 +9,9 @@ import static android.widget.LinearLayout.VERTICAL;
import static java.text.DateFormat.SHORT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.sf.briar.api.Rating.BAD;
import static net.sf.briar.api.Rating.GOOD;
import static net.sf.briar.api.Rating.UNRATED;
import static net.sf.briar.api.messaging.Rating.BAD;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.Executor;
@@ -24,15 +24,15 @@ import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.Rating;
import net.sf.briar.api.AuthorId;
import net.sf.briar.api.android.BundleEncrypter;
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.NoSuchMessageException;
import net.sf.briar.api.messaging.AuthorId;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;

View File

@@ -4,7 +4,6 @@ import static android.content.Context.MODE_PRIVATE;
import java.io.File;
import net.sf.briar.api.crypto.Password;
import net.sf.briar.api.db.DatabaseConfig;
import net.sf.briar.api.ui.UiCallback;
import android.app.Application;
@@ -39,13 +38,8 @@ public class HelloWorldModule extends AbstractModule {
return app.getApplicationContext().getDir("db", MODE_PRIVATE);
}
public Password getPassword() {
return new Password() {
public char[] getPassword() {
return "foo bar".toCharArray();
}
};
public char[] getPassword() {
return "foo bar".toCharArray();
}
public long getMaxSize() {

View File

@@ -1,25 +1,14 @@
package net.sf.briar.android.invitation;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import net.sf.briar.android.BriarActivity;
import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.api.AuthorId;
import net.sf.briar.api.android.BundleEncrypter;
import net.sf.briar.api.android.ReferenceManager;
import net.sf.briar.api.crypto.CryptoComponent;
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.invitation.InvitationListener;
import net.sf.briar.api.invitation.InvitationState;
import net.sf.briar.api.invitation.InvitationTask;
import net.sf.briar.api.invitation.InvitationTaskFactory;
import android.content.Intent;
import android.os.Bundle;
import com.google.inject.Inject;
@@ -27,12 +16,6 @@ import com.google.inject.Inject;
public class AddContactActivity extends BriarActivity
implements InvitationListener {
private static final Logger LOG =
Logger.getLogger(AddContactActivity.class.getName());
private final BriarServiceConnection serviceConnection =
new BriarServiceConnection();
@Inject private BundleEncrypter bundleEncrypter;
@Inject private CryptoComponent crypto;
@Inject private InvitationTaskFactory invitationTaskFactory;
@@ -40,6 +23,7 @@ implements InvitationListener {
private AddContactView view = null;
private InvitationTask task = null;
private long taskHandle = -1;
private AuthorId localAuthorId = null;
private String networkName = null;
private boolean useBluetooth = false;
private int localInvitationCode = -1, remoteInvitationCode = -1;
@@ -47,10 +31,7 @@ implements InvitationListener {
private boolean connectionFailed = false;
private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false;
// Fields that are accessed from DB threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseExecutor private volatile Executor dbExecutor;
private String contactName = null;
@Override
public void onCreate(Bundle state) {
@@ -60,6 +41,8 @@ implements InvitationListener {
setView(new NetworkSetupView(this));
} else {
// Restore the activity's state
byte[] id = state.getByteArray("net.sf.briar.LOCAL_AUTHOR_ID");
if(id != null) localAuthorId = new AuthorId(id);
networkName = state.getString("net.sf.briar.NETWORK_NAME");
useBluetooth = state.getBoolean("net.sf.briar.USE_BLUETOOTH");
taskHandle = state.getLong("net.sf.briar.TASK_HANDLE", -1);
@@ -70,7 +53,8 @@ implements InvitationListener {
localInvitationCode = state.getInt("net.sf.briar.LOCAL_CODE");
remoteInvitationCode = state.getInt("net.sf.briar.REMOTE_CODE");
connectionFailed = state.getBoolean("net.sf.briar.FAILED");
if(state.getBoolean("net.sf.briar.MATCHED")) {
contactName = state.getString("net.sf.briar.CONTACT_NAME");
if(contactName != null) {
localCompared = remoteCompared = true;
localMatched = remoteMatched = true;
}
@@ -81,10 +65,10 @@ implements InvitationListener {
setView(new InvitationCodeView(this));
} else if(connectionFailed) {
setView(new ConnectionFailedView(this));
} else if(localMatched && remoteMatched) {
setView(new ContactAddedView(this));
} else {
} else if(contactName == null) {
setView(new CodesDoNotMatchView(this));
} else {
setView(new ContactAddedView(this));
}
} else {
// A background task exists - listen to it and get its state
@@ -98,6 +82,7 @@ implements InvitationListener {
remoteCompared = s.getRemoteCompared();
localMatched = s.getLocalMatched();
remoteMatched = s.getRemoteMatched();
contactName = s.getContactName();
// Set the appropriate view for the state
if(localInvitationCode == -1) {
setView(new NetworkSetupView(this));
@@ -112,15 +97,14 @@ implements InvitationListener {
} else if(!remoteCompared) {
setView(new WaitForContactView(this));
} else if(localMatched && remoteMatched) {
setView(new ContactAddedView(this));
if(contactName == null)
setView(new WaitForContactView(this));
else setView(new ContactAddedView(this));
} else {
setView(new CodesDoNotMatchView(this));
}
}
}
// Bind to the service so we can wait for the DB to be opened
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
}
@Override
@@ -131,12 +115,16 @@ implements InvitationListener {
@Override
public void onSaveInstanceState(Bundle state) {
if(localAuthorId != null) {
state.putByteArray("net.sf.briar.LOCAL_AUTHOR_ID",
localAuthorId.getBytes());
}
state.putString("net.sf.briar.NETWORK_NAME", networkName);
state.putBoolean("net.sf.briar.USE_BLUETOOTH", useBluetooth);
state.putInt("net.sf.briar.LOCAL_CODE", localInvitationCode);
state.putInt("net.sf.briar.REMOTE_CODE", remoteInvitationCode);
state.putBoolean("net.sf.briar.FAILED", connectionFailed);
state.putBoolean("net.sf.briar.MATCHED", localMatched && remoteMatched);
state.putString("net.sf.briar.CONTACT_NAME", contactName);
if(task != null) state.putLong("net.sf.briar.TASK_HANDLE", taskHandle);
bundleEncrypter.encrypt(state);
}
@@ -145,7 +133,6 @@ implements InvitationListener {
public void onDestroy() {
super.onDestroy();
if(task != null) task.removeListener(this);
unbindService(serviceConnection);
}
void setView(AddContactView view) {
@@ -157,6 +144,7 @@ implements InvitationListener {
void reset(AddContactView view) {
task = null;
taskHandle = -1;
localAuthorId = null;
networkName = null;
useBluetooth = false;
localInvitationCode = -1;
@@ -164,9 +152,14 @@ implements InvitationListener {
connectionFailed = false;
localCompared = remoteCompared = false;
localMatched = remoteMatched = false;
contactName = null;
setView(view);
}
void setLocalAuthorId(AuthorId localAuthorId) {
this.localAuthorId = localAuthorId;
}
void setNetworkName(String networkName) {
this.networkName = networkName;
}
@@ -191,8 +184,10 @@ implements InvitationListener {
void remoteInvitationCodeEntered(int code) {
setView(new ConnectionView(this));
// FIXME: These calls are blocking the UI thread for too long
task = invitationTaskFactory.createTask(localInvitationCode, code);
if(localAuthorId == null) throw new IllegalStateException();
if(localInvitationCode == -1) throw new IllegalStateException();
task = invitationTaskFactory.createTask(localAuthorId,
localInvitationCode, code);
taskHandle = referenceManager.putReference(task, InvitationTask.class);
task.addListener(AddContactActivity.this);
task.addListener(new ReferenceCleaner(referenceManager, taskHandle));
@@ -204,11 +199,10 @@ implements InvitationListener {
}
void remoteConfirmationCodeEntered(int code) {
localCompared = true;
if(code == remoteConfirmationCode) {
localMatched = true;
if(remoteMatched) setView(new ContactAddedView(this));
else if(remoteCompared) setView(new CodesDoNotMatchView(this));
else setView(new WaitForContactView(this));
setView(new WaitForContactView(this));
task.localConfirmationSucceeded();
} else {
setView(new CodesDoNotMatchView(this));
@@ -216,23 +210,8 @@ implements InvitationListener {
}
}
void addContactAndFinish(final String nickname) {
dbExecutor.execute(new Runnable() {
public void run() {
try {
serviceConnection.waitForStartup();
db.addContact(nickname);
} 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();
}
}
});
finish();
String getContactName() {
return contactName;
}
public void connectionSucceeded(final int localCode, final int remoteCode) {
@@ -259,8 +238,6 @@ implements InvitationListener {
public void run() {
remoteCompared = true;
remoteMatched = true;
if(localMatched)
setView(new ContactAddedView(AddContactActivity.this));
}
});
}
@@ -275,6 +252,23 @@ implements InvitationListener {
});
}
public void pseudonymExchangeSucceeded(final String remoteName) {
runOnUiThread(new Runnable() {
public void run() {
contactName = remoteName;
setView(new ContactAddedView(AddContactActivity.this));
}
});
}
public void pseudonymExchangeFailed() {
runOnUiThread(new Runnable() {
public void run() {
setView(new ConnectionFailedView(AddContactActivity.this));
}
});
}
/**
* Cleans up the reference to the invitation task when the task completes.
* This class is static to prevent memory leaks.
@@ -299,11 +293,19 @@ implements InvitationListener {
}
public void remoteConfirmationSucceeded() {
referenceManager.removeReference(handle, InvitationTask.class);
// Wait for the pseudonym exchange to succeed or fail
}
public void remoteConfirmationFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
public void pseudonymExchangeSucceeded(String remoteName) {
referenceManager.removeReference(handle, InvitationTask.class);
}
public void pseudonymExchangeFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
}
}

View File

@@ -1,27 +1,18 @@
package net.sf.briar.android.invitation;
import static android.text.InputType.TYPE_CLASS_TEXT;
import static android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS;
import static android.text.InputType.TYPE_TEXT_VARIATION_PERSON_NAME;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import net.sf.briar.R;
import net.sf.briar.android.widgets.CommonLayoutParams;
import android.content.Context;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class ContactAddedView extends AddContactView implements OnClickListener,
OnEditorActionListener {
EditText nicknameEntry = null;
public class ContactAddedView extends AddContactView
implements OnClickListener {
ContactAddedView(Context ctx) {
super(ctx);
@@ -46,50 +37,21 @@ OnEditorActionListener {
innerLayout.addView(added);
addView(innerLayout);
TextView enterNickname = new TextView(ctx);
enterNickname.setGravity(CENTER_HORIZONTAL);
enterNickname.setPadding(10, 0, 10, 10);
enterNickname.setText(R.string.enter_nickname);
addView(enterNickname);
TextView contactName = new TextView(ctx);
contactName.setTextSize(22);
contactName.setPadding(10, 0, 10, 10);
contactName.setText(container.getContactName());
addView(contactName);
innerLayout = new LinearLayout(ctx);
innerLayout.setOrientation(HORIZONTAL);
innerLayout.setGravity(CENTER);
final Button doneButton = new Button(ctx);
Button doneButton = new Button(ctx);
doneButton.setLayoutParams(CommonLayoutParams.WRAP_WRAP);
doneButton.setText(R.string.done_button);
doneButton.setEnabled(false);
doneButton.setOnClickListener(this);
nicknameEntry = new EditText(ctx) {
@Override
protected void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
doneButton.setEnabled(text.length() > 0);
}
};
nicknameEntry.setTextSize(26);
nicknameEntry.setPadding(10, 0, 10, 10);
nicknameEntry.setMinEms(5);
nicknameEntry.setMaxEms(20);
nicknameEntry.setMaxLines(1);
nicknameEntry.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_FLAG_CAP_WORDS |
TYPE_TEXT_VARIATION_PERSON_NAME);
nicknameEntry.setOnEditorActionListener(this);
innerLayout.addView(nicknameEntry);
innerLayout.addView(doneButton);
addView(innerLayout);
}
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
String nickname = textView.getText().toString();
if(nickname.length() > 0) container.addContactAndFinish(nickname);
return true;
addView(doneButton);
}
public void onClick(View view) {
String nickname = nicknameEntry.getText().toString();
container.addContactAndFinish(nickname);
container.finish();
}
}

View File

@@ -20,11 +20,13 @@ implements WifiStateListener, BluetoothStateListener, OnClickListener {
void populate() {
removeAllViews();
Context ctx = getContext();
TextView sameNetwork = new TextView(ctx);
sameNetwork.setTextSize(14);
sameNetwork.setPadding(10, 10, 10, 10);
sameNetwork.setText(R.string.same_network);
addView(sameNetwork);
TextView chooseIdentity = new TextView(ctx);
chooseIdentity.setTextSize(14);
chooseIdentity.setPadding(10, 10, 10, 10);
chooseIdentity.setText(R.string.choose_identity);
addView(chooseIdentity);
// FIXME: Add a spinner for choosing which identity to use
WifiWidget wifi = new WifiWidget(ctx);
wifi.init(this);

View File

@@ -10,7 +10,7 @@ import android.widget.ArrayAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
class ContactNameSpinnerAdapter extends ArrayAdapter<Contact>
public class ContactNameSpinnerAdapter extends ArrayAdapter<Contact>
implements SpinnerAdapter {
ContactNameSpinnerAdapter(Context context) {
@@ -24,7 +24,7 @@ implements SpinnerAdapter {
name.setTextSize(18);
name.setMaxLines(1);
name.setPadding(10, 10, 10, 10);
name.setText(getItem(position).getName());
name.setText(getItem(position).getAuthor().getName());
return name;
}

View File

@@ -5,8 +5,6 @@ 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.Comparator;
@@ -25,7 +23,6 @@ import net.sf.briar.api.Contact;
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.DatabaseExecutor;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.PrivateMessageHeader;
@@ -34,8 +31,6 @@ import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageFactory;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@@ -60,9 +55,7 @@ implements OnClickListener, DatabaseListener {
// Fields that are accessed from DB threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseExecutor private volatile Executor dbExecutor;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
@Inject private volatile MessageFactory messageFactory;
@Override
public void onCreate(Bundle state) {
@@ -93,70 +86,6 @@ implements OnClickListener, DatabaseListener {
// Bind to the service so we can wait for the DB to be opened
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
// Add some fake messages to the database in a background thread
insertFakeMessages();
}
// FIXME: Remove this
private void insertFakeMessages() {
dbExecutor.execute(new Runnable() {
public void run() {
try {
// Wait for the service to be bound and started
serviceConnection.waitForStartup();
// If there are no messages in the DB, create some fake ones
Collection<PrivateMessageHeader> headers =
db.getPrivateMessageHeaders();
if(!headers.isEmpty()) return;
if(LOG.isLoggable(INFO))
LOG.info("Inserting fake private messages");
// We'll also need a contact to exchange messages with
ContactId contactId = db.addContact("Carol");
// Insert some text messages to and from the contact
for(int i = 0; i < 20; i++) {
String body;
if(i % 3 == 0) {
body = "Message " + i + " is short.";
} else {
body = "Message " + i + " is long enough to"
+ " wrap onto a second line on some"
+ " screens.";
}
Message m = messageFactory.createPrivateMessage(null,
"text/plain", body.getBytes("UTF-8"));
if(Math.random() < 0.5)
db.addLocalPrivateMessage(m, contactId);
else db.receiveMessage(contactId, m);
db.setReadFlag(m.getId(), i % 4 == 0);
}
// Insert a non-text message
Message m = messageFactory.createPrivateMessage(null,
"image/jpeg", new byte[1000]);
db.receiveMessage(contactId, m);
// Insert a long text message
StringBuilder s = new StringBuilder();
for(int i = 0; i < 100; i++)
s.append("This is a very tedious message. ");
m = messageFactory.createPrivateMessage(m.getId(),
"text/plain", s.toString().getBytes("UTF-8"));
db.addLocalPrivateMessage(m, contactId);
} 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

View File

@@ -31,7 +31,7 @@ class ConversationListItem {
}
String getContactName() {
return contact.getName();
return contact.getAuthor().getName();
}
String getSubject() {