Separated Android code and core code into distinct Eclipse projects.

This should make it possible to develop the core in Eclipse without the ADT.
This commit is contained in:
akwizgran
2012-12-05 20:39:31 +00:00
parent 366d391d89
commit f4f7b96d50
345 changed files with 43 additions and 19 deletions

9
briar-android/.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry combineaccessrules="false" kind="src" path="/briar-core"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>

33
briar-android/.project Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>briar-android</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.sf.briar"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<application
android:theme="@style/LightTheme"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<service
android:name=".android.helloworld.HelloWorldService"
android:exported="false" >
<intent-filter>
<action android:name="net.sf.briar.android.helloworld.HelloWorldService" />
</intent-filter>
</service>
<activity
android:name=".android.helloworld.HelloWorldActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".android.invitation.AddContactActivity"
android:label="@string/add_a_contact" >
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,17 @@
# This file is used to override default values used by the Ant build system.
#
# This file must be checked into Version Control Systems, as it is
# integral to the build system of your project.
# This file is only used by the Ant script.
# You can use this to override default values such as
# 'source.dir' for the location of your java source folder and
# 'out.dir' for the location of your output folder.
# You can also use it define how the release builds are signed by declaring
# the following properties:
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.

92
briar-android/build.xml Normal file
View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Briar" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>

Binary file not shown.

4
briar-android/lint.xml Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<issue id="UseSparseArrays" severity="ignore" />
</lint>

View File

@@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,14 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-16

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LightTheme" parent="android:Theme.Holo.Light" />
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="roboguice_modules">
<item>net.sf.briar.android.AndroidModule</item>
<item>net.sf.briar.android.helloworld.HelloWorldModule</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>
<item>net.sf.briar.protocol.duplex.DuplexProtocolModule</item>
<item>net.sf.briar.protocol.simplex.SimplexProtocolModule</item>
<item>net.sf.briar.serial.SerialModule</item>
<item>net.sf.briar.transport.TransportModule</item>
</string-array>
</resources>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Briar</string>
<string name="welcome">Welcome to Briar! Add a contact to get started.</string>
<string name="face_to_face">For security reasons you must be face to face with someone to add them as a contact.</string>
<string name="add_contact_button">Add a contact</string>
<string name="add_a_contact">Add a Contact</string>
<string name="same_network">Briar can add contacts via Wi-Fi or Bluetooth. To use Wi-Fi you must both be connected to the same network.</string>
<string name="wifi_not_available">Wi-Fi is not available on this device</string>
<string name="wifi_disabled">Wi-Fi is OFF</string>
<string name="wifi_disconnected">Wi-Fi is DISCONNECTED</string>
<string name="wifi_connected">Wi-Fi is CONNECTED to %1$s</string>
<string name="bluetooth_not_available">Bluetooth is not available on this device</string>
<string name="bluetooth_disabled">Bluetooth is OFF</string>
<string name="bluetooth_not_discoverable">Bluetooth is NOT DISCOVERABLE</string>
<string name="bluetooth_enabled">Bluetooth is ON</string>
<string name="continue_button">Continue</string>
<string name="your_invitation_code">Your invitation code is</string>
<string name="enter_invitation_code">Please enter your contact\'s invitation code:</string>
<string name="connecting_wifi">Connecting via %1$s\u2026</string>
<string name="connecting_bluetooth">Connecting via Bluetooth\u2026</string>
<string name="connection_failed">Connection failed</string>
<string name="check_same_network">Please check that you are both using the same network.</string>
<string name="try_again_button">Try again</string>
<string name="connected_to_contact">Connected to contact</string>
<string name="your_confirmation_code">Your confirmation code is</string>
<string name="enter_confirmation_code">Please enter your contact\'s confirmation code:</string>
<string name="waiting_for_contact">Waiting for contact\u2026</string>
<string name="codes_do_not_match">Codes do not match</string>
<string name="interfering">This could mean that someone is trying to interfere with your connection.</string>
<string name="contact_added">Contact added</string>
<string name="enter_nickname">Please enter a nickname for this contact:</string>
<string name="add_another_contact_button">Add another contact</string>
<string name="done_button">Done</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LightTheme" parent="android:Theme.Light" />
</resources>

