Commit de472ba2 authored by akwizgran's avatar akwizgran

Create an identity at startup if the database doesn't exist.

parent ce7e9e73
......@@ -24,6 +24,14 @@
<action android:name="net.sf.briar.android.BriarService" />
</intent-filter>
</service>
<activity
android:name=".android.HomeScreenActivity"
android:label="@string/app_name" >
</activity>
<activity
android:name=".android.SetupActivity"
android:label="@string/app_name" >
</activity>
<activity
android:name=".android.SplashScreenActivity"
android:label="@string/app_name" >
......@@ -32,10 +40,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".android.HomeScreenActivity"
android:label="@string/app_name" >
</activity>
<activity
android:name=".android.contact.ContactListActivity"
android:label="@string/contact_list_title" >
......@@ -56,6 +60,10 @@
android:name=".android.groups.WriteGroupMessageActivity"
android:label="@string/compose_group_title" >
</activity>
<activity
android:name=".android.identity.CreateIdentityActivity"
android:label="@string/create_identity_title" >
</activity>
<activity
android:name=".android.invitation.AddContactActivity"
android:label="@string/add_contact_title" >
......
......@@ -11,4 +11,4 @@
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-16
target=android-17
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LightTheme" parent="android:Theme.Holo.Light" />
<integer name="horizontal_border_width">5</integer>
<integer name="spinner_padding">10</integer>
</resources>
\ No newline at end of file
......@@ -49,4 +49,8 @@
<string name="groups_title">Groups</string>
<string name="compose_group_title">New Post</string>
<string name="blogs_title">Blogs</string>
<string name="create_identity_item">New identity\u2026</string>
<string name="create_identity_title">Create an Identity</string>
<string name="choose_nickname">Choose your nickname:</string>
<string name="create_button">Create</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LightTheme" parent="android:Theme.Light" />
<integer name="horizontal_border_width">2</integer>
<integer name="spinner_padding">0</integer>
</resources>
\ No newline at end of file
......@@ -56,7 +56,7 @@ public class BriarService extends RoboService {
b.setContentIntent(pi);
b.setOngoing(true);
startForeground(1, b.build());
// Start the services in the background thread
// Start the services in a background thread
new Thread() {
@Override
public void run() {
......@@ -92,8 +92,11 @@ public class BriarService extends RoboService {
private void startServices() {
try {
if(LOG.isLoggable(INFO)) LOG.info("Starting");
db.open(false);
if(LOG.isLoggable(INFO)) LOG.info("Database opened");
boolean reopened = db.open();
if(LOG.isLoggable(INFO)) {
if(reopened) LOG.info("Database reopened");
else LOG.info("Database created");
}
keyManager.start();
if(LOG.isLoggable(INFO)) LOG.info("Key manager started");
int pluginsStarted = pluginManager.start(this);
......
package net.sf.briar.android;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.sf.briar.R;
import net.sf.briar.api.Author;
import net.sf.briar.api.LocalAuthor;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
public class LocalAuthorSpinnerAdapter extends ArrayAdapter<LocalAuthor>
public class LocalAuthorSpinnerAdapter extends BaseAdapter
implements SpinnerAdapter {
public LocalAuthorSpinnerAdapter(Context context) {
super(context, android.R.layout.simple_spinner_item,
new ArrayList<LocalAuthor>());
private final Context ctx;
private final List<LocalAuthor> list = new ArrayList<LocalAuthor>();
public LocalAuthorSpinnerAdapter(Context ctx) {
this.ctx = ctx;
}
public void add(LocalAuthor a) {
list.add(a);
notifyDataSetChanged();
}
public void clear() {
list.clear();
notifyDataSetChanged();
}
public int getCount() {
return list.size() + 1;
}
public LocalAuthor getItem(int position) {
if(position == list.size()) return null;
return list.get(position);
}
public long getItemId(int position) {
return android.R.layout.simple_spinner_item;
}
public void sort(Comparator<Author> comparator) {
Collections.sort(list, comparator);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView name = new TextView(getContext());
Log.i("Briar", "getView: " + position);
TextView name = new TextView(ctx);
name.setTextSize(18);
name.setMaxLines(1);
name.setPadding(10, 10, 10, 10);
name.setText(getItem(position).getName());
Resources res = ctx.getResources();
int pad = res.getInteger(R.integer.spinner_padding);
name.setPadding(pad, pad, pad, pad);
if(position == list.size()) name.setText(R.string.create_identity_item);
else name.setText(list.get(position).getName());
return name;
}
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
Log.i("Briar", "getDropDownView: " + position);
return getView(position, convertView, parent);
}
}
package net.sf.briar.android;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.InputMethodManager.HIDE_IMPLICIT_ONLY;
import static android.widget.LinearLayout.VERTICAL;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP;
import java.io.IOException;
import java.security.KeyPair;
import java.util.concurrent.Executor;
import net.sf.briar.R;
import net.sf.briar.api.AuthorFactory;
import net.sf.briar.api.LocalAuthor;
import net.sf.briar.api.android.ReferenceManager;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.crypto.CryptoExecutor;
import android.content.Intent;
import android.os.Bundle;
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.ProgressBar;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import com.google.inject.Inject;
public class SetupActivity extends BriarActivity
implements OnEditorActionListener, OnClickListener {
@Inject @CryptoExecutor private Executor cryptoExecutor;
private EditText nicknameEntry = null;
private Button createButton = null;
private ProgressBar progress = null;
// Fields that are accessed from background threads must be volatile
@Inject private volatile CryptoComponent crypto;
@Inject private volatile AuthorFactory authorFactory;
@Inject private volatile ReferenceManager referenceManager;
@Override
public void onCreate(Bundle state) {
super.onCreate(null);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(MATCH_MATCH);
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
TextView chooseNickname = new TextView(this);
chooseNickname.setGravity(CENTER);
chooseNickname.setTextSize(18);
chooseNickname.setPadding(10, 10, 10, 10);
chooseNickname.setText(R.string.choose_nickname);
layout.addView(chooseNickname);
nicknameEntry = new EditText(this);
nicknameEntry.setTextSize(18);
nicknameEntry.setMaxLines(1);
nicknameEntry.setPadding(10, 10, 10, 10);
nicknameEntry.setOnEditorActionListener(this);
layout.addView(nicknameEntry);
createButton = new Button(this);
createButton.setLayoutParams(WRAP_WRAP);
createButton.setText(R.string.create_button);
createButton.setOnClickListener(this);
layout.addView(createButton);
progress = new ProgressBar(this);
progress.setLayoutParams(WRAP_WRAP);
progress.setIndeterminate(true);
progress.setVisibility(GONE);
layout.addView(progress);
setContentView(layout);
}
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
validateNickname();
return true;
}
public void onClick(View view) {
if(!validateNickname()) return;
final String nickname = nicknameEntry.getText().toString();
// Replace the button with a progress bar
createButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
// Create the identity in a background thread
cryptoExecutor.execute(new Runnable() {
public void run() {
KeyPair keyPair = crypto.generateSignatureKeyPair();
final byte[] publicKey = keyPair.getPublic().getEncoded();
final byte[] privateKey = keyPair.getPrivate().getEncoded();
LocalAuthor a;
try {
a = authorFactory.createLocalAuthor(nickname, publicKey,
privateKey);
} catch(IOException e) {
throw new RuntimeException(e);
}
showHomeScreen(referenceManager.putReference(a,
LocalAuthor.class));
}
});
}
private void showHomeScreen(final long handle) {
runOnUiThread(new Runnable() {
public void run() {
Intent i = new Intent(SetupActivity.this,
HomeScreenActivity.class);
i.putExtra("net.sf.briar.LOCAL_AUTHOR_HANDLE", handle);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();
}
});
}
private boolean validateNickname() {
if(nicknameEntry.getText().toString().equals("")) return false;
// Hide the soft keyboard
Object o = getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0);
return true;
}
}
package net.sf.briar.android;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.view.Gravity.CENTER;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.api.db.DatabaseConfig;
import roboguice.RoboGuice;
import roboguice.activity.RoboSplashActivity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.google.inject.Injector;
public class SplashScreenActivity extends RoboSplashActivity {
public SplashScreenActivity() {
......@@ -30,8 +33,15 @@ public class SplashScreenActivity extends RoboSplashActivity {
}
protected void startNextActivity() {
Intent i = new Intent(this, HomeScreenActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
startActivity(i);
Injector guice = RoboGuice.getBaseApplicationInjector(getApplication());
if(guice.getInstance(DatabaseConfig.class).databaseExists()) {
Intent i = new Intent(this, HomeScreenActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
} else {
Intent i = new Intent(this, SetupActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
}
}
package net.sf.briar.android.contact;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP_1;
import java.util.Collection;
import java.util.Comparator;
......@@ -15,8 +20,8 @@ import net.sf.briar.android.BriarActivity;
import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.invitation.AddContactActivity;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.Contact;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.android.DatabaseUiExecutor;
......@@ -50,7 +55,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
@Inject private ConnectionRegistry connectionRegistry;
private ContactListAdapter adapter = null;
// Fields that are accessed from DB threads must be volatile
// Fields that are accessed from background threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
......@@ -58,33 +63,37 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
public void onCreate(Bundle state) {
super.onCreate(null);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
layout.setLayoutParams(MATCH_MATCH);
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
adapter = new ContactListAdapter(this);
ListView list = new ListView(this);
// Give me all the width and all the unused height
list.setLayoutParams(CommonLayoutParams.MATCH_WRAP_1);
list.setLayoutParams(MATCH_WRAP_1);
list.setAdapter(adapter);
list.setOnItemClickListener(adapter);
layout.addView(list);
layout.addView(new HorizontalBorder(this));
LinearLayout footer = new LinearLayout(this);
footer.setLayoutParams(MATCH_WRAP);
footer.setOrientation(HORIZONTAL);
footer.setGravity(CENTER);
footer.addView(new HorizontalSpace(this));
ImageButton addContactButton = new ImageButton(this);
addContactButton.setBackgroundResource(0);
addContactButton.setImageResource(R.drawable.social_add_person);
addContactButton.setOnClickListener(this);
layout.addView(addContactButton);
footer.addView(addContactButton);
footer.addView(new HorizontalSpace(this));
layout.addView(footer);
setContentView(layout);
// Listen for contacts being added or removed
db.addListener(this);
// Listen for contacts connecting or disconnecting
connectionRegistry.addListener(this);
// Bind to the service so we can wait for the DB to be opened
// Bind to the service so we can wait for it to start
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
}
......@@ -92,6 +101,8 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
@Override
public void onResume() {
super.onResume();
db.addListener(this);
connectionRegistry.addListener(this);
loadContacts();
}
......@@ -133,10 +144,15 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
}
@Override
public void onDestroy() {
super.onDestroy();
public void onPause() {
super.onPause();
db.removeListener(this);
connectionRegistry.removeListener(this);
}
@Override
public void onDestroy() {
super.onDestroy();
unbindService(serviceConnection);
}
......@@ -145,7 +161,6 @@ implements OnClickListener, DatabaseListener, ConnectionListener {
}
public void eventOccurred(DatabaseEvent e) {
// These events should be rare, so just reload the list
if(e instanceof ContactAddedEvent) loadContacts();
else if(e instanceof ContactRemovedEvent) loadContacts();
}
......
......@@ -2,11 +2,11 @@ package net.sf.briar.android.contact;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.widget.LinearLayout.HORIZONTAL;
import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP_1;
import java.util.ArrayList;
import net.sf.briar.R;
import net.sf.briar.android.widgets.CommonLayoutParams;
import android.content.Context;
import android.text.Html;
import android.text.format.DateUtils;
......@@ -44,7 +44,7 @@ implements OnItemClickListener {
TextView name = new TextView(ctx);
// Give me all the unused width
name.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
name.setLayoutParams(WRAP_WRAP_1);
name.setTextSize(18);
name.setMaxLines(1);
name.setPadding(0, 10, 10, 10);
......
......@@ -4,6 +4,8 @@ import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP_1;
import java.util.Collection;
import java.util.concurrent.Executor;
......@@ -14,7 +16,6 @@ import net.sf.briar.android.AscendingHeaderComparator;
import net.sf.briar.android.BriarActivity;
import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.api.Author;
import net.sf.briar.api.android.DatabaseUiExecutor;
......@@ -55,7 +56,7 @@ OnClickListener, OnItemClickListener {
private GroupAdapter adapter = null;
private ListView list = null;
// Fields that are accessed from DB threads must be volatile
// Fields that are accessed from background threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
private volatile GroupId groupId = null;
......@@ -66,22 +67,22 @@ OnClickListener, OnItemClickListener {
Intent i = getIntent();
restricted = i.getBooleanExtra("net.sf.briar.RESTRICTED", false);
byte[] id = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
if(id == null) throw new IllegalStateException();
groupId = new GroupId(id);
byte[] b = i.getByteArrayExtra("net.sf.briar.GROUP_ID");
if(b == null) throw new IllegalStateException();
groupId = new GroupId(b);
groupName = i.getStringExtra("net.sf.briar.GROUP_NAME");
if(groupName == null) throw new IllegalStateException();
setTitle(groupName);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
layout.setLayoutParams(MATCH_MATCH);
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
adapter = new GroupAdapter(this);
list = new ListView(this);
// Give me all the width and all the unused height
list.setLayoutParams(CommonLayoutParams.MATCH_WRAP_1);
list.setLayoutParams(MATCH_WRAP_1);
list.setAdapter(adapter);
list.setOnItemClickListener(this);
layout.addView(list);
......@@ -96,7 +97,7 @@ OnClickListener, OnItemClickListener {
setContentView(layout);
// Bind to the service so we can wait for the DB to be opened
// Bind to the service so we can wait for it to start
bindService(new Intent(BriarService.class.getName()),
serviceConnection, 0);
}
......@@ -234,8 +235,6 @@ OnClickListener, OnItemClickListener {
}
i.putExtra("net.sf.briar.CONTENT_TYPE", item.getContentType());
i.putExtra("net.sf.briar.TIMESTAMP", item.getTimestamp());
i.putExtra("net.sf.briar.FIRST", position == 0);
i.putExtra("net.sf.briar.LAST", position == adapter.getCount() - 1);
startActivityForResult(i, position);
}
}
......@@ -6,13 +6,13 @@ import static android.view.View.INVISIBLE;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.text.DateFormat.SHORT;
import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP_1;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import java.util.ArrayList;
import net.sf.briar.R;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.Author;
import net.sf.briar.api.db.GroupMessageHeader;
......@@ -39,6 +39,7 @@ class GroupAdapter extends ArrayAdapter<GroupMessageHeader> {
public View getView(int position, View convertView, ViewGroup parent) {
GroupMessageHeader item = getItem(position);
Context ctx = getContext();
// FIXME: Use a RelativeLayout
LinearLayout layout = new LinearLayout(ctx);
layout.setOrientation(HORIZONTAL);
......@@ -49,7 +50,7 @@ class GroupAdapter extends ArrayAdapter<GroupMessageHeader> {
LinearLayout innerLayout = new LinearLayout(ctx);
// Give me all the unused width
innerLayout.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
innerLayout.setLayoutParams(WRAP_WRAP_1);
innerLayout.setOrientation(VERTICAL);
LinearLayout authorLayout = new LinearLayout(ctx);
......@@ -66,7 +67,7 @@ class GroupAdapter extends ArrayAdapter<GroupMessageHeader> {
TextView name = new TextView(ctx);
// Give me all the unused width
name.setLayoutParams(CommonLayoutParams.WRAP_WRAP_1);
name.setLayoutParams(WRAP_WRAP_1);
name.setTextSize(18);
name.setMaxLines(1);
name.setPadding(0, 10, 10, 10);
......
......@@ -6,6 +6,9 @@ import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP;
import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP_1;
import java.util.ArrayList;
import java.util.Collection;
......@@ -18,7 +21,6 @@ import net.sf.briar.R;
import net.sf.briar.android.BriarActivity;
import net.sf.briar.android.BriarService;
import net.sf.briar.android.BriarService.BriarServiceConnection;
import net.sf.briar.android.widgets.CommonLayoutParams;
import net.sf.briar.android.widgets.HorizontalBorder;
import net.sf.briar.android.widgets.HorizontalSpace;
import net.sf.briar.api.android.DatabaseUiExecutor;
......@@ -56,7 +58,7 @@ implements OnClickListener, DatabaseListener {
private ListView list = null;
private ImageButton newGroupButton = null, composeButton = null;
// Fields that are accessed from DB threads must be volatile
// Fields that are accessed from background threads must be volatile
@Inject private volatile DatabaseComponent db;
@Inject @DatabaseUiExecutor private volatile Executor dbUiExecutor;
private volatile boolean restricted = false;
......@@ -65,7 +67,7 @@ implements OnClickListener, DatabaseListener {
public void onCreate(Bundle state) {
super.onCreate(null);
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(CommonLayoutParams.MATCH_MATCH);
layout.setLayoutParams(MATCH_MATCH);
layout.setOrientation(VERTICAL);
layout.setGravity(CENTER_HORIZONTAL);
......@@ -78,7 +80,7 @@ implements OnClickListener, DatabaseListener {
adapter = new GroupListAdapter(this);
list<