mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Rewrote Android invitation wizard to use Views instead of Activities.
This commit is contained in:
@@ -32,35 +32,7 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.NetworkSetupActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.InvitationCodeActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.ConnectionActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.ConnectionFailedActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.ConfirmationCodeActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.WaitForContactActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.CodesDoNotMatchActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".android.invitation.ContactAddedActivity"
|
||||
android:name=".android.invitation.AddContactActivity"
|
||||
android:label="@string/add_a_contact" >
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<string-array name="roboguice_modules">
|
||||
<item>net.sf.briar.android.AndroidModule</item>
|
||||
<item>net.sf.briar.android.helloworld.HelloWorldModule</item>
|
||||
<item>net.sf.briar.android.invitation.AndroidInvitationModule</item>
|
||||
<item>net.sf.briar.clock.ClockModule</item>
|
||||
<item>net.sf.briar.crypto.CryptoModule</item>
|
||||
<item>net.sf.briar.db.DatabaseModule</item>
|
||||
<item>net.sf.briar.invitation.InvitationModule</item>
|
||||
<item>net.sf.briar.lifecycle.LifecycleModule</item>
|
||||
<item>net.sf.briar.plugins.PluginsModule</item>
|
||||
<item>net.sf.briar.protocol.ProtocolModule</item>
|
||||
|
||||
@@ -9,7 +9,7 @@ import static java.util.logging.Level.INFO;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sf.briar.R;
|
||||
import net.sf.briar.android.invitation.NetworkSetupActivity;
|
||||
import net.sf.briar.android.invitation.AddContactActivity;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -66,6 +66,6 @@ implements OnClickListener {
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
startActivity(new Intent(this, NetworkSetupActivity.class));
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
128
src/net/sf/briar/android/invitation/AddContactActivity.java
Normal file
128
src/net/sf/briar/android/invitation/AddContactActivity.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.invitation.ConfirmationCallback;
|
||||
import net.sf.briar.api.invitation.ConnectionCallback;
|
||||
import net.sf.briar.api.invitation.InvitationManager;
|
||||
import roboguice.activity.RoboActivity;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class AddContactActivity extends RoboActivity
|
||||
implements ConnectionCallback, ConfirmationCallback {
|
||||
|
||||
@Inject private CryptoComponent crypto;
|
||||
@Inject private InvitationManager invitationManager;
|
||||
|
||||
// All of the following must be accessed on the UI thread
|
||||
private AddContactView view = null;
|
||||
private String networkName = null;
|
||||
private boolean useBluetooth = false;
|
||||
private int localInvitationCode = -1;
|
||||
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
|
||||
private ConfirmationCallback callback = null;
|
||||
private boolean localMatched = false;
|
||||
private boolean remoteCompared = false, remoteMatched = false;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if(view == null) setView(new NetworkSetupView(this));
|
||||
else view.populate();
|
||||
}
|
||||
|
||||
void setView(AddContactView view) {
|
||||
this.view = view;
|
||||
view.init(this);
|
||||
setContentView(view);
|
||||
}
|
||||
|
||||
void setNetworkName(String networkName) {
|
||||
this.networkName = networkName;
|
||||
}
|
||||
|
||||
String getNetworkName() {
|
||||
return networkName;
|
||||
}
|
||||
|
||||
void setUseBluetooth(boolean useBluetooth) {
|
||||
this.useBluetooth = useBluetooth;
|
||||
}
|
||||
|
||||
boolean getUseBluetooth() {
|
||||
return useBluetooth;
|
||||
}
|
||||
|
||||
int generateLocalInvitationCode() {
|
||||
localInvitationCode = crypto.generateInvitationCode();
|
||||
return localInvitationCode;
|
||||
}
|
||||
|
||||
int getLocalInvitationCode() {
|
||||
return localInvitationCode;
|
||||
}
|
||||
|
||||
void remoteInvitationCodeEntered(int code) {
|
||||
setView(new ConnectionView(this));
|
||||
localMatched = remoteCompared = remoteMatched = false;
|
||||
invitationManager.connect(localInvitationCode, code, this);
|
||||
}
|
||||
|
||||
int getLocalConfirmationCode() {
|
||||
return localConfirmationCode;
|
||||
}
|
||||
|
||||
void remoteConfirmationCodeEntered(int code) {
|
||||
if(code == remoteConfirmationCode) {
|
||||
localMatched = true;
|
||||
if(remoteMatched) setView(new ContactAddedView(this));
|
||||
else if(remoteCompared) setView(new CodesDoNotMatchView(this));
|
||||
else setView(new WaitForContactView(this));
|
||||
callback.codesMatch();
|
||||
} else {
|
||||
setView(new CodesDoNotMatchView(this));
|
||||
callback.codesDoNotMatch();
|
||||
}
|
||||
}
|
||||
|
||||
public void connectionEstablished(final int localCode, final int remoteCode,
|
||||
final ConfirmationCallback c) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
localConfirmationCode = localCode;
|
||||
remoteConfirmationCode = remoteCode;
|
||||
callback = c;
|
||||
setView(new ConfirmationCodeView(AddContactActivity.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void connectionNotEstablished() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
setView(new ConnectionFailedView(AddContactActivity.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void codesMatch() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
remoteCompared = true;
|
||||
remoteMatched = true;
|
||||
if(localMatched)
|
||||
setView(new ContactAddedView(AddContactActivity.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void codesDoNotMatch() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
remoteCompared = true;
|
||||
if(localMatched)
|
||||
setView(new CodesDoNotMatchView(AddContactActivity.this));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
25
src/net/sf/briar/android/invitation/AddContactView.java
Normal file
25
src/net/sf/briar/android/invitation/AddContactView.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import android.content.Context;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
abstract class AddContactView extends LinearLayout {
|
||||
|
||||
protected AddContactActivity container = null;
|
||||
|
||||
AddContactView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
void init(AddContactActivity container) {
|
||||
this.container = container;
|
||||
setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
setOrientation(VERTICAL);
|
||||
setGravity(CENTER_HORIZONTAL);
|
||||
populate();
|
||||
}
|
||||
|
||||
abstract void populate();
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class AndroidInvitationModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(InvitationManager.class).to(InvitationManagerImpl.class).in(
|
||||
Singleton.class);
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,5 @@ package net.sf.briar.android.invitation;
|
||||
|
||||
interface CodeEntryListener {
|
||||
|
||||
void codeEntered(String code);
|
||||
void codeEntered(int remoteCode);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.sf.briar.android.invitation;
|
||||
import static android.text.InputType.TYPE_CLASS_NUMBER;
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static net.sf.briar.api.plugins.InvitationConstants.MAX_CODE;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.view.KeyEvent;
|
||||
@@ -53,6 +54,7 @@ OnEditorActionListener, OnClickListener {
|
||||
codeEntry.setMaxEms(5);
|
||||
codeEntry.setMaxLines(1);
|
||||
codeEntry.setInputType(TYPE_CLASS_NUMBER);
|
||||
codeEntry.requestFocus();
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
@@ -63,16 +65,24 @@ OnEditorActionListener, OnClickListener {
|
||||
}
|
||||
|
||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
||||
validateAndReturnCode();
|
||||
if(!validateAndReturnCode()) codeEntry.setText("");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
validateAndReturnCode();
|
||||
if(!validateAndReturnCode()) codeEntry.setText("");
|
||||
}
|
||||
|
||||
private void validateAndReturnCode() {
|
||||
CharSequence code = codeEntry.getText();
|
||||
if(code.length() == 6) listener.codeEntered(code.toString());
|
||||
private boolean validateAndReturnCode() {
|
||||
String remoteCodeString = codeEntry.getText().toString();
|
||||
int remoteCode;
|
||||
try {
|
||||
remoteCode = Integer.valueOf(remoteCodeString);
|
||||
} catch(NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
if(remoteCode < 0 || remoteCode > MAX_CODE) return false;
|
||||
listener.codeEntered(remoteCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CodesDoNotMatchActivity extends Activity
|
||||
implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ImageView icon = new ImageView(this);
|
||||
icon.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.alerts_and_states_error);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(this);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.codes_do_not_match);
|
||||
innerLayout.addView(failed);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
TextView interfering = new TextView(this);
|
||||
interfering.setGravity(CENTER_HORIZONTAL);
|
||||
interfering.setPadding(0, 0, 0, 10);
|
||||
interfering.setText(R.string.interfering);
|
||||
layout.addView(interfering);
|
||||
|
||||
Button tryAgain = new Button(this);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
tryAgain.setLayoutParams(lp);
|
||||
tryAgain.setText(R.string.try_again_button);
|
||||
tryAgain.setOnClickListener(this);
|
||||
layout.addView(tryAgain);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(this, InvitationCodeActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
58
src/net/sf/briar/android/invitation/CodesDoNotMatchView.java
Normal file
58
src/net/sf/briar/android/invitation/CodesDoNotMatchView.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CodesDoNotMatchView extends AddContactView
|
||||
implements OnClickListener {
|
||||
|
||||
CodesDoNotMatchView(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.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.alerts_and_states_error);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(ctx);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.codes_do_not_match);
|
||||
innerLayout.addView(failed);
|
||||
addView(innerLayout);
|
||||
|
||||
TextView interfering = new TextView(ctx);
|
||||
interfering.setGravity(CENTER_HORIZONTAL);
|
||||
interfering.setPadding(0, 0, 0, 10);
|
||||
interfering.setText(R.string.interfering);
|
||||
addView(interfering);
|
||||
|
||||
Button tryAgain = new Button(ctx);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
tryAgain.setLayoutParams(lp);
|
||||
tryAgain.setText(R.string.try_again_button);
|
||||
tryAgain.setOnClickListener(this);
|
||||
addView(tryAgain);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
// Try again
|
||||
container.setView(new NetworkSetupView(container));
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class ConfirmationCodeActivity extends RoboActivity
|
||||
implements CodeEntryListener {
|
||||
|
||||
@Inject private InvitationManager manager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ImageView icon = new ImageView(this);
|
||||
icon.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.navigation_accept);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView connected = new TextView(this);
|
||||
connected.setTextSize(20);
|
||||
connected.setText(R.string.connected_to_contact);
|
||||
innerLayout.addView(connected);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
TextView yourCode = new TextView(this);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_confirmation_code);
|
||||
layout.addView(yourCode);
|
||||
|
||||
TextView code = new TextView(this);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
code.setText(manager.getLocalConfirmationCode());
|
||||
layout.addView(code);
|
||||
|
||||
CodeEntryWidget codeEntry = new CodeEntryWidget(this);
|
||||
Resources res = getResources();
|
||||
codeEntry.init(this, res.getString(R.string.enter_confirmation_code));
|
||||
layout.addView(codeEntry);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
public void codeEntered(String code) {
|
||||
if(code.equals(manager.getRemoteConfirmationCode())) {
|
||||
Intent intent = new Intent(this, WaitForContactActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
startActivity(intent);
|
||||
} else {
|
||||
Intent intent = new Intent(this, CodesDoNotMatchActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
startActivity(intent);
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ConfirmationCodeView extends AddContactView
|
||||
implements CodeEntryListener {
|
||||
|
||||
ConfirmationCodeView(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.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.navigation_accept);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView connected = new TextView(ctx);
|
||||
connected.setTextSize(20);
|
||||
connected.setText(R.string.connected_to_contact);
|
||||
innerLayout.addView(connected);
|
||||
addView(innerLayout);
|
||||
|
||||
TextView yourCode = new TextView(ctx);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_confirmation_code);
|
||||
addView(yourCode);
|
||||
|
||||
TextView code = new TextView(ctx);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
int localCode = container.getLocalConfirmationCode();
|
||||
code.setText(String.format("%06d", localCode));
|
||||
addView(code);
|
||||
|
||||
CodeEntryWidget codeEntry = new CodeEntryWidget(ctx);
|
||||
Resources res = getResources();
|
||||
codeEntry.init(this, res.getString(R.string.enter_confirmation_code));
|
||||
addView(codeEntry);
|
||||
}
|
||||
|
||||
public void codeEntered(int remoteCode) {
|
||||
container.remoteConfirmationCodeEntered(remoteCode);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
interface ConfirmationListener {
|
||||
|
||||
void confirmationReceived();
|
||||
|
||||
void confirmationNotReceived();
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class ConnectionActivity extends RoboActivity
|
||||
implements ConnectionListener {
|
||||
|
||||
@Inject private InvitationManager manager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
Bundle b = getIntent().getExtras();
|
||||
String networkName = b.getString(
|
||||
"net.sf.briar.android.invitation.NETWORK_NAME");
|
||||
boolean useBluetooth = b.getBoolean(
|
||||
"net.sf.briar.android.invitation.USE_BLUETOOTH");
|
||||
|
||||
TextView yourCode = new TextView(this);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_invitation_code);
|
||||
layout.addView(yourCode);
|
||||
|
||||
TextView code = new TextView(this);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
code.setText(manager.getLocalInvitationCode());
|
||||
layout.addView(code);
|
||||
|
||||
if(networkName != null) {
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(this);
|
||||
progress.setIndeterminate(true);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(this);
|
||||
Resources res = getResources();
|
||||
String connectingVia = res.getString(R.string.connecting_wifi);
|
||||
connecting.setText(String.format(connectingVia, networkName));
|
||||
innerLayout.addView(connecting);
|
||||
|
||||
layout.addView(innerLayout);
|
||||
manager.startWifiConnectionWorker(this);
|
||||
}
|
||||
|
||||
if(useBluetooth) {
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(this);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
progress.setIndeterminate(true);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(this);
|
||||
connecting.setText(R.string.connecting_bluetooth);
|
||||
innerLayout.addView(connecting);
|
||||
|
||||
layout.addView(innerLayout);
|
||||
manager.startBluetoothConnectionWorker(this);
|
||||
}
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
manager.tryToConnect(this);
|
||||
}
|
||||
|
||||
public void connectionEstablished() {
|
||||
final Intent intent = new Intent(this, ConfirmationCodeActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void connectionNotEstablished() {
|
||||
final Intent intent = new Intent(this, ConnectionFailedActivity.class);
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ConnectionFailedActivity extends Activity
|
||||
implements WifiStateListener, BluetoothStateListener, OnClickListener {
|
||||
|
||||
private WifiWidget wifi = null;
|
||||
private BluetoothWidget bluetooth = null;
|
||||
private Button tryAgainButton = null;
|
||||
private String networkName = null;
|
||||
private boolean useBluetooth = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ImageView icon = new ImageView(this);
|
||||
icon.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.alerts_and_states_error);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(this);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.connection_failed);
|
||||
innerLayout.addView(failed);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
TextView checkNetwork = new TextView(this);
|
||||
checkNetwork.setGravity(CENTER_HORIZONTAL);
|
||||
checkNetwork.setText(R.string.check_same_network);
|
||||
layout.addView(checkNetwork);
|
||||
|
||||
wifi = new WifiWidget(this);
|
||||
wifi.init(this);
|
||||
layout.addView(wifi);
|
||||
|
||||
bluetooth = new BluetoothWidget(this);
|
||||
bluetooth.init(this);
|
||||
layout.addView(bluetooth);
|
||||
|
||||
tryAgainButton = new Button(this);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
tryAgainButton.setLayoutParams(lp);
|
||||
tryAgainButton.setText(R.string.try_again_button);
|
||||
tryAgainButton.setOnClickListener(this);
|
||||
enabledOrDisableTryAgainButton();
|
||||
layout.addView(tryAgainButton);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
wifi.populate();
|
||||
bluetooth.populate();
|
||||
}
|
||||
|
||||
public void wifiStateChanged(String networkName) {
|
||||
this.networkName = networkName;
|
||||
enabledOrDisableTryAgainButton();
|
||||
}
|
||||
|
||||
public void bluetoothStateChanged(boolean enabled) {
|
||||
useBluetooth = enabled;
|
||||
enabledOrDisableTryAgainButton();
|
||||
}
|
||||
|
||||
private void enabledOrDisableTryAgainButton() {
|
||||
if(tryAgainButton == null) return; // Activity not created yet
|
||||
if(useBluetooth || networkName != null) tryAgainButton.setEnabled(true);
|
||||
else tryAgainButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(this, InvitationCodeActivity.class);
|
||||
intent.putExtra("net.sf.briar.android.invitation.NETWORK_NAME",
|
||||
networkName);
|
||||
intent.putExtra("net.sf.briar.android.invitation.USE_BLUETOOTH",
|
||||
useBluetooth);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ConnectionFailedView extends AddContactView
|
||||
implements WifiStateListener, BluetoothStateListener, OnClickListener {
|
||||
|
||||
private Button tryAgainButton = null;
|
||||
|
||||
ConnectionFailedView(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.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.alerts_and_states_error);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(ctx);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.connection_failed);
|
||||
innerLayout.addView(failed);
|
||||
addView(innerLayout);
|
||||
|
||||
TextView checkNetwork = new TextView(ctx);
|
||||
checkNetwork.setGravity(CENTER_HORIZONTAL);
|
||||
checkNetwork.setText(R.string.check_same_network);
|
||||
addView(checkNetwork);
|
||||
|
||||
WifiWidget wifi = new WifiWidget(ctx);
|
||||
wifi.init(this);
|
||||
addView(wifi);
|
||||
|
||||
BluetoothWidget bluetooth = new BluetoothWidget(ctx);
|
||||
bluetooth.init(this);
|
||||
addView(bluetooth);
|
||||
|
||||
tryAgainButton = new Button(ctx);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
tryAgainButton.setLayoutParams(lp);
|
||||
tryAgainButton.setText(R.string.try_again_button);
|
||||
tryAgainButton.setOnClickListener(this);
|
||||
enabledOrDisableTryAgainButton();
|
||||
addView(tryAgainButton);
|
||||
}
|
||||
|
||||
public void wifiStateChanged(String networkName) {
|
||||
container.setNetworkName(networkName);
|
||||
enabledOrDisableTryAgainButton();
|
||||
}
|
||||
|
||||
public void bluetoothStateChanged(boolean enabled) {
|
||||
container.setUseBluetooth(enabled);
|
||||
enabledOrDisableTryAgainButton();
|
||||
}
|
||||
|
||||
private void enabledOrDisableTryAgainButton() {
|
||||
if(tryAgainButton == null) return; // Activity not created yet
|
||||
boolean useBluetooth = container.getUseBluetooth();
|
||||
String networkName = container.getNetworkName();
|
||||
if(useBluetooth || networkName != null) tryAgainButton.setEnabled(true);
|
||||
else tryAgainButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
// Try again
|
||||
container.setView(new InvitationCodeView(container));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
interface ConnectionListener {
|
||||
|
||||
void connectionEstablished();
|
||||
|
||||
void connectionNotEstablished();
|
||||
}
|
||||
71
src/net/sf/briar/android/invitation/ConnectionView.java
Normal file
71
src/net/sf/briar/android/invitation/ConnectionView.java
Normal file
@@ -0,0 +1,71 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ConnectionView extends AddContactView {
|
||||
|
||||
ConnectionView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
void populate() {
|
||||
removeAllViews();
|
||||
Context ctx = getContext();
|
||||
TextView yourCode = new TextView(ctx);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_invitation_code);
|
||||
addView(yourCode);
|
||||
|
||||
TextView code = new TextView(ctx);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
int localCode = container.getLocalInvitationCode();
|
||||
code.setText(String.format("%06d", localCode));
|
||||
addView(code);
|
||||
|
||||
String networkName = container.getNetworkName();
|
||||
if(networkName != null) {
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(ctx);
|
||||
progress.setIndeterminate(true);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(ctx);
|
||||
Resources res = getResources();
|
||||
String connectingVia = res.getString(R.string.connecting_wifi);
|
||||
connecting.setText(String.format(connectingVia, networkName));
|
||||
innerLayout.addView(connecting);
|
||||
|
||||
addView(innerLayout);
|
||||
}
|
||||
|
||||
boolean useBluetooth = container.getUseBluetooth();
|
||||
if(useBluetooth) {
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(ctx);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
progress.setIndeterminate(true);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(ctx);
|
||||
connecting.setText(R.string.connecting_bluetooth);
|
||||
innerLayout.addView(connecting);
|
||||
|
||||
addView(innerLayout);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,11 @@ package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.content.Context;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
@@ -20,43 +14,42 @@ import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
public class ContactAddedActivity extends Activity implements OnClickListener,
|
||||
public class ContactAddedView extends AddContactView implements OnClickListener,
|
||||
OnEditorActionListener {
|
||||
|
||||
private volatile Button done = null;
|
||||
private Button done = null;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
ContactAddedView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
void populate() {
|
||||
removeAllViews();
|
||||
Context ctx = getContext();
|
||||
LinearLayout innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ImageView icon = new ImageView(this);
|
||||
ImageView icon = new ImageView(ctx);
|
||||
icon.setImageResource(R.drawable.navigation_accept);
|
||||
icon.setPadding(10, 10, 10, 10);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(this);
|
||||
TextView failed = new TextView(ctx);
|
||||
failed.setText(R.string.contact_added);
|
||||
failed.setTextSize(20);
|
||||
innerLayout.addView(failed);
|
||||
layout.addView(innerLayout);
|
||||
addView(innerLayout);
|
||||
|
||||
TextView enterNickname = new TextView(this);
|
||||
TextView enterNickname = new TextView(ctx);
|
||||
enterNickname.setGravity(CENTER_HORIZONTAL);
|
||||
enterNickname.setText(R.string.enter_nickname);
|
||||
layout.addView(enterNickname);
|
||||
addView(enterNickname);
|
||||
|
||||
final Button addAnother = new Button(this);
|
||||
final Button done = new Button(this);
|
||||
final Button addAnother = new Button(ctx);
|
||||
final Button done = new Button(ctx);
|
||||
this.done = done;
|
||||
EditText nicknameEntry = new EditText(this) {
|
||||
EditText nicknameEntry = new EditText(ctx) {
|
||||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start,
|
||||
int lengthBefore, int lengthAfter) {
|
||||
@@ -68,9 +61,9 @@ OnEditorActionListener {
|
||||
nicknameEntry.setMaxEms(20);
|
||||
nicknameEntry.setMaxLines(1);
|
||||
nicknameEntry.setOnEditorActionListener(this);
|
||||
layout.addView(nicknameEntry);
|
||||
addView(nicknameEntry);
|
||||
|
||||
innerLayout = new LinearLayout(this);
|
||||
innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
@@ -83,20 +76,16 @@ OnEditorActionListener {
|
||||
done.setEnabled(false);
|
||||
done.setOnClickListener(this);
|
||||
innerLayout.addView(done);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
setContentView(layout);
|
||||
addView(innerLayout);
|
||||
}
|
||||
|
||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
||||
if(textView.getText().length() > 0) finish();
|
||||
if(textView.getText().length() > 0) container.finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
if(done == null) return;
|
||||
if(view != done)
|
||||
startActivity(new Intent(this, NetworkSetupActivity.class));
|
||||
finish();
|
||||
if(view == done) container.finish(); // Done
|
||||
else container.setView(new NetworkSetupView(container)); // Add another
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class InvitationCodeActivity extends RoboActivity
|
||||
implements CodeEntryListener {
|
||||
|
||||
@Inject private InvitationManager manager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
TextView yourCode = new TextView(this);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_invitation_code);
|
||||
layout.addView(yourCode);
|
||||
|
||||
TextView code = new TextView(this);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
code.setText(manager.getLocalInvitationCode());
|
||||
layout.addView(code);
|
||||
|
||||
CodeEntryWidget codeEntry = new CodeEntryWidget(this);
|
||||
Resources res = getResources();
|
||||
codeEntry.init(this, res.getString(R.string.enter_invitation_code));
|
||||
layout.addView(codeEntry);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
public void codeEntered(String code) {
|
||||
manager.setRemoteInvitationCode(code);
|
||||
Intent intent = new Intent(this, ConnectionActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
46
src/net/sf/briar/android/invitation/InvitationCodeView.java
Normal file
46
src/net/sf/briar/android/invitation/InvitationCodeView.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class InvitationCodeView extends AddContactView
|
||||
implements CodeEntryListener {
|
||||
|
||||
private int localCode = -1;
|
||||
|
||||
InvitationCodeView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
void init(AddContactActivity container) {
|
||||
localCode = container.generateLocalInvitationCode();
|
||||
super.init(container);
|
||||
}
|
||||
|
||||
void populate() {
|
||||
removeAllViews();
|
||||
Context ctx = getContext();
|
||||
TextView yourCode = new TextView(ctx);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_invitation_code);
|
||||
addView(yourCode);
|
||||
|
||||
TextView code = new TextView(ctx);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
code.setText(String.format("%06d", localCode));
|
||||
addView(code);
|
||||
|
||||
CodeEntryWidget codeEntry = new CodeEntryWidget(ctx);
|
||||
Resources res = getResources();
|
||||
codeEntry.init(this, res.getString(R.string.enter_invitation_code));
|
||||
addView(codeEntry);
|
||||
}
|
||||
|
||||
public void codeEntered(int remoteCode) {
|
||||
container.remoteInvitationCodeEntered(remoteCode);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
interface InvitationManager {
|
||||
|
||||
int TIMEOUT = 20 * 1000;
|
||||
|
||||
void tryToConnect(ConnectionListener listener);
|
||||
|
||||
String getLocalInvitationCode();
|
||||
|
||||
String getRemoteInvitationCode();
|
||||
|
||||
void setRemoteInvitationCode(String code);
|
||||
|
||||
void startWifiConnectionWorker(Context ctx);
|
||||
|
||||
void startBluetoothConnectionWorker(Context ctx);
|
||||
|
||||
String getLocalConfirmationCode();
|
||||
|
||||
String getRemoteConfirmationCode();
|
||||
|
||||
void startConfirmationWorker(ConfirmationListener listener);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
class InvitationManagerImpl implements InvitationManager {
|
||||
|
||||
public void tryToConnect(final ConnectionListener listener) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// FIXME
|
||||
Thread.sleep((long) (Math.random() * TIMEOUT));
|
||||
if(Math.random() < 0.5) listener.connectionEstablished();
|
||||
else listener.connectionNotEstablished();
|
||||
} catch(InterruptedException e) {
|
||||
Log.w(getClass().getName(), e.toString());
|
||||
listener.connectionNotEstablished();
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public String getLocalInvitationCode() {
|
||||
// FIXME
|
||||
return "123456";
|
||||
}
|
||||
|
||||
public String getRemoteInvitationCode() {
|
||||
// FIXME
|
||||
return "123456";
|
||||
}
|
||||
|
||||
public void setRemoteInvitationCode(String code) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
public void startWifiConnectionWorker(Context ctx) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
public void startBluetoothConnectionWorker(Context ctx) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
public String getLocalConfirmationCode() {
|
||||
// FIXME
|
||||
return "123456";
|
||||
}
|
||||
|
||||
public String getRemoteConfirmationCode() {
|
||||
// FIXME
|
||||
return "123456";
|
||||
}
|
||||
|
||||
public void startConfirmationWorker(ConfirmationListener listener) {
|
||||
// FIXME
|
||||
try {
|
||||
Thread.sleep(1000 + (int) (Math.random() * 4 * 1000));
|
||||
} catch(InterruptedException e) {
|
||||
Log.w(getClass().getName(), e.toString());
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
listener.confirmationReceived();
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class NetworkSetupActivity extends Activity
|
||||
implements WifiStateListener, BluetoothStateListener, OnClickListener {
|
||||
|
||||
private WifiWidget wifi = null;
|
||||
private BluetoothWidget bluetooth = null;
|
||||
private Button continueButton = null;
|
||||
private String networkName = null;
|
||||
private boolean useBluetooth = false;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
TextView sameNetwork = new TextView(this);
|
||||
sameNetwork.setGravity(CENTER_HORIZONTAL);
|
||||
sameNetwork.setText(R.string.same_network);
|
||||
layout.addView(sameNetwork);
|
||||
|
||||
wifi = new WifiWidget(this);
|
||||
wifi.init(this);
|
||||
layout.addView(wifi);
|
||||
|
||||
bluetooth = new BluetoothWidget(this);
|
||||
bluetooth.init(this);
|
||||
layout.addView(bluetooth);
|
||||
|
||||
continueButton = new Button(this);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
continueButton.setLayoutParams(lp);
|
||||
continueButton.setText(R.string.continue_button);
|
||||
continueButton.setOnClickListener(this);
|
||||
enableOrDisableContinueButton();
|
||||
layout.addView(continueButton);
|
||||
|
||||
setContentView(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
wifi.populate();
|
||||
bluetooth.populate();
|
||||
}
|
||||
|
||||
public void wifiStateChanged(final String name) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
networkName = name;
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bluetoothStateChanged(final boolean enabled) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
useBluetooth = enabled;
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableOrDisableContinueButton() {
|
||||
if(continueButton == null) return; // Activity not created yet
|
||||
if(useBluetooth || networkName != null) continueButton.setEnabled(true);
|
||||
else continueButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
Intent intent = new Intent(this, InvitationCodeActivity.class);
|
||||
intent.putExtra("net.sf.briar.android.invitation.NETWORK_NAME",
|
||||
networkName);
|
||||
intent.putExtra("net.sf.briar.android.invitation.USE_BLUETOOTH",
|
||||
useBluetooth);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
76
src/net/sf/briar/android/invitation/NetworkSetupView.java
Normal file
76
src/net/sf/briar/android/invitation/NetworkSetupView.java
Normal file
@@ -0,0 +1,76 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class NetworkSetupView extends AddContactView
|
||||
implements WifiStateListener, BluetoothStateListener, OnClickListener {
|
||||
|
||||
private Button continueButton = null;
|
||||
|
||||
NetworkSetupView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
void populate() {
|
||||
removeAllViews();
|
||||
Context ctx = getContext();
|
||||
TextView sameNetwork = new TextView(ctx);
|
||||
sameNetwork.setGravity(CENTER_HORIZONTAL);
|
||||
sameNetwork.setText(R.string.same_network);
|
||||
addView(sameNetwork);
|
||||
|
||||
WifiWidget wifi = new WifiWidget(ctx);
|
||||
wifi.init(this);
|
||||
addView(wifi);
|
||||
|
||||
BluetoothWidget bluetooth = new BluetoothWidget(ctx);
|
||||
bluetooth.init(this);
|
||||
addView(bluetooth);
|
||||
|
||||
continueButton = new Button(ctx);
|
||||
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
|
||||
continueButton.setLayoutParams(lp);
|
||||
continueButton.setText(R.string.continue_button);
|
||||
continueButton.setOnClickListener(this);
|
||||
enableOrDisableContinueButton();
|
||||
addView(continueButton);
|
||||
}
|
||||
|
||||
public void wifiStateChanged(final String networkName) {
|
||||
container.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
container.setNetworkName(networkName);
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bluetoothStateChanged(final boolean enabled) {
|
||||
container.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
container.setUseBluetooth(enabled);
|
||||
enableOrDisableContinueButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void enableOrDisableContinueButton() {
|
||||
if(continueButton == null) return; // Activity not created yet
|
||||
boolean useBluetooth = container.getUseBluetooth();
|
||||
String networkName = container.getNetworkName();
|
||||
if(useBluetooth || networkName != null) continueButton.setEnabled(true);
|
||||
else continueButton.setEnabled(false);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
// Continue
|
||||
container.setView(new InvitationCodeView(container));
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import net.sf.briar.R;
|
||||
import roboguice.activity.RoboActivity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
public class WaitForContactActivity extends RoboActivity
|
||||
implements ConfirmationListener {
|
||||
|
||||
@Inject private InvitationManager manager;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
LinearLayout innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ImageView icon = new ImageView(this);
|
||||
icon.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.navigation_accept);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(this);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.connected_to_contact);
|
||||
innerLayout.addView(failed);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
TextView yourCode = new TextView(this);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_confirmation_code);
|
||||
layout.addView(yourCode);
|
||||
|
||||
TextView code = new TextView(this);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
code.setText(manager.getLocalConfirmationCode());
|
||||
layout.addView(code);
|
||||
|
||||
innerLayout = new LinearLayout(this);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(this);
|
||||
progress.setIndeterminate(true);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(this);
|
||||
connecting.setText(R.string.waiting_for_contact);
|
||||
innerLayout.addView(connecting);
|
||||
layout.addView(innerLayout);
|
||||
|
||||
setContentView(layout);
|
||||
|
||||
manager.startConfirmationWorker(this);
|
||||
}
|
||||
|
||||
public void confirmationReceived() {
|
||||
startActivity(new Intent(this, ContactAddedActivity.class));
|
||||
finish();
|
||||
}
|
||||
|
||||
public void confirmationNotReceived() {
|
||||
Intent intent = new Intent(this, CodesDoNotMatchActivity.class);
|
||||
intent.putExtras(getIntent().getExtras());
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
62
src/net/sf/briar/android/invitation/WaitForContactView.java
Normal file
62
src/net/sf/briar/android/invitation/WaitForContactView.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package net.sf.briar.android.invitation;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import net.sf.briar.R;
|
||||
import android.content.Context;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class WaitForContactView extends AddContactView {
|
||||
|
||||
WaitForContactView(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.setPadding(10, 10, 10, 10);
|
||||
icon.setImageResource(R.drawable.navigation_accept);
|
||||
innerLayout.addView(icon);
|
||||
|
||||
TextView failed = new TextView(ctx);
|
||||
failed.setTextSize(20);
|
||||
failed.setText(R.string.connected_to_contact);
|
||||
innerLayout.addView(failed);
|
||||
addView(innerLayout);
|
||||
|
||||
TextView yourCode = new TextView(ctx);
|
||||
yourCode.setGravity(CENTER_HORIZONTAL);
|
||||
yourCode.setText(R.string.your_confirmation_code);
|
||||
addView(yourCode);
|
||||
|
||||
TextView code = new TextView(ctx);
|
||||
code.setGravity(CENTER_HORIZONTAL);
|
||||
code.setTextSize(50);
|
||||
int localCode = container.getLocalConfirmationCode();
|
||||
code.setText(String.format("%06d", localCode));
|
||||
addView(code);
|
||||
|
||||
innerLayout = new LinearLayout(ctx);
|
||||
innerLayout.setOrientation(HORIZONTAL);
|
||||
innerLayout.setGravity(CENTER);
|
||||
|
||||
ProgressBar progress = new ProgressBar(ctx);
|
||||
progress.setIndeterminate(true);
|
||||
progress.setPadding(0, 10, 10, 0);
|
||||
innerLayout.addView(progress);
|
||||
|
||||
TextView connecting = new TextView(ctx);
|
||||
connecting.setText(R.string.waiting_for_contact);
|
||||
innerLayout.addView(connecting);
|
||||
addView(innerLayout);
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public interface CryptoComponent {
|
||||
|
||||
/**
|
||||
* Derives a tag key from the given temporary secret.
|
||||
* @param alice Indicates whether the key is for connections initiated by
|
||||
* @param alice indicates whether the key is for connections initiated by
|
||||
* Alice or Bob.
|
||||
*/
|
||||
ErasableKey deriveTagKey(byte[] secret, boolean alice);
|
||||
@@ -19,9 +19,9 @@ public interface CryptoComponent {
|
||||
/**
|
||||
* Derives a frame key from the given temporary secret and connection
|
||||
* number.
|
||||
* @param alice Indicates whether the key is for a connection initiated by
|
||||
* @param alice indicates whether the key is for a connection initiated by
|
||||
* Alice or Bob.
|
||||
* @param initiator Indicates whether the key is for the initiator's or the
|
||||
* @param initiator indicates whether the key is for the initiator's or the
|
||||
* responder's side of the connection.
|
||||
*/
|
||||
ErasableKey deriveFrameKey(byte[] secret, long connection, boolean alice,
|
||||
@@ -30,7 +30,7 @@ public interface CryptoComponent {
|
||||
/**
|
||||
* Derives an initial shared secret from two public keys and one of the
|
||||
* corresponding private keys.
|
||||
* @param alice Indicates whether the private key belongs to Alice or Bob.
|
||||
* @param alice indicates whether the private key belongs to Alice or Bob.
|
||||
*/
|
||||
byte[] deriveInitialSecret(byte[] ourPublicKey, byte[] theirPublicKey,
|
||||
PrivateKey ourPrivateKey, boolean alice);
|
||||
@@ -67,7 +67,7 @@ public interface CryptoComponent {
|
||||
|
||||
MessageDigest getMessageDigest();
|
||||
|
||||
PseudoRandom getPseudoRandom(int seed);
|
||||
PseudoRandom getPseudoRandom(int seed1, int seed2);
|
||||
|
||||
SecureRandom getSecureRandom();
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public interface DatabaseComponent {
|
||||
|
||||
/**
|
||||
* Opens the database.
|
||||
* @param resume True to reopen an existing database or false to create a
|
||||
* @param resume true to reopen an existing database or false to create a
|
||||
* new one.
|
||||
*/
|
||||
void open(boolean resume) throws DbException, IOException;
|
||||
|
||||
14
src/net/sf/briar/api/invitation/ConfirmationCallback.java
Normal file
14
src/net/sf/briar/api/invitation/ConfirmationCallback.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package net.sf.briar.api.invitation;
|
||||
|
||||
/** An interface for informing a peer of whether confirmation codes match. */
|
||||
public interface ConfirmationCallback {
|
||||
|
||||
/** Called to indicate that the confirmation codes match. */
|
||||
void codesMatch();
|
||||
|
||||
/**
|
||||
* Called to indicate that either the confirmation codes do not match or
|
||||
* the result of the comparison is unknown.
|
||||
*/
|
||||
void codesDoNotMatch();
|
||||
}
|
||||
18
src/net/sf/briar/api/invitation/ConnectionCallback.java
Normal file
18
src/net/sf/briar/api/invitation/ConnectionCallback.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package net.sf.briar.api.invitation;
|
||||
|
||||
/** An interface for monitoring the status of an invitation connection. */
|
||||
public interface ConnectionCallback extends ConfirmationCallback {
|
||||
|
||||
/**
|
||||
* Called if the connection is successfully established.
|
||||
* @param localCode the local confirmation code.
|
||||
* @param remoteCode the remote confirmation code.
|
||||
* @param c a callback to inform the remote peer of the result of the local
|
||||
* peer's confirmation code comparison.
|
||||
*/
|
||||
void connectionEstablished(int localCode, int remoteCode,
|
||||
ConfirmationCallback c);
|
||||
|
||||
/** Called if the connection cannot be established. */
|
||||
void connectionNotEstablished();
|
||||
}
|
||||
17
src/net/sf/briar/api/invitation/InvitationManager.java
Normal file
17
src/net/sf/briar/api/invitation/InvitationManager.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package net.sf.briar.api.invitation;
|
||||
|
||||
/**
|
||||
* Allows invitation connections to be established and their status to be
|
||||
* monitored.
|
||||
*/
|
||||
public interface InvitationManager {
|
||||
|
||||
/**
|
||||
* Tries to establish an invitation connection.
|
||||
* @param localCode the local invitation code.
|
||||
* @param remoteCode the remote invitation code.
|
||||
* @param c1 a callback to be informed of the connection's status and the
|
||||
* result of the remote peer's confirmation code comparison.
|
||||
*/
|
||||
void connect(int localCode, int remoteCode, ConnectionCallback c);
|
||||
}
|
||||
@@ -6,9 +6,9 @@ public interface InvitationConstants {
|
||||
|
||||
int CODE_BITS = 19; // Codes must fit into six decimal digits
|
||||
|
||||
int MAX_CODE = 1 << CODE_BITS - 1;
|
||||
int MAX_CODE = (1 << CODE_BITS) - 1;
|
||||
|
||||
int HASH_LENGTH = 48;
|
||||
int HASH_LENGTH = 48; // Bytes
|
||||
|
||||
int MAX_PUBLIC_KEY_LENGTH = 120;
|
||||
int MAX_PUBLIC_KEY_LENGTH = 120; // Bytes
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ public interface Plugin {
|
||||
void stop() throws IOException;
|
||||
|
||||
/**
|
||||
* Returns true if the plugin's poll() method should be called
|
||||
* periodically to attempt to establish connections.
|
||||
* Returns true if the plugin's {@link Plugin#poll(Collection)} method
|
||||
* should be called periodically to attempt to establish connections.
|
||||
*/
|
||||
boolean shouldPoll();
|
||||
|
||||
/**
|
||||
* Returns the desired interval in milliseconds between calls to the
|
||||
* plugin's poll() method.
|
||||
* plugin's {@link Plugin#poll(Collection)} method.
|
||||
*/
|
||||
long getPollingInterval();
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ public interface PluginCallback {
|
||||
* Presents the user with a choice among two or more named options and
|
||||
* returns the user's response. The message may consist of a translatable
|
||||
* format string and arguments.
|
||||
* @return An index into the array of options indicating the user's choice,
|
||||
* @return an index into the array of options indicating the user's choice,
|
||||
* or -1 if the user cancelled the choice.
|
||||
*/
|
||||
int showChoice(String[] options, String... message);
|
||||
|
||||
@@ -5,6 +5,10 @@ import java.util.Collection;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Responsible for starting transport plugins at startup, stopping them at
|
||||
* shutdown, and providing access to plugins for exchanging invitations.
|
||||
*/
|
||||
public interface PluginManager {
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,8 +9,7 @@ import java.io.InputStream;
|
||||
*/
|
||||
public interface SimplexTransportReader {
|
||||
|
||||
/** Returns an input stream for reading from the transport.
|
||||
* @throws IOException */
|
||||
/** Returns an input stream for reading from the transport. */
|
||||
InputStream getInputStream() throws IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,7 @@ public interface SimplexTransportWriter {
|
||||
/** Returns the capacity of the transport in bytes. */
|
||||
long getCapacity();
|
||||
|
||||
/** Returns an output stream for writing to the transport.
|
||||
* @throws IOException */
|
||||
/** Returns an output stream for writing to the transport. */
|
||||
OutputStream getOutputStream() throws IOException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ public interface UiCallback {
|
||||
* Presents the user with a choice among two or more named options and
|
||||
* returns the user's response. The message may consist of a translatable
|
||||
* format string and arguments.
|
||||
* @return An index into the array of options indicating the user's choice,
|
||||
* @return an index into the array of options indicating the user's choice,
|
||||
* or -1 if the user cancelled the choice.
|
||||
*/
|
||||
int showChoice(String[] options, String... message);
|
||||
|
||||
@@ -274,8 +274,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
}
|
||||
|
||||
public PseudoRandom getPseudoRandom(int seed) {
|
||||
return new PseudoRandomImpl(getMessageDigest(), seed);
|
||||
public PseudoRandom getPseudoRandom(int seed1, int seed2) {
|
||||
return new PseudoRandomImpl(getMessageDigest(), seed1, seed2);
|
||||
}
|
||||
|
||||
public SecureRandom getSecureRandom() {
|
||||
|
||||
@@ -11,10 +11,11 @@ class PseudoRandomImpl implements PseudoRandom {
|
||||
private byte[] state;
|
||||
private int offset;
|
||||
|
||||
PseudoRandomImpl(MessageDigest messageDigest, int seed) {
|
||||
PseudoRandomImpl(MessageDigest messageDigest, int seed1, int seed2) {
|
||||
this.messageDigest = messageDigest;
|
||||
byte[] seedBytes = new byte[4];
|
||||
ByteUtils.writeUint32(seed, seedBytes, 0);
|
||||
byte[] seedBytes = new byte[8];
|
||||
ByteUtils.writeUint32(seed1, seedBytes, 0);
|
||||
ByteUtils.writeUint32(seed2, seedBytes, 4);
|
||||
messageDigest.update(seedBytes);
|
||||
state = messageDigest.digest();
|
||||
offset = 0;
|
||||
|
||||
@@ -24,9 +24,9 @@ import net.sf.briar.api.transport.TemporarySecret;
|
||||
/**
|
||||
* A low-level interface to the database (DatabaseComponent provides a
|
||||
* high-level interface). Most operations take a transaction argument, which is
|
||||
* obtained by calling startTransaction(). Every transaction must be
|
||||
* terminated by calling either abortTransaction() or commitTransaction(),
|
||||
* even if an exception is thrown.
|
||||
* obtained by calling {@link #startTransaction()}. Every transaction must be
|
||||
* terminated by calling either {@link #abortTransaction(T)} or
|
||||
* {@link #commitTransaction(T)}, even if an exception is thrown.
|
||||
* <p>
|
||||
* Locking is provided by the DatabaseComponent implementation. To prevent
|
||||
* deadlock, locks must be acquired in the following order:
|
||||
@@ -45,7 +45,7 @@ interface Database<T> {
|
||||
|
||||
/**
|
||||
* Opens the database.
|
||||
* @param resume True to reopen an existing database, false to create a
|
||||
* @param resume true to reopen an existing database, false to create a
|
||||
* new one.
|
||||
*/
|
||||
void open(boolean resume) throws DbException, IOException;
|
||||
|
||||
@@ -326,7 +326,7 @@ DatabaseCleaner.Callback {
|
||||
* that have changed from sendable to not sendable, or vice versa.
|
||||
* <p>
|
||||
* Locking: message write.
|
||||
* @param increment True if the message's sendability has changed from 0 to
|
||||
* @param increment true if the message's sendability has changed from 0 to
|
||||
* greater than 0, or false if it has changed from greater than 0 to 0.
|
||||
*/
|
||||
private int updateAncestorSendability(T txn, MessageId m, boolean increment)
|
||||
@@ -1396,7 +1396,7 @@ DatabaseCleaner.Callback {
|
||||
* the ancestors of those messages if necessary.
|
||||
* <p>
|
||||
* Locking: message write.
|
||||
* @param increment True if the user's rating for the author has changed
|
||||
* @param increment true if the user's rating for the author has changed
|
||||
* from not good to good, or false if it has changed from good to not good.
|
||||
*/
|
||||
private void updateAuthorSendability(T txn, AuthorId a, boolean increment)
|
||||
|
||||
81
src/net/sf/briar/invitation/InvitationManagerImpl.java
Normal file
81
src/net/sf/briar/invitation/InvitationManagerImpl.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.sf.briar.invitation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.sf.briar.api.crypto.CryptoComponent;
|
||||
import net.sf.briar.api.crypto.PseudoRandom;
|
||||
import net.sf.briar.api.invitation.ConfirmationCallback;
|
||||
import net.sf.briar.api.invitation.ConnectionCallback;
|
||||
import net.sf.briar.api.invitation.InvitationManager;
|
||||
import net.sf.briar.api.plugins.PluginManager;
|
||||
import net.sf.briar.api.plugins.duplex.DuplexPlugin;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
class InvitationManagerImpl implements InvitationManager {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
InvitationManagerImpl(CryptoComponent crypto, PluginManager pluginManager) {
|
||||
this.crypto = crypto;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
public void connect(int localCode, int remoteCode, ConnectionCallback c) {
|
||||
Collection<DuplexPlugin> plugins = pluginManager.getInvitationPlugins();
|
||||
// Alice is the party with the smaller invitation code
|
||||
if(localCode < remoteCode) {
|
||||
PseudoRandom r = crypto.getPseudoRandom(localCode, remoteCode);
|
||||
startAliceInvitationWorker(plugins, r, c);
|
||||
} else {
|
||||
PseudoRandom r = crypto.getPseudoRandom(remoteCode, localCode);
|
||||
startBobInvitationWorker(plugins, r, c);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAliceInvitationWorker(Collection<DuplexPlugin> plugins,
|
||||
PseudoRandom r, ConnectionCallback c) {
|
||||
// FIXME
|
||||
new FakeWorkerThread(c).start();
|
||||
}
|
||||
|
||||
private void startBobInvitationWorker(Collection<DuplexPlugin> plugins,
|
||||
PseudoRandom r, ConnectionCallback c) {
|
||||
// FIXME
|
||||
new FakeWorkerThread(c).start();
|
||||
}
|
||||
|
||||
private static class FakeWorkerThread extends Thread {
|
||||
|
||||
private final ConnectionCallback callback;
|
||||
|
||||
private FakeWorkerThread(ConnectionCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep((long) (Math.random() * 30 * 1000));
|
||||
} catch(InterruptedException ignored) {}
|
||||
if(Math.random() < 0.8) {
|
||||
callback.connectionNotEstablished();
|
||||
} else {
|
||||
callback.connectionEstablished(123456, 123456,
|
||||
new ConfirmationCallback() {
|
||||
|
||||
public void codesMatch() {}
|
||||
|
||||
public void codesDoNotMatch() {}
|
||||
});
|
||||
try {
|
||||
Thread.sleep((long) (Math.random() * 10 * 1000));
|
||||
} catch(InterruptedException ignored) {}
|
||||
if(Math.random() < 0.5) callback.codesMatch();
|
||||
else callback.codesDoNotMatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/net/sf/briar/invitation/InvitationModule.java
Normal file
13
src/net/sf/briar/invitation/InvitationModule.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package net.sf.briar.invitation;
|
||||
|
||||
import net.sf.briar.api.invitation.InvitationManager;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class InvitationModule extends AbstractModule {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(InvitationManager.class).to(InvitationManagerImpl.class);
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
public synchronized int start(Context appContext) {
|
||||
Set<TransportId> ids = new HashSet<TransportId>();
|
||||
// Instantiate and start the simplex plugins
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Starting simplex plugins");
|
||||
for(String s : getSimplexPluginFactoryNames()) {
|
||||
try {
|
||||
Class<?> c = Class.forName(s);
|
||||
@@ -128,6 +129,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
}
|
||||
}
|
||||
// Instantiate and start the duplex plugins
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Starting duplex plugins");
|
||||
for(String s : getDuplexPluginFactoryNames()) {
|
||||
try {
|
||||
Class<?> c = Class.forName(s);
|
||||
@@ -161,6 +163,7 @@ class PluginManagerImpl implements PluginManager {
|
||||
}
|
||||
}
|
||||
// Start the poller
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Starting poller");
|
||||
List<Plugin> plugins = new ArrayList<Plugin>();
|
||||
plugins.addAll(simplexPlugins);
|
||||
plugins.addAll(duplexPlugins);
|
||||
@@ -182,8 +185,10 @@ class PluginManagerImpl implements PluginManager {
|
||||
public synchronized int stop() {
|
||||
int stopped = 0;
|
||||
// Stop the poller
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Stopping poller");
|
||||
poller.stop();
|
||||
// Stop the simplex plugins
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Stopping simplex plugins");
|
||||
for(SimplexPlugin plugin : simplexPlugins) {
|
||||
try {
|
||||
plugin.stop();
|
||||
@@ -192,7 +197,9 @@ class PluginManagerImpl implements PluginManager {
|
||||
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
|
||||
}
|
||||
}
|
||||
simplexPlugins.clear();
|
||||
// Stop the duplex plugins
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Stopping duplex plugins");
|
||||
for(DuplexPlugin plugin : duplexPlugins) {
|
||||
try {
|
||||
plugin.stop();
|
||||
@@ -201,7 +208,9 @@ class PluginManagerImpl implements PluginManager {
|
||||
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
|
||||
}
|
||||
}
|
||||
duplexPlugins.clear();
|
||||
// Shut down the executors
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Stopping executors");
|
||||
pluginExecutor.shutdown();
|
||||
androidExecutor.shutdown();
|
||||
// Return the number of plugins successfully stopped
|
||||
|
||||
@@ -349,10 +349,10 @@ class DroidtoothPlugin implements DuplexPlugin {
|
||||
if(!running) return;
|
||||
}
|
||||
if(adapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) return;
|
||||
Intent intent = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
intent.putExtra(EXTRA_DISCOVERABLE_DURATION, 60);
|
||||
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
appContext.startActivity(intent);
|
||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
i.putExtra(EXTRA_DISCOVERABLE_DURATION, 120);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
appContext.startActivity(i);
|
||||
}
|
||||
|
||||
private static class BluetoothStateReceiver extends BroadcastReceiver {
|
||||
|
||||
@@ -11,7 +11,7 @@ import net.sf.briar.api.transport.ConnectionWriter;
|
||||
|
||||
/**
|
||||
* A ConnectionWriter that buffers its input and writes a frame whenever there
|
||||
* is a full frame to write or the flush() method is called.
|
||||
* is a full frame to write or the {@link #flush()} method is called.
|
||||
* <p>
|
||||
* This class is not thread-safe.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user