View File

@@ -0,0 +1,82 @@
package net.sf.briar.android;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean;
import net.sf.briar.api.android.AndroidExecutor;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import com.google.inject.Inject;
class AndroidExecutorImpl implements AndroidExecutor {
private static final int SHUTDOWN = 0, RUN = 1;
private final Runnable loop;
private final AtomicBoolean started = new AtomicBoolean(false);
private final CountDownLatch startLatch = new CountDownLatch(1);
private volatile Handler handler = null;
@Inject
AndroidExecutorImpl() {
loop = new Runnable() {
public void run() {
Looper.prepare();
handler = new FutureTaskHandler();
startLatch.countDown();
Looper.loop();
}
};
}
private void startIfNecessary() {
if(started.getAndSet(true)) return;
new Thread(loop, "AndroidExecutor").start();
try {
startLatch.await();
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public <V> V run(Callable<V> c) throws InterruptedException,
ExecutionException {
startIfNecessary();
Future<V> f = new FutureTask<V>(c);
Message m = Message.obtain(handler, RUN, f);
handler.sendMessage(m);
return f.get();
}
public void shutdown() {
if(handler != null) {
Message m = Message.obtain(handler, SHUTDOWN);
handler.sendMessage(m);
}
}
private static class FutureTaskHandler extends Handler {
@Override
public void handleMessage(Message m) {
switch(m.what) {
case SHUTDOWN:
Looper.myLooper().quit();
break;
case RUN:
((FutureTask<?>) m.obj).run();
break;
default:
throw new IllegalArgumentException();
}
}
}
}

View File

@@ -0,0 +1,13 @@
package net.sf.briar.android;
import net.sf.briar.api.android.AndroidExecutor;
import com.google.inject.AbstractModule;
public class AndroidModule extends AbstractModule {
@Override
protected void configure() {
bind(AndroidExecutor.class).to(AndroidExecutorImpl.class);
}
}

View File

@@ -0,0 +1,71 @@
package net.sf.briar.android.helloworld;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import java.util.logging.Logger;
import net.sf.briar.R;
import net.sf.briar.android.invitation.AddContactActivity;
import roboguice.activity.RoboActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout.LayoutParams;
import android.widget.TextView;
public class HelloWorldActivity extends RoboActivity
implements OnClickListener {
private static final Logger LOG =
Logger.getLogger(HelloWorldActivity.class.getName());
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(LOG.isLoggable(INFO)) LOG.info("Created");
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
TextView welcome = new TextView(this);
welcome.setPadding(0, 0, 0, 10);
welcome.setText(R.string.welcome);
layout.addView(welcome);
TextView faceToFace = new TextView(this);
faceToFace.setPadding(0, 0, 0, 10);
faceToFace.setText(R.string.face_to_face);
layout.addView(faceToFace);
Button addContact = new Button(this);
LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
addContact.setLayoutParams(lp);
addContact.setText(R.string.add_contact_button);
addContact.setCompoundDrawablesWithIntrinsicBounds(
R.drawable.social_add_person, 0, 0, 0);
addContact.setOnClickListener(this);
layout.addView(addContact);
setContentView(layout);
startService(new Intent(HelloWorldService.class.getName()));
}
@Override
public void onDestroy() {
super.onDestroy();
if(LOG.isLoggable(INFO)) LOG.info("Destroyed");
}
public void onClick(View view) {
startActivity(new Intent(this, AddContactActivity.class));
}
}

View File

@@ -0,0 +1,56 @@
package net.sf.briar.android.helloworld;
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;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Singleton;
public class HelloWorldModule extends AbstractModule {
@Override
protected void configure() {
bind(UiCallback.class).toInstance(new UiCallback() {
public int showChoice(String[] options, String... message) {
return -1;
}
public boolean showConfirmationMessage(String... message) {
return false;
}
public void showMessage(String... message) {}
});
}
@Provides @Singleton
DatabaseConfig getDatabaseConfig(final Application app) {
return new DatabaseConfig() {
public File getDataDirectory() {
return app.getApplicationContext().getDir("db", MODE_PRIVATE);
}
public Password getPassword() {
return new Password() {
public char[] getPassword() {
return "foo bar".toCharArray();
}
};
}
public long getMaxSize() {
return Long.MAX_VALUE;
}
};
}
}

View File

@@ -0,0 +1,97 @@
package net.sf.briar.android.helloworld;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.io.IOException;
import java.util.logging.Logger;
import net.sf.briar.api.crypto.KeyManager;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.plugins.PluginManager;
import roboguice.service.RoboService;
import android.content.Intent;
import android.os.IBinder;
import com.google.inject.Inject;
public class HelloWorldService extends RoboService {
private static final Logger LOG =
Logger.getLogger(HelloWorldService.class.getName());
@Inject private DatabaseComponent db;
@Inject private KeyManager keyManager;
@Inject private PluginManager pluginManager;
@Override
public void onCreate() {
super.onCreate();
if(LOG.isLoggable(INFO)) LOG.info("Created");
new Thread() {
@Override
public void run() {
startServices();
}
}.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(LOG.isLoggable(INFO)) LOG.info("Started");
return 0;
}
@Override
public IBinder onBind(Intent intent) {
if(LOG.isLoggable(INFO)) LOG.info("Bound");
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
if(LOG.isLoggable(INFO)) LOG.info("Destroyed");
new Thread() {
@Override
public void run() {
stopServices();
}
}.start();
}
private void startServices() {
try {
if(LOG.isLoggable(INFO)) LOG.info("Starting");
db.open(false);
if(LOG.isLoggable(INFO)) LOG.info("Database opened");
keyManager.start();
if(LOG.isLoggable(INFO)) LOG.info("Key manager started");
int pluginsStarted = pluginManager.start(this);
if(LOG.isLoggable(INFO))
LOG.info(pluginsStarted + " plugins started");
} catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
private void stopServices() {
try {
if(LOG.isLoggable(INFO)) LOG.info("Shutting down");
int pluginsStopped = pluginManager.stop();
if(LOG.isLoggable(INFO))
LOG.info(pluginsStopped + " plugins stopped");
keyManager.stop();
if(LOG.isLoggable(INFO)) LOG.info("Key manager stopped");
db.close();
if(LOG.isLoggable(INFO)) LOG.info("Database closed");
} catch(DbException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning(e.toString());
}
}
}

View File

@@ -0,0 +1,225 @@
package net.sf.briar.android.invitation;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.invitation.InvitationListener;
import net.sf.briar.api.invitation.InvitationManager;
import net.sf.briar.api.invitation.InvitationState;
import net.sf.briar.api.invitation.InvitationTask;
import roboguice.activity.RoboActivity;
import android.os.Bundle;
import com.google.inject.Inject;
public class AddContactActivity extends RoboActivity
implements InvitationListener {
@Inject private CryptoComponent crypto;
@Inject private InvitationManager invitationManager;
// All of the following must be accessed on the UI thread
private AddContactView view = null;
private InvitationTask task = null;
private String networkName = null;
private boolean useBluetooth = false;
private int localInvitationCode = -1, remoteInvitationCode = -1;
private int localConfirmationCode = -1, remoteConfirmationCode = -1;
private boolean connectionFailed = false;
private boolean localCompared = false, remoteCompared = false;
private boolean localMatched = false, remoteMatched = false;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if(state == null) {
// This is a new activity
setView(new NetworkSetupView(this));
} else {
// Restore the activity's state
networkName = state.getString("net.sf.briar.NETWORK_NAME");
useBluetooth = state.getBoolean("net.sf.briar.USE_BLUETOOTH");
int handle = state.getInt("TASK_HANDLE", -1);
task = invitationManager.getTask(handle);
if(task == null) {
// No background task - we must be in an initial or final state
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")) {
localCompared = remoteCompared = true;
localMatched = remoteMatched = true;
}
// Set the appropriate view for the state
if(localInvitationCode == -1) {
setView(new NetworkSetupView(this));
} else if(remoteInvitationCode == -1) {
setView(new InvitationCodeView(this));
} else if(connectionFailed) {
setView(new ConnectionFailedView(this));
} else if(localMatched && remoteMatched) {
setView(new ContactAddedView(this));
} else {
setView(new CodesDoNotMatchView(this));
}
} else {
// A background task exists - listen to it and get its state
InvitationState s = task.addListener(this);
localInvitationCode = s.getLocalInvitationCode();
remoteInvitationCode = s.getRemoteInvitationCode();
localConfirmationCode = s.getLocalConfirmationCode();
remoteConfirmationCode = s.getRemoteConfirmationCode();
connectionFailed = s.getConnectionFailed();
localCompared = s.getLocalCompared();
remoteCompared = s.getRemoteCompared();
localMatched = s.getLocalMatched();
remoteMatched = s.getRemoteMatched();
// Set the appropriate view for the state
if(localInvitationCode == -1) {
setView(new NetworkSetupView(this));
} else if(remoteInvitationCode == -1) {
setView(new InvitationCodeView(this));
} else if(localConfirmationCode == -1) {
setView(new ConnectionView(this));
} else if(connectionFailed) {
setView(new ConnectionFailedView(this));
} else if(!localCompared) {
setView(new ConfirmationCodeView(this));
} else if(!remoteCompared) {
setView(new WaitForContactView(this));
} else if(localMatched && remoteMatched) {
setView(new ContactAddedView(this));
} else {
setView(new CodesDoNotMatchView(this));
}
}
}
}
@Override
public void onResume() {
super.onResume();
view.populate();
}
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
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);
if(task != null) state.putInt("TASK_HANDLE", task.getHandle());
}
@Override
public void onDestroy() {
super.onDestroy();
if(task != null) task.removeListener(this);
}
void setView(AddContactView view) {
this.view = view;
view.init(this);
setContentView(view);
}
void reset(AddContactView view) {
task = null;
networkName = null;
useBluetooth = false;
localInvitationCode = -1;
localConfirmationCode = remoteConfirmationCode = -1;
connectionFailed = false;
localCompared = remoteCompared = false;
localMatched = remoteMatched = false;
setView(view);
}
void setNetworkName(String networkName) {
this.networkName = networkName;
}
String getNetworkName() {
return networkName;
}
void setUseBluetooth(boolean useBluetooth) {
this.useBluetooth = useBluetooth;
}
boolean getUseBluetooth() {
return useBluetooth;
}
int getLocalInvitationCode() {
if(localInvitationCode == -1)
localInvitationCode = crypto.generateInvitationCode();
return localInvitationCode;
}
void remoteInvitationCodeEntered(int code) {
setView(new ConnectionView(this));
// FIXME: These calls are blocking the UI thread for too long
task = invitationManager.createTask(localInvitationCode, code);
task.addListener(AddContactActivity.this);
task.connect();
}
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));
task.localConfirmationSucceeded();
} else {
setView(new CodesDoNotMatchView(this));
task.localConfirmationFailed();
}
}
public void connectionSucceeded(final int localCode, final int remoteCode) {
runOnUiThread(new Runnable() {
public void run() {
localConfirmationCode = localCode;
remoteConfirmationCode = remoteCode;
setView(new ConfirmationCodeView(AddContactActivity.this));
}
});
}
public void connectionFailed() {
runOnUiThread(new Runnable() {
public void run() {
connectionFailed = true;
setView(new ConnectionFailedView(AddContactActivity.this));
}
});
}
public void remoteConfirmationSucceeded() {
runOnUiThread(new Runnable() {
public void run() {
remoteCompared = true;
remoteMatched = true;
if(localMatched)
setView(new ContactAddedView(AddContactActivity.this));
}
});
}
public void remoteConfirmationFailed() {
runOnUiThread(new Runnable() {
public void run() {
remoteCompared = true;
if(localMatched)
setView(new CodesDoNotMatchView(AddContactActivity.this));
}
});
}
}

