Provide earlier feedback in the UI when connecting to a new contact.

Partially addresses issue #3611924.
This commit is contained in:
akwizgran
2013-05-15 18:22:04 +01:00
parent 07ab659c6b
commit 635973c845
7 changed files with 114 additions and 29 deletions

View File

@@ -57,7 +57,7 @@ implements InvitationListener {
private BluetoothWifiStateReceiver receiver = null; private BluetoothWifiStateReceiver receiver = null;
private int localInvitationCode = -1, remoteInvitationCode = -1; private int localInvitationCode = -1, remoteInvitationCode = -1;
private int localConfirmationCode = -1, remoteConfirmationCode = -1; private int localConfirmationCode = -1, remoteConfirmationCode = -1;
private boolean connectionFailed = false; private boolean connected = false, connectionFailed = false;
private boolean localCompared = false, remoteCompared = false; private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false; private boolean localMatched = false, remoteMatched = false;
private String contactName = null; private String contactName = null;
@@ -109,6 +109,7 @@ implements InvitationListener {
remoteInvitationCode = s.getRemoteInvitationCode(); remoteInvitationCode = s.getRemoteInvitationCode();
localConfirmationCode = s.getLocalConfirmationCode(); localConfirmationCode = s.getLocalConfirmationCode();
remoteConfirmationCode = s.getRemoteConfirmationCode(); remoteConfirmationCode = s.getRemoteConfirmationCode();
connected = s.getConnected();
connectionFailed = s.getConnectionFailed(); connectionFailed = s.getConnectionFailed();
localCompared = s.getLocalCompared(); localCompared = s.getLocalCompared();
remoteCompared = s.getRemoteCompared(); remoteCompared = s.getRemoteCompared();
@@ -120,10 +121,12 @@ implements InvitationListener {
setView(new NetworkSetupView(this)); setView(new NetworkSetupView(this));
} else if(remoteInvitationCode == -1) { } else if(remoteInvitationCode == -1) {
setView(new InvitationCodeView(this)); setView(new InvitationCodeView(this));
} else if(localConfirmationCode == -1) {
setView(new ConnectionView(this));
} else if(connectionFailed) { } else if(connectionFailed) {
setView(new ConnectionFailedView(this)); setView(new ConnectionFailedView(this));
} else if(connected && localConfirmationCode == -1) {
setView(new ConnectedView(this));
} else if(localConfirmationCode == -1) {
setView(new ConnectionView(this));
} else if(!localCompared) { } else if(!localCompared) {
setView(new ConfirmationCodeView(this)); setView(new ConfirmationCodeView(this));
} else if(!remoteCompared) { } else if(!remoteCompared) {
@@ -296,7 +299,26 @@ implements InvitationListener {
return contactName; return contactName;
} }
public void connectionSucceeded(final int localCode, final int remoteCode) { public void connectionSucceeded() {
runOnUiThread(new Runnable() {
public void run() {
connected = true;
setView(new ConnectedView(AddContactActivity.this));
}
});
}
public void connectionFailed() {
runOnUiThread(new Runnable() {
public void run() {
connectionFailed = true;
setView(new ConnectionFailedView(AddContactActivity.this));
}
});
}
public void keyAgreementSucceeded(final int localCode,
final int remoteCode) {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
localConfirmationCode = localCode; localConfirmationCode = localCode;
@@ -306,7 +328,7 @@ implements InvitationListener {
}); });
} }
public void connectionFailed() { public void keyAgreementFailed() {
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
public void run() { public void run() {
connectionFailed = true; connectionFailed = true;
@@ -390,14 +412,22 @@ implements InvitationListener {
this.handle = handle; this.handle = handle;
} }
public void connectionSucceeded(int localCode, int remoteCode) { public void connectionSucceeded() {
// Wait for remote confirmation to succeed or fail // Wait for key agreement to succeed or fail
} }
public void connectionFailed() { public void connectionFailed() {
referenceManager.removeReference(handle, InvitationTask.class); referenceManager.removeReference(handle, InvitationTask.class);
} }
public void keyAgreementSucceeded(int localCode, int remoteCode) {
// Wait for remote confirmation to succeed or fail
}
public void keyAgreementFailed() {
referenceManager.removeReference(handle, InvitationTask.class);
}
public void remoteConfirmationSucceeded() { public void remoteConfirmationSucceeded() {
// Wait for the pseudonym exchange to succeed or fail // Wait for the pseudonym exchange to succeed or fail
} }

View File

@@ -0,0 +1,34 @@
package net.sf.briar.android.invitation;
import static android.view.Gravity.CENTER;
import net.sf.briar.R;
import android.content.Context;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ConnectedView extends AddContactView {
ConnectedView(Context ctx) {
super(ctx);
}
void populate() {
removeAllViews();
Context ctx = getContext();
LinearLayout innerLayout = new LinearLayout(ctx);
innerLayout.setOrientation(HORIZONTAL);
innerLayout.setGravity(CENTER);
ImageView icon = new ImageView(ctx);
icon.setImageResource(R.drawable.navigation_accept);
innerLayout.addView(icon);
TextView connected = new TextView(ctx);
connected.setTextSize(22);
connected.setPadding(10, 10, 10, 10);
connected.setText(R.string.connected_to_contact);
innerLayout.addView(connected);
addView(innerLayout);
}
}

View File

@@ -6,38 +6,42 @@ package net.sf.briar.api.invitation;
*/ */
public interface InvitationListener { public interface InvitationListener {
/** Called if a connection is established and key agreement succeeds. */ /** Called if a connection to the remote peer is established. */
void connectionSucceeded(int localCode, int remoteCode); void connectionSucceeded();
/** /**
* Called if a connection cannot be established. This indicates that the * Called if a connection to the remote peer cannot be established. This
* protocol has ended unsuccessfully. * indicates that the protocol has ended unsuccessfully.
*/ */
void connectionFailed(); void connectionFailed();
/** Called if key agreement with the remote peer succeeds. */
void keyAgreementSucceeded(int localCode, int remoteCode);
/** /**
* Informs the local peer that the remote peer's confirmation check * Called if key agreement with the remote peer fails or the connection is
* succeeded. * lost. This indicates that the protocol has ended unsuccessfully.
*/ */
void keyAgreementFailed();
/** Called if the remote peer's confirmation check succeeds. */
void remoteConfirmationSucceeded(); void remoteConfirmationSucceeded();
/** /**
* Informs the local peer that the remote peer's confirmation check did * Called if remote peer's confirmation check fails or the connection is
* not succeed, or the connection was lost during confirmation. This * lost. This indicates that the protocol has ended unsuccessfully.
* indicates that the protocol has ended unsuccessfully.
*/ */
void remoteConfirmationFailed(); void remoteConfirmationFailed();
/** /**
* Informs the local peer of the name used by the remote peer. Called if * Called if the exchange of pseudonyms succeeds. This indicates that the
* the exchange of pseudonyms succeeds. This indicates that the protocol * protocol has ended successfully.
* has ended successfully.
*/ */
void pseudonymExchangeSucceeded(String remoteName); void pseudonymExchangeSucceeded(String remoteName);
/** /**
* Called if the exchange of pseudonyms fails. This indicates that the * Called if the exchange of pseudonyms fails or the connection is lost.
* protocol has ended unsuccessfully. * This indicates that the protocol has ended unsuccessfully.
*/ */
void pseudonymExchangeFailed(); void pseudonymExchangeFailed();
} }

View File

@@ -4,20 +4,21 @@ public class InvitationState {
private final int localInvitationCode, remoteInvitationCode; private final int localInvitationCode, remoteInvitationCode;
private final int localConfirmationCode, remoteConfirmationCode; private final int localConfirmationCode, remoteConfirmationCode;
private final boolean connectionFailed; private final boolean connected, connectionFailed;
private final boolean localCompared, remoteCompared; private final boolean localCompared, remoteCompared;
private final boolean localMatched, remoteMatched; private final boolean localMatched, remoteMatched;
private final String contactName; private final String contactName;
public InvitationState(int localInvitationCode, int remoteInvitationCode, public InvitationState(int localInvitationCode, int remoteInvitationCode,
int localConfirmationCode, int remoteConfirmationCode, int localConfirmationCode, int remoteConfirmationCode,
boolean connectionFailed, boolean localCompared, boolean connected, boolean connectionFailed, boolean localCompared,
boolean remoteCompared, boolean localMatched, boolean remoteCompared, boolean localMatched,
boolean remoteMatched, String contactName) { boolean remoteMatched, String contactName) {
this.localInvitationCode = localInvitationCode; this.localInvitationCode = localInvitationCode;
this.remoteInvitationCode = remoteInvitationCode; this.remoteInvitationCode = remoteInvitationCode;
this.localConfirmationCode = localConfirmationCode; this.localConfirmationCode = localConfirmationCode;
this.remoteConfirmationCode = remoteConfirmationCode; this.remoteConfirmationCode = remoteConfirmationCode;
this.connected = connected;
this.connectionFailed = connectionFailed; this.connectionFailed = connectionFailed;
this.localCompared = localCompared; this.localCompared = localCompared;
this.remoteCompared = remoteCompared; this.remoteCompared = remoteCompared;
@@ -42,6 +43,10 @@ public class InvitationState {
return remoteConfirmationCode; return remoteConfirmationCode;
} }
public boolean getConnected() {
return connected;
}
public boolean getConnectionFailed() { public boolean getConnectionFailed() {
return connectionFailed; return connectionFailed;
} }

View File

@@ -91,10 +91,12 @@ class AliceConnector extends Connector {
secret = deriveMasterSecret(hash, key, true); secret = deriveMasterSecret(hash, key, true);
} catch(IOException e) { } catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true); tryToClose(conn, true);
return; return;
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true); tryToClose(conn, true);
return; return;
} }
@@ -102,7 +104,7 @@ class AliceConnector extends Connector {
if(LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded"); if(LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int[] codes = crypto.deriveConfirmationCodes(secret); int[] codes = crypto.deriveConfirmationCodes(secret);
int aliceCode = codes[0], bobCode = codes[1]; int aliceCode = codes[0], bobCode = codes[1];
group.connectionSucceeded(aliceCode, bobCode); group.keyAgreementSucceeded(aliceCode, bobCode);
// Exchange confirmation results // Exchange confirmation results
try { try {
sendConfirmation(w); sendConfirmation(w);

View File

@@ -91,10 +91,12 @@ class BobConnector extends Connector {
secret = deriveMasterSecret(hash, key, false); secret = deriveMasterSecret(hash, key, false);
} catch(IOException e) { } catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true); tryToClose(conn, true);
return; return;
} catch(GeneralSecurityException e) { } catch(GeneralSecurityException e) {
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
group.keyAgreementFailed();
tryToClose(conn, true); tryToClose(conn, true);
return; return;
} }
@@ -102,7 +104,7 @@ class BobConnector extends Connector {
if(LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded"); if(LOG.isLoggable(INFO)) LOG.info(pluginName + " agreement succeeded");
int[] codes = crypto.deriveConfirmationCodes(secret); int[] codes = crypto.deriveConfirmationCodes(secret);
int aliceCode = codes[0], bobCode = codes[1]; int aliceCode = codes[0], bobCode = codes[1];
group.connectionSucceeded(bobCode, aliceCode); group.keyAgreementSucceeded(bobCode, aliceCode);
// Exchange confirmation results // Exchange confirmation results
try { try {
if(receiveConfirmation(r)) group.remoteConfirmationSucceeded(); if(receiveConfirmation(r)) group.remoteConfirmationSucceeded();

View File

@@ -101,7 +101,7 @@ class ConnectorGroup extends Thread implements InvitationTask {
public synchronized InvitationState addListener(InvitationListener l) { public synchronized InvitationState addListener(InvitationListener l) {
listeners.add(l); listeners.add(l);
return new InvitationState(localInvitationCode, remoteInvitationCode, return new InvitationState(localInvitationCode, remoteInvitationCode,
localConfirmationCode, remoteConfirmationCode, localConfirmationCode, remoteConfirmationCode, connected.get(),
connectionFailed, localCompared, remoteCompared, localMatched, connectionFailed, localCompared, remoteCompared, localMatched,
remoteMatched, remoteName); remoteMatched, remoteName);
} }
@@ -202,16 +202,24 @@ class ConnectorGroup extends Thread implements InvitationTask {
} }
boolean getAndSetConnected() { boolean getAndSetConnected() {
return connected.getAndSet(true); boolean redundant = connected.getAndSet(true);
if(!redundant) {
for(InvitationListener l : listeners) l.connectionSucceeded();
}
return redundant;
} }
void connectionSucceeded(int localCode, int remoteCode) { void keyAgreementSucceeded(int localCode, int remoteCode) {
synchronized(this) { synchronized(this) {
localConfirmationCode = localCode; localConfirmationCode = localCode;
remoteConfirmationCode = remoteCode; remoteConfirmationCode = remoteCode;
} }
for(InvitationListener l : listeners) for(InvitationListener l : listeners)
l.connectionSucceeded(localCode, remoteCode); l.keyAgreementSucceeded(localCode, remoteCode);
}
void keyAgreementFailed() {
for(InvitationListener l : listeners) l.keyAgreementFailed();
} }
void remoteConfirmationSucceeded() { void remoteConfirmationSucceeded() {