diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index f7362082dc8990e344eca835d9bb590b0598dc8f..9512c8e2d69c0ff76a2468f27257852356a500d0 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -9,6 +9,7 @@ <string name="blogs_button">Blogs</string> <string name="synchronize_button">Synchronize</string> <string name="quit_button">Quit</string> + <string name="new_identity_item">New identity\u2026</string> <string name="contact_list_title">Contacts</string> <string name="contact_connected">Connected</string> <string name="format_contact_last_connected">Last connected <br /> %1$s</string> diff --git a/briar-android/src/net/sf/briar/android/AuthorNameComparator.java b/briar-android/src/net/sf/briar/android/AuthorNameComparator.java deleted file mode 100644 index acde1829d838efa2a0b7cdfb7658e483755618f5..0000000000000000000000000000000000000000 --- a/briar-android/src/net/sf/briar/android/AuthorNameComparator.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.sf.briar.android; - -import java.util.Comparator; - -import net.sf.briar.api.Author; - -public class AuthorNameComparator implements Comparator<Author> { - - public static final AuthorNameComparator INSTANCE = - new AuthorNameComparator(); - - public int compare(Author a1, Author a2) { - return String.CASE_INSENSITIVE_ORDER.compare(a1.getName(), - a2.getName()); - } -} diff --git a/briar-android/src/net/sf/briar/android/LocalAuthorItem.java b/briar-android/src/net/sf/briar/android/LocalAuthorItem.java new file mode 100644 index 0000000000000000000000000000000000000000..0e2d63c095c562c5447261c7e1781e58b3b93c1b --- /dev/null +++ b/briar-android/src/net/sf/briar/android/LocalAuthorItem.java @@ -0,0 +1,19 @@ +package net.sf.briar.android; + +import net.sf.briar.api.LocalAuthor; + +public class LocalAuthorItem { + + public static final LocalAuthorItem ANONYMOUS = new LocalAuthorItem(null); + public static final LocalAuthorItem NEW = new LocalAuthorItem(null); + + private final LocalAuthor localAuthor; + + public LocalAuthorItem(LocalAuthor localAuthor) { + this.localAuthor = localAuthor; + } + + public LocalAuthor getLocalAuthor() { + return localAuthor; + } +} diff --git a/briar-android/src/net/sf/briar/android/LocalAuthorItemComparator.java b/briar-android/src/net/sf/briar/android/LocalAuthorItemComparator.java new file mode 100644 index 0000000000000000000000000000000000000000..c9ce0a1b0e43a86caaa14e9e22e3c7bb3f26b32c --- /dev/null +++ b/briar-android/src/net/sf/briar/android/LocalAuthorItemComparator.java @@ -0,0 +1,21 @@ +package net.sf.briar.android; + +import static net.sf.briar.android.LocalAuthorItem.ANONYMOUS; +import static net.sf.briar.android.LocalAuthorItem.NEW; + +import java.util.Comparator; + +public class LocalAuthorItemComparator implements Comparator<LocalAuthorItem> { + + public static final LocalAuthorItemComparator INSTANCE = + new LocalAuthorItemComparator(); + + public int compare(LocalAuthorItem a, LocalAuthorItem b) { + if(a == b) return 0; + if(a == ANONYMOUS || b == NEW) return -1; + if(a == NEW || b == ANONYMOUS) return 1; + String aName = a.getLocalAuthor().getName(); + String bName = b.getLocalAuthor().getName(); + return String.CASE_INSENSITIVE_ORDER.compare(aName, bName); + } +} diff --git a/briar-android/src/net/sf/briar/android/LocalAuthorSpinnerAdapter.java b/briar-android/src/net/sf/briar/android/LocalAuthorSpinnerAdapter.java index 668c5a427614d2d9369d0dc44f5a6b305ebb6c37..b112a405648933eb70dab89f347115cb8de1799b 100644 --- a/briar-android/src/net/sf/briar/android/LocalAuthorSpinnerAdapter.java +++ b/briar-android/src/net/sf/briar/android/LocalAuthorSpinnerAdapter.java @@ -1,40 +1,87 @@ package net.sf.briar.android; +import static net.sf.briar.android.LocalAuthorItem.ANONYMOUS; +import static net.sf.briar.android.LocalAuthorItem.NEW; + 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.LocalAuthor; import android.content.Context; import android.content.res.Resources; 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 ctx) { - super(ctx, android.R.layout.simple_spinner_item, - new ArrayList<LocalAuthor>()); + private final Context ctx; + private final boolean includeAnonymous; + private final List<LocalAuthorItem> list = new ArrayList<LocalAuthorItem>(); + + public LocalAuthorSpinnerAdapter(Context ctx, boolean includeAnonymous) { + this.ctx = ctx; + this.includeAnonymous = includeAnonymous; + } + + public void add(LocalAuthorItem item) { + list.add(item); + } + + public void clear() { + list.clear(); + } + + public int getCount() { + return includeAnonymous ? list.size() + 2 : list.size() + 1; } @Override + public View getDropDownView(int position, View convertView, + ViewGroup parent) { + return getView(position, convertView, parent); + } + + public LocalAuthorItem getItem(int position) { + if(includeAnonymous) { + if(position == 0) return ANONYMOUS; + if(position == list.size() + 1) return NEW; + return list.get(position - 1); + } else { + if(position == list.size()) return NEW; + return list.get(position); + } + } + + public long getItemId(int position) { + return android.R.layout.simple_spinner_item; + } + public View getView(int position, View convertView, ViewGroup parent) { - TextView name = new TextView(getContext()); + TextView name = new TextView(ctx); name.setTextSize(18); name.setMaxLines(1); - Resources res = getContext().getResources(); + Resources res = ctx.getResources(); int pad = res.getInteger(R.integer.spinner_padding); name.setPadding(pad, pad, pad, pad); - name.setText(getItem(position).getName()); + LocalAuthorItem item = getItem(position); + if(item == ANONYMOUS) name.setText(R.string.anonymous); + else if(item == NEW) name.setText(R.string.new_identity_item); + else name.setText(item.getLocalAuthor().getName()); return name; } @Override - public View getDropDownView(int position, View convertView, - ViewGroup parent) { - return getView(position, convertView, parent); + public boolean isEmpty() { + return getCount() == 0; + } + + public void sort(Comparator<LocalAuthorItem> comparator) { + Collections.sort(list, comparator); } } diff --git a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java index b205c5a200eab8e1f2d6df6de5f9cffc50166ed3..b5f83046a151e5948efa4c07ad44709b7a05a364 100644 --- a/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java +++ b/briar-android/src/net/sf/briar/android/contact/ContactListActivity.java @@ -139,6 +139,7 @@ implements OnClickListener, DatabaseListener, ConnectionListener { adapter.add(new ContactListItem(c, conn)); } adapter.sort(ContactComparator.INSTANCE); + adapter.notifyDataSetChanged(); } }); } diff --git a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java index 893442a6a0d065d094d90fe2bfcda217167fcc88..289c85c5927c39712df4c3f53a7ddadef41b7a11 100644 --- a/briar-android/src/net/sf/briar/android/groups/GroupActivity.java +++ b/briar-android/src/net/sf/briar/android/groups/GroupActivity.java @@ -142,6 +142,7 @@ OnClickListener, OnItemClickListener { adapter.clear(); for(GroupMessageHeader h : headers) adapter.add(h); adapter.sort(AscendingHeaderComparator.INSTANCE); + adapter.notifyDataSetChanged(); selectFirstUnread(); } }); diff --git a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java index 6759acba8815187811a83664d45830305948c165..45d08d452f21d4d0f7aac00716c68549d04458d1 100644 --- a/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java +++ b/briar-android/src/net/sf/briar/android/groups/GroupListActivity.java @@ -174,6 +174,7 @@ implements OnClickListener, DatabaseListener, NoGroupsDialog.Listener { adapter.add(new GroupListItem(g, headerList)); adapter.sort(GroupComparator.INSTANCE); } + adapter.notifyDataSetChanged(); selectFirstUnread(); } }); diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java index 0f1f6648d30dc5f001e6159e62e6390a0317eecb..4f7f2ff085509a763c9a8941a7431b576a577b96 100644 --- a/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java +++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupMessageActivity.java @@ -7,6 +7,8 @@ import static android.widget.LinearLayout.HORIZONTAL; import static android.widget.LinearLayout.VERTICAL; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static net.sf.briar.android.LocalAuthorItem.ANONYMOUS; +import static net.sf.briar.android.LocalAuthorItem.NEW; import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP; import java.io.IOException; @@ -20,11 +22,13 @@ import java.util.concurrent.Executor; import java.util.logging.Logger; import net.sf.briar.R; -import net.sf.briar.android.AuthorNameComparator; import net.sf.briar.android.BriarActivity; import net.sf.briar.android.BriarService; import net.sf.briar.android.BriarService.BriarServiceConnection; +import net.sf.briar.android.LocalAuthorItem; +import net.sf.briar.android.LocalAuthorItemComparator; import net.sf.briar.android.LocalAuthorSpinnerAdapter; +import net.sf.briar.android.identity.CreateIdentityActivity; import net.sf.briar.android.widgets.HorizontalSpace; import net.sf.briar.api.LocalAuthor; import net.sf.briar.api.android.BundleEncrypter; @@ -103,7 +107,7 @@ implements OnItemSelectedListener, OnClickListener { from.setText(R.string.from); header.addView(from); - fromAdapter = new LocalAuthorSpinnerAdapter(this); + fromAdapter = new LocalAuthorSpinnerAdapter(this, true); fromSpinner = new Spinner(this); fromSpinner.setAdapter(fromAdapter); fromSpinner.setOnItemSelectedListener(this); @@ -126,7 +130,7 @@ implements OnItemSelectedListener, OnClickListener { TextView to = new TextView(this); to.setTextSize(18); - to.setPadding(10, 10, 10, 10); + to.setPadding(10, 0, 0, 10); to.setText(R.string.to); header.addView(to); @@ -188,8 +192,10 @@ implements OnItemSelectedListener, OnClickListener { runOnUiThread(new Runnable() { public void run() { fromAdapter.clear(); - for(LocalAuthor a : localAuthors) fromAdapter.add(a); - fromAdapter.sort(AuthorNameComparator.INSTANCE); + for(LocalAuthor a : localAuthors) + fromAdapter.add(new LocalAuthorItem(a)); + fromAdapter.sort(LocalAuthorItemComparator.INSTANCE); + fromAdapter.notifyDataSetChanged(); } }); } @@ -256,7 +262,15 @@ implements OnItemSelectedListener, OnClickListener { public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if(parent == fromSpinner) { - localAuthor = fromAdapter.getItem(position); + LocalAuthorItem item = fromAdapter.getItem(position); + if(item == ANONYMOUS) { + localAuthor = null; + } else if(item == NEW) { + localAuthor = null; + startActivity(new Intent(this, CreateIdentityActivity.class)); + } else { + localAuthor = item.getLocalAuthor(); + } } else if(parent == toSpinner) { group = toAdapter.getItem(position); groupId = group.getId(); diff --git a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java index 07cd6e9dae99f4c556fbf5bf82278c00ab46a918..e07efb08f715c14e126d22c10f587f9219f9a73b 100644 --- a/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java +++ b/briar-android/src/net/sf/briar/android/invitation/AddContactActivity.java @@ -7,10 +7,11 @@ import java.util.Collection; import java.util.concurrent.Executor; import java.util.logging.Logger; -import net.sf.briar.android.AuthorNameComparator; import net.sf.briar.android.BriarActivity; import net.sf.briar.android.BriarService; import net.sf.briar.android.BriarService.BriarServiceConnection; +import net.sf.briar.android.LocalAuthorItem; +import net.sf.briar.android.LocalAuthorItemComparator; import net.sf.briar.android.LocalAuthorSpinnerAdapter; import net.sf.briar.api.AuthorId; import net.sf.briar.api.LocalAuthor; @@ -214,8 +215,10 @@ implements InvitationListener { runOnUiThread(new Runnable() { public void run() { adapter.clear(); - for(LocalAuthor a : localAuthors) adapter.add(a); - adapter.sort(AuthorNameComparator.INSTANCE); + for(LocalAuthor a : localAuthors) + adapter.add(new LocalAuthorItem(a)); + adapter.sort(LocalAuthorItemComparator.INSTANCE); + adapter.notifyDataSetChanged(); } }); } diff --git a/briar-android/src/net/sf/briar/android/invitation/NetworkSetupView.java b/briar-android/src/net/sf/briar/android/invitation/NetworkSetupView.java index 2a150f0a03868a3494cb3847f958bbb55ad41a46..ddd2ac6d6d2d998dae088233d595d6176c5bfb34 100644 --- a/briar-android/src/net/sf/briar/android/invitation/NetworkSetupView.java +++ b/briar-android/src/net/sf/briar/android/invitation/NetworkSetupView.java @@ -1,12 +1,16 @@ package net.sf.briar.android.invitation; import static android.view.Gravity.CENTER; +import static net.sf.briar.android.LocalAuthorItem.NEW; import static net.sf.briar.android.widgets.CommonLayoutParams.MATCH_WRAP; import static net.sf.briar.android.widgets.CommonLayoutParams.WRAP_WRAP; import net.sf.briar.R; +import net.sf.briar.android.LocalAuthorItem; import net.sf.briar.android.LocalAuthorSpinnerAdapter; +import net.sf.briar.android.identity.CreateIdentityActivity; import net.sf.briar.api.AuthorId; import android.content.Context; +import android.content.Intent; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; @@ -43,7 +47,7 @@ OnClickListener { yourNickname.setText(R.string.your_nickname); innerLayout.addView(yourNickname); - adapter = new LocalAuthorSpinnerAdapter(ctx); + adapter = new LocalAuthorSpinnerAdapter(ctx, false); spinner = new Spinner(ctx); spinner.setAdapter(adapter); spinner.setOnItemSelectedListener(this); @@ -90,13 +94,20 @@ OnClickListener { AuthorId localAuthorId = container.getLocalAuthorId(); boolean useBluetooth = container.getUseBluetooth(); String networkName = container.getNetworkName(); - continueButton.setEnabled(localAuthorId != null && - (useBluetooth || networkName != null)); + boolean networkAvailable = useBluetooth || networkName != null; + continueButton.setEnabled(localAuthorId != null && networkAvailable); } public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - container.setLocalAuthorId(adapter.getItem(position).getId()); + LocalAuthorItem item = adapter.getItem(position); + if(item == NEW) { + container.setLocalAuthorId(null); + Intent i = new Intent(container, CreateIdentityActivity.class); + container.startActivity(i); + } else { + container.setLocalAuthorId(item.getLocalAuthor().getId()); + } enableOrDisableContinueButton(); } diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java index effa869bc2bc5751d15258ca2adce394522428f1..fca12f6e4288b4c6a06a4dbdccb6f59831419d98 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationActivity.java @@ -149,6 +149,7 @@ implements DatabaseListener, OnClickListener, OnItemClickListener { adapter.clear(); for(PrivateMessageHeader h : headers) adapter.add(h); adapter.sort(AscendingHeaderComparator.INSTANCE); + adapter.notifyDataSetChanged(); selectFirstUnread(); } }); diff --git a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java index 950afba1c8e9755869893f363733d4492229c40d..0e8f50293fcc7c91b968953897a27f0c4fb1de1b 100644 --- a/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/ConversationListActivity.java @@ -148,6 +148,7 @@ implements OnClickListener, DatabaseListener, NoContactsDialog.Listener { adapter.add(new ConversationListItem(c, headerList)); adapter.sort(ConversationComparator.INSTANCE); } + adapter.notifyDataSetChanged(); selectFirstUnread(); } }); diff --git a/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java b/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java index db08956fb9fad67a90e418247e6323e656873c8c..0ddfced47472ef1f12f999ea2c90c76bc388000a 100644 --- a/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java +++ b/briar-android/src/net/sf/briar/android/messages/WritePrivateMessageActivity.java @@ -114,7 +114,7 @@ implements OnItemSelectedListener, OnClickListener { TextView to = new TextView(this); to.setTextSize(18); - to.setPadding(10, 10, 10, 10); + to.setPadding(10, 0, 0, 10); to.setText(R.string.to); header.addView(to);