View 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();
}

View File

@@ -0,0 +1,6 @@
package net.sf.briar.android.invitation;
interface BluetoothStateListener {
void bluetoothStateChanged(boolean enabled);
}

View File

@@ -0,0 +1,93 @@
package net.sf.briar.android.invitation;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.provider.Settings.ACTION_BLUETOOTH_SETTINGS;
import static android.view.Gravity.CENTER;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import net.sf.briar.R;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class BluetoothWidget extends LinearLayout implements OnClickListener {
private BluetoothStateListener listener = null;
public BluetoothWidget(Context ctx) {
super(ctx);
}
void init(BluetoothStateListener listener) {
this.listener = listener;
setOrientation(HORIZONTAL);
setGravity(CENTER);
populate();
}
void populate() {
removeAllViews();
Context ctx = getContext();
TextView status = new TextView(ctx);
status.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1));
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if(adapter == null) {
bluetoothStateChanged(false);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.bluetooth_not_available);
addView(status);
} else if(adapter.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
bluetoothStateChanged(true);
ImageView ok = new ImageView(ctx);
ok.setImageResource(R.drawable.navigation_accept);
ok.setPadding(10, 10, 10, 10);
addView(ok);
status.setText(R.string.bluetooth_enabled);
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
} else if(adapter.isEnabled()) {
bluetoothStateChanged(false);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.bluetooth_not_discoverable);
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
} else {
bluetoothStateChanged(false);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.bluetooth_disabled);
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
}
}
private void bluetoothStateChanged(boolean enabled) {
listener.bluetoothStateChanged(enabled);
}
public void onClick(View view) {
getContext().startActivity(new Intent(ACTION_BLUETOOTH_SETTINGS));
}
}

View File

@@ -0,0 +1,6 @@
package net.sf.briar.android.invitation;
interface CodeEntryListener {
void codeEntered(int remoteCode);
}

View File

@@ -0,0 +1,93 @@
package net.sf.briar.android.invitation;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.text.InputType.TYPE_CLASS_NUMBER;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
import static net.sf.briar.api.plugins.InvitationConstants.MAX_CODE;
import net.sf.briar.R;
import android.content.Context;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
public class CodeEntryWidget extends LinearLayout implements
OnEditorActionListener, OnClickListener {
private CodeEntryListener listener = null;
private EditText codeEntry = null;
public CodeEntryWidget(Context ctx) {
super(ctx);
}
void init(CodeEntryListener listener, String prompt) {
this.listener = listener;
setOrientation(VERTICAL);
setGravity(CENTER_HORIZONTAL);
Context ctx = getContext();
TextView enterCode = new TextView(ctx);
enterCode.setGravity(CENTER_HORIZONTAL);
enterCode.setPadding(0, 0, 0, 10);
enterCode.setText(prompt);
addView(enterCode);
final Button continueButton = new Button(ctx);
continueButton.setText(R.string.continue_button);
continueButton.setEnabled(false);
continueButton.setOnClickListener(this);
codeEntry = new EditText(ctx) {
@Override
protected void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
continueButton.setEnabled(text.length() == 6);
}
};
codeEntry.setOnEditorActionListener(this);
codeEntry.setMinEms(5);
codeEntry.setMaxEms(5);
codeEntry.setMaxLines(1);
codeEntry.setInputType(TYPE_CLASS_NUMBER);
LinearLayout innerLayout = new LinearLayout(ctx);
innerLayout.setOrientation(HORIZONTAL);
innerLayout.setGravity(CENTER);
innerLayout.addView(codeEntry);
innerLayout.addView(continueButton);
addView(innerLayout);
}
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
if(!validateAndReturnCode()) codeEntry.setText("");
return true;
}
public void onClick(View view) {
if(!validateAndReturnCode()) codeEntry.setText("");
}
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;
// Hide the soft keyboard
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
listener.codeEntered(remoteCode);
return true;
}
}

View 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.reset(new NetworkSetupView(container));
}
}

View 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 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);
}
}

View File

@@ -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.reset(new InvitationCodeView(container));
}
}

View 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);
}
}
}

View File

@@ -0,0 +1,81 @@
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.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 {
ContactAddedView(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);
icon.setPadding(10, 10, 10, 10);
innerLayout.addView(icon);
TextView added = new TextView(ctx);
added.setText(R.string.contact_added);
added.setTextSize(20);
innerLayout.addView(added);
addView(innerLayout);
TextView enterNickname = new TextView(ctx);
enterNickname.setGravity(CENTER_HORIZONTAL);
enterNickname.setPadding(0, 0, 0, 10);
enterNickname.setText(R.string.enter_nickname);
addView(enterNickname);
innerLayout = new LinearLayout(ctx);
innerLayout.setOrientation(HORIZONTAL);
innerLayout.setGravity(CENTER);
final Button done = new Button(ctx);
EditText nicknameEntry = new EditText(ctx) {
@Override
protected void onTextChanged(CharSequence text, int start,
int lengthBefore, int lengthAfter) {
done.setEnabled(text.length() > 0);
}
};
nicknameEntry.setMinEms(10);
nicknameEntry.setMaxEms(20);
nicknameEntry.setMaxLines(1);
nicknameEntry.setOnEditorActionListener(this);
innerLayout.addView(nicknameEntry);
done.setText(R.string.done_button);
done.setEnabled(false);
done.setOnClickListener(this);
innerLayout.addView(done);
addView(innerLayout);
}
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
if(textView.getText().length() > 0) container.finish();
return true;
}
public void onClick(View view) {
container.finish(); // Done
}
}

View File

@@ -0,0 +1,40 @@
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 {
InvitationCodeView(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);
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);
}
}

View 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));
}
}

View 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);
}
}

View File

@@ -0,0 +1,6 @@
package net.sf.briar.android.invitation;
interface WifiStateListener {
void wifiStateChanged(String networkName);
}

View File

@@ -0,0 +1,102 @@
package net.sf.briar.android.invitation;
import static android.content.Context.WIFI_SERVICE;
import static android.provider.Settings.ACTION_WIFI_SETTINGS;
import static android.view.Gravity.CENTER;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import net.sf.briar.R;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
public class WifiWidget extends LinearLayout implements OnClickListener {
private WifiStateListener listener = null;
public WifiWidget(Context ctx) {
super(ctx);
}
void init(WifiStateListener listener) {
this.listener = listener;
setOrientation(HORIZONTAL);
setGravity(CENTER);
populate();
}
void populate() {
removeAllViews();
Context ctx = getContext();
TextView status = new TextView(ctx);
status.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT, 1));
WifiManager wifi = (WifiManager) ctx.getSystemService(WIFI_SERVICE);
if(wifi == null) {
wifiStateChanged(null);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.wifi_not_available);
addView(status);
} else if(wifi.isWifiEnabled()) {
WifiInfo info = wifi.getConnectionInfo();
String networkName = info.getSSID();
int networkId = info.getNetworkId();
if(networkName == null || networkId == -1) {
wifiStateChanged(null);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.wifi_disconnected);
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
} else {
wifiStateChanged(networkName);
ImageView ok = new ImageView(ctx);
ok.setImageResource(R.drawable.navigation_accept);
ok.setPadding(10, 10, 10, 10);
addView(ok);
Resources res = getResources();
String connected = res.getString(R.string.wifi_connected);
status.setText(String.format(connected, networkName));
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
}
} else {
wifiStateChanged(null);
ImageView warning = new ImageView(ctx);
warning.setImageResource(R.drawable.alerts_and_states_warning);
warning.setPadding(10, 10, 10, 10);
addView(warning);
status.setText(R.string.wifi_disabled);
addView(status);
ImageButton settings = new ImageButton(ctx);
settings.setImageResource(R.drawable.action_settings);
settings.setOnClickListener(this);
addView(settings);
}
}
private void wifiStateChanged(String networkName) {
if(listener != null) listener.wifiStateChanged(networkName);
}
public void onClick(View view) {
getContext().startActivity(new Intent(ACTION_WIFI_SETTINGS));
}
}