diff --git a/briar-android/res/drawable/ic_check_white.xml b/briar-android/res/drawable/ic_check_white.xml
new file mode 100644
index 000000000..59f823220
--- /dev/null
+++ b/briar-android/res/drawable/ic_check_white.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/briar-android/res/layout/list_item_contact.xml b/briar-android/res/layout/list_item_contact.xml
index 8f879c90a..4f1410693 100644
--- a/briar-android/res/layout/list_item_contact.xml
+++ b/briar-android/res/layout/list_item_contact.xml
@@ -64,6 +64,7 @@
android:layout_height="wrap_content"
android:textColor="@android:color/tertiary_text_light"
android:textSize="@dimen/text_size_tiny"
+ android:visibility="gone"
tools:text="My Identity"/>
diff --git a/briar-android/res/layout/list_item_selectable_contact.xml b/briar-android/res/layout/list_item_selectable_contact.xml
new file mode 100644
index 000000000..920129df0
--- /dev/null
+++ b/briar-android/res/layout/list_item_selectable_contact.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/menu/forum_share_actions.xml b/briar-android/res/menu/forum_share_actions.xml
new file mode 100644
index 000000000..f128fd17c
--- /dev/null
+++ b/briar-android/res/menu/forum_share_actions.xml
@@ -0,0 +1,12 @@
+
+
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java
new file mode 100644
index 000000000..590c7a2d7
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java
@@ -0,0 +1,189 @@
+package org.briarproject.android.contact;
+
+import android.content.Context;
+import android.support.v7.util.SortedList;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.briarproject.R;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.identity.Author;
+
+import java.util.List;
+
+import im.delight.android.identicons.IdenticonDrawable;
+
+import static android.support.v7.util.SortedList.INVALID_POSITION;
+
+public abstract class BaseContactListAdapter
+ extends RecyclerView.Adapter {
+
+ protected SortedList contacts;
+ protected final OnItemClickListener listener;
+ protected Context ctx;
+
+ public BaseContactListAdapter(Context context, OnItemClickListener listener) {
+ this.ctx = context;
+ this.listener = listener;
+ this.contacts = new SortedList(ContactListItem.class,
+ new SortedListCallBacks());
+ }
+
+ @Override
+ public void onBindViewHolder(final VH ui, final int position) {
+ final ContactListItem item = getItem(position);
+
+ Author author = item.getContact().getAuthor();
+ ui.avatar.setImageDrawable(
+ new IdenticonDrawable(author.getId().getBytes()));
+ String contactName = author.getName();
+ ui.name.setText(contactName);
+
+ ui.layout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listener != null) listener.onItemClick(ui.avatar, item);
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return contacts.size();
+ }
+
+ public ContactListItem getItem(int position) {
+ if (position == INVALID_POSITION || contacts.size() <= position) {
+ return null; // Not found
+ }
+ return contacts.get(position);
+ }
+
+ public void updateItem(int position, ContactListItem item) {
+ contacts.updateItemAt(position, item);
+ }
+
+ public int findItemPosition(ContactListItem c) {
+ return contacts.indexOf(c);
+ }
+
+ public int findItemPosition(ContactId c) {
+ int count = getItemCount();
+ for (int i = 0; i < count; i++) {
+ ContactListItem item = getItem(i);
+ if (item.getContact().getId().equals(c)) return i;
+ }
+ return INVALID_POSITION; // Not found
+ }
+
+ public void addAll(List contacts) {
+ this.contacts.addAll(contacts);
+ }
+
+ public void add(ContactListItem contact) {
+ contacts.add(contact);
+ }
+
+ public void remove(ContactListItem contact) {
+ contacts.remove(contact);
+ }
+
+ public void clear() {
+ contacts.clear();
+ }
+
+ public static class BaseContactHolder extends RecyclerView.ViewHolder {
+ public final ViewGroup layout;
+ public final ImageView avatar;
+ public final TextView name;
+
+ public BaseContactHolder(View v) {
+ super(v);
+
+ layout = (ViewGroup) v;
+ avatar = (ImageView) v.findViewById(R.id.avatarView);
+ name = (TextView) v.findViewById(R.id.nameView);
+ }
+ }
+
+ public int compareContactListItems(ContactListItem c1, ContactListItem c2) {
+ return compareByName(c1, c2);
+ }
+
+ protected int compareByName(ContactListItem c1, ContactListItem c2) {
+ int authorCompare = c1.getLocalAuthor().getName()
+ .compareTo(c2.getLocalAuthor().getName());
+ if (authorCompare == 0) {
+ // if names are equal, compare by time instead
+ return compareByTime(c1, c2);
+ } else {
+ return authorCompare;
+ }
+ }
+
+ protected int compareByTime(ContactListItem c1, ContactListItem c2) {
+ long time1 = c1.getTimestamp();
+ long time2 = c2.getTimestamp();
+ if (time1 < time2) return 1;
+ if (time1 > time2) return -1;
+ return 0;
+ }
+
+ protected class SortedListCallBacks extends SortedList.Callback {
+
+ @Override
+ public void onInserted(int position, int count) {
+ notifyItemRangeInserted(position, count);
+ }
+
+ @Override
+ public void onChanged(int position, int count) {
+ notifyItemRangeChanged(position, count);
+ }
+
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ notifyItemMoved(fromPosition, toPosition);
+ }
+
+ @Override
+ public void onRemoved(int position, int count) {
+ notifyItemRangeRemoved(position, count);
+ }
+
+ @Override
+ public int compare(ContactListItem c1, ContactListItem c2) {
+ return compareContactListItems(c1, c2);
+ }
+
+ @Override
+ public boolean areItemsTheSame(ContactListItem c1, ContactListItem c2) {
+ return c1.getContact().getId().equals(c2.getContact().getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(ContactListItem c1,
+ ContactListItem c2) {
+ // check for all properties that influence visual
+ // representation of contact
+ if (c1.isConnected() != c2.isConnected()) {
+ return false;
+ }
+ if (c1.getUnreadCount() != c2.getUnreadCount()) {
+ return false;
+ }
+ if (c1.getTimestamp() != c2.getTimestamp()) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public interface OnItemClickListener {
+ void onItemClick(View view, ContactListItem item);
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
index becdaa922..dee3031f7 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java
@@ -1,14 +1,7 @@
package org.briarproject.android.contact;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.os.Build;
import android.support.v4.content.ContextCompat;
-import android.support.v7.util.SortedList;
-import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -17,98 +10,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.identity.Author;
-import org.briarproject.api.identity.AuthorId;
-
-import java.util.List;
-
-import im.delight.android.identicons.IdenticonDrawable;
-
-import static android.support.v7.util.SortedList.INVALID_POSITION;
public class ContactListAdapter
- extends RecyclerView.Adapter {
+ extends BaseContactListAdapter {
- private final SortedList contacts =
- new SortedList(ContactListItem.class,
- new SortedList.Callback() {
- @Override
- public void onInserted(int position, int count) {
- notifyItemRangeInserted(position, count);
- }
-
- @Override
- public void onChanged(int position, int count) {
- notifyItemRangeChanged(position, count);
- }
-
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- notifyItemMoved(fromPosition, toPosition);
- }
-
- @Override
- public void onRemoved(int position, int count) {
- notifyItemRangeRemoved(position, count);
- }
-
- @Override
- public int compare(ContactListItem c1,
- ContactListItem c2) {
- int authorCompare = 0;
- if (chooser) {
- authorCompare = c1.getLocalAuthor().getName()
- .compareTo(
- c2.getLocalAuthor().getName());
- }
- if (authorCompare == 0) {
- // sort items by time
- // and do not take unread messages into account
- long time1 = c1.getTimestamp();
- long time2 = c2.getTimestamp();
- if (time1 < time2) return 1;
- if (time1 > time2) return -1;
- return 0;
- } else {
- return authorCompare;
- }
- }
-
- @Override
- public boolean areItemsTheSame(ContactListItem c1,
- ContactListItem c2) {
- return c1.getContact().getId()
- .equals(c2.getContact().getId());
- }
-
- @Override
- public boolean areContentsTheSame(ContactListItem c1,
- ContactListItem c2) {
- // check for all properties that influence visual
- // representation of contact
- if (c1.isConnected() != c2.isConnected()) {
- return false;
- }
- if (c1.getUnreadCount() != c2.getUnreadCount()) {
- return false;
- }
- if (c1.getTimestamp() != c2.getTimestamp()) {
- return false;
- }
- return true;
- }
- });
- private final OnItemClickListener listener;
- private final boolean chooser;
- private Context ctx;
- private AuthorId localAuthorId;
-
- public ContactListAdapter(Context context, OnItemClickListener listener,
- boolean chooser) {
- ctx = context;
- this.listener = listener;
- this.chooser = chooser;
+ public ContactListAdapter(Context context, OnItemClickListener listener) {
+ super(context, listener);
}
@Override
@@ -121,38 +28,25 @@ public class ContactListAdapter
@Override
public void onBindViewHolder(final ContactHolder ui, final int position) {
+ super.onBindViewHolder(ui, position);
+
final ContactListItem item = getItem(position);
+ // name and unread count
+ String contactName = item.getContact().getAuthor().getName();
int unread = item.getUnreadCount();
- if (!chooser && unread > 0) {
- ui.layout.setBackgroundColor(
- ContextCompat.getColor(ctx, R.color.unread_background));
- }
-
- if (item.isConnected()) {
- ui.bulb.setImageResource(R.drawable.contact_connected);
- } else {
- ui.bulb.setImageResource(R.drawable.contact_disconnected);
- }
-
- Author author = item.getContact().getAuthor();
- ui.avatar.setImageDrawable(
- new IdenticonDrawable(author.getId().getBytes()));
- String contactName = author.getName();
-
- if (!chooser && unread > 0) {
+ if (unread > 0) {
// TODO show these in a bubble on top of the avatar
ui.name.setText(contactName + " (" + unread + ")");
+
+ // different background for contacts with unread messages
+ ui.layout.setBackgroundColor(
+ ContextCompat.getColor(ctx, R.color.unread_background));
} else {
ui.name.setText(contactName);
}
- if (chooser) {
- ui.identity.setText(item.getLocalAuthor().getName());
- } else {
- ui.identity.setVisibility(View.GONE);
- }
-
+ // date of last message
if (item.isEmpty()) {
ui.date.setText(R.string.no_private_messages);
} else {
@@ -162,115 +56,33 @@ public class ContactListAdapter
DateUtils.getRelativeTimeSpanString(ctx, timestamp));
}
- if (chooser && !item.getLocalAuthor().getId().equals(localAuthorId)) {
- grayOutItem(ui);
- }
-
- ui.layout.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- listener.onItemClick(ui.avatar, item);
- }
- });
- }
-
- @Override
- public int getItemCount() {
- return contacts.size();
- }
-
- /**
- * Set the identity from whose perspective the contact shall be chosen.
- * This is only used if chooser is true.
- * @param authorId The ID of the local Author
- */
- public void setLocalAuthor(AuthorId authorId) {
- localAuthorId = authorId;
- notifyDataSetChanged();
- }
-
- private void grayOutItem(final ContactHolder ui) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- float alpha = 0.25f;
- ui.bulb.setAlpha(alpha);
- ui.avatar.setAlpha(alpha);
- ui.name.setAlpha(alpha);
- ui.date.setAlpha(alpha);
- ui.identity.setAlpha(alpha);
+ // online/offline
+ if (item.isConnected()) {
+ ui.bulb.setImageResource(R.drawable.contact_connected);
} else {
- ColorFilter colorFilter = new PorterDuffColorFilter(Color.GRAY,
- PorterDuff.Mode.MULTIPLY);
- ui.bulb.setColorFilter(colorFilter);
- ui.avatar.setColorFilter(colorFilter);
- ui.name.setEnabled(false);
- ui.date.setEnabled(false);
+ ui.bulb.setImageResource(R.drawable.contact_disconnected);
}
}
- public ContactListItem getItem(int position) {
- if (position == INVALID_POSITION || contacts.size() <= position) {
- return null; // Not found
- }
- return contacts.get(position);
- }
+ protected static class ContactHolder
+ extends BaseContactListAdapter.BaseContactHolder {
- public void updateItem(int position, ContactListItem item) {
- contacts.updateItemAt(position, item);
- }
-
- public int findItemPosition(ContactId c) {
- int count = getItemCount();
- for (int i = 0; i < count; i++) {
- ContactListItem item = getItem(i);
- if (item.getContact().getId().equals(c)) return i;
- }
- return INVALID_POSITION; // Not found
- }
-
- public void addAll(List contacts) {
- this.contacts.addAll(contacts);
- }
-
- public void add(ContactListItem contact) {
- contacts.add(contact);
- }
-
- public void remove(ContactListItem contact) {
- contacts.remove(contact);
- }
-
- public void clear() {
- contacts.beginBatchedUpdates();
-
- while(contacts.size() != 0) {
- contacts.removeItemAt(0);
- }
-
- contacts.endBatchedUpdates();
- }
-
- public static class ContactHolder extends RecyclerView.ViewHolder {
- public ViewGroup layout;
- public ImageView bulb;
- public ImageView avatar;
- public TextView name;
- public TextView identity;
- public TextView date;
+ public final ImageView bulb;
+ public final TextView date;
+ public final TextView identity;
public ContactHolder(View v) {
super(v);
- layout = (ViewGroup) v;
bulb = (ImageView) v.findViewById(R.id.bulbView);
- avatar = (ImageView) v.findViewById(R.id.avatarView);
- name = (TextView) v.findViewById(R.id.nameView);
- identity = (TextView) v.findViewById(R.id.identityView);
date = (TextView) v.findViewById(R.id.dateView);
+ identity = (TextView) v.findViewById(R.id.identityView);
}
}
- public interface OnItemClickListener {
- void onItemClick(View view, ContactListItem item);
+ @Override
+ public int compareContactListItems(ContactListItem c1, ContactListItem c2) {
+ return compareByTime(c1, c2);
}
}
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index f3737bd1d..6e063bb36 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -101,7 +101,7 @@ public class ContactListFragment extends BaseEventFragment {
inflater.inflate(R.layout.activity_contact_list, container,
false);
- ContactListAdapter.OnItemClickListener onItemClickListener =
+ BaseContactListAdapter.OnItemClickListener onItemClickListener =
new ContactListAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, ContactListItem item) {
@@ -124,8 +124,7 @@ public class ContactListFragment extends BaseEventFragment {
}
};
- adapter = new ContactListAdapter(getContext(), onItemClickListener,
- false);
+ adapter = new ContactListAdapter(getContext(), onItemClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
list.setLayoutManager(new LinearLayoutManager(getContext()));
list.setAdapter(adapter);
diff --git a/briar-android/src/org/briarproject/android/contact/SelectContactsDialog.java b/briar-android/src/org/briarproject/android/contact/SelectContactsDialog.java
deleted file mode 100644
index 53b6588d7..000000000
--- a/briar-android/src/org/briarproject/android/contact/SelectContactsDialog.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.briarproject.android.contact;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnMultiChoiceClickListener;
-import android.support.v7.app.AlertDialog;
-
-import org.briarproject.R;
-import org.briarproject.api.contact.Contact;
-import org.briarproject.api.contact.ContactId;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-public class SelectContactsDialog implements OnMultiChoiceClickListener {
-
- private Listener listener = null;
- private List contacts = null;
- private Set selected = null;
-
- public void setListener(Listener listener) {
- this.listener = listener;
- }
-
- public void setContacts(Collection contacts) {
- this.contacts = new ArrayList(contacts);
- }
-
- public void setSelected(Collection selected) {
- this.selected = new HashSet(selected);
- }
-
- public Dialog build(Context ctx) {
- if (listener == null || contacts == null || selected == null)
- throw new IllegalStateException();
- AlertDialog.Builder builder = new AlertDialog.Builder(ctx,
- R.style.BriarDialogTheme);
- int size = contacts.size();
- String[] names = new String[size];
- boolean[] checked = new boolean[size];
- for (int i = 0; i < size; i++) {
- Contact c = contacts.get(i);
- names[i] = c.getAuthor().getName();
- checked[i] = selected.contains(c.getId());
- }
- builder.setMultiChoiceItems(names, checked, this);
- builder.setPositiveButton(R.string.done_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- listener.contactsSelected(selected);
- }
- });
- builder.setNegativeButton(R.string.cancel_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- listener.contactSelectionCancelled();
- }
- });
- return builder.create();
- }
-
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (isChecked) selected.add(contacts.get(which).getId());
- else selected.remove(contacts.get(which).getId());
- }
-
- public interface Listener {
-
- void contactsSelected(Collection selected);
-
- void contactSelectionCancelled();
- }
-}
diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java
new file mode 100644
index 000000000..0e3e81181
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java
@@ -0,0 +1,81 @@
+package org.briarproject.android.forum;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+
+import org.briarproject.R;
+import org.briarproject.android.contact.BaseContactListAdapter;
+import org.briarproject.android.contact.ContactListItem;
+import org.briarproject.api.contact.ContactId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class ContactSelectorAdapter
+ extends
+ BaseContactListAdapter {
+
+ public ContactSelectorAdapter(Context context,
+ OnItemClickListener listener) {
+
+ super(context, listener);
+ }
+
+ @Override
+ public SelectableContactHolder onCreateViewHolder(ViewGroup viewGroup,
+ int i) {
+ View v = LayoutInflater.from(ctx)
+ .inflate(R.layout.list_item_selectable_contact, viewGroup,
+ false);
+
+ return new SelectableContactHolder(v);
+ }
+
+ @Override
+ public void onBindViewHolder(final SelectableContactHolder ui,
+ final int position) {
+ super.onBindViewHolder(ui, position);
+
+ final SelectableContactListItem item =
+ (SelectableContactListItem) getItem(position);
+
+ if (item.isSelected()) {
+ ui.checkBox.setChecked(true);
+ } else {
+ ui.checkBox.setChecked(false);
+ }
+ }
+
+ public Collection getSelectedContactIds() {
+ Collection selected = new ArrayList();
+
+ for (int i = 0; i < contacts.size(); i++) {
+ SelectableContactListItem item =
+ (SelectableContactListItem) contacts.get(i);
+ if (item.isSelected()) selected.add(item.getContact().getId());
+ }
+
+ return selected;
+ }
+
+ protected static class SelectableContactHolder
+ extends BaseContactListAdapter.BaseContactHolder {
+
+ private final CheckBox checkBox;
+
+ public SelectableContactHolder(View v) {
+ super(v);
+
+ checkBox = (CheckBox) v.findViewById(R.id.checkBox);
+ }
+ }
+
+ @Override
+ public int compareContactListItems(ContactListItem c1, ContactListItem c2) {
+ return compareByName(c1, c2);
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 8f2f58d34..cd44592df 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -2,6 +2,8 @@ package org.briarproject.android.forum;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -42,6 +44,8 @@ import java.util.logging.Logger;
import javax.inject.Inject;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.View.GONE;
@@ -152,9 +156,13 @@ public class ForumActivity extends BriarActivity implements EventListener,
return true;
case R.id.action_forum_share:
Intent i2 = new Intent(this, ShareForumActivity.class);
+ i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
i2.putExtra(GROUP_ID, groupId.getBytes());
i2.putExtra(FORUM_NAME, forum.getName());
- startActivity(i2);
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeCustomAnimation(this, android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right);
+ ActivityCompat.startActivity(this, i2, options.toBundle());
return true;
default:
return super.onOptionsItemSelected(item);
diff --git a/briar-android/src/org/briarproject/android/forum/NoContactsDialog.java b/briar-android/src/org/briarproject/android/forum/NoContactsDialog.java
deleted file mode 100644
index 583e25d82..000000000
--- a/briar-android/src/org/briarproject/android/forum/NoContactsDialog.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.briarproject.android.forum;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.support.v7.app.AlertDialog;
-
-import org.briarproject.R;
-
-public class NoContactsDialog {
-
- private Listener listener = null;
-
- public void setListener(Listener listener) {
- this.listener = listener;
- }
-
- public Dialog build(Context ctx) {
- if (listener == null) throw new IllegalStateException();
- AlertDialog.Builder builder = new AlertDialog.Builder(ctx,
- R.style.BriarDialogTheme);
- builder.setMessage(R.string.no_contacts_prompt);
- builder.setPositiveButton(R.string.add_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- listener.contactCreationSelected();
- }
- });
- builder.setNegativeButton(R.string.cancel_button,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- listener.contactCreationCancelled();
- }
- });
- return builder.create();
- }
-
- public interface Listener {
-
- void contactCreationSelected();
-
- void contactCreationCancelled();
- }
-}
diff --git a/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java b/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java
new file mode 100644
index 000000000..93d266c6c
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java
@@ -0,0 +1,36 @@
+package org.briarproject.android.forum;
+
+import org.briarproject.android.contact.ContactListItem;
+import org.briarproject.android.contact.ConversationItem;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collections;
+
+// This class is not thread-safe
+public class SelectableContactListItem extends ContactListItem {
+
+ private boolean selected;
+
+ public SelectableContactListItem(Contact contact, LocalAuthor localAuthor,
+ GroupId groupId, boolean selected) {
+
+ super(contact, localAuthor, false, groupId, Collections.emptyList());
+
+ this.selected = selected;
+ }
+
+ public void setSelected(boolean selected) {
+ this.selected = selected;
+ }
+
+ public boolean isSelected() {
+ return selected;
+ }
+
+ public void toggleSelected() {
+ selected = !selected;
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
index fd8e805f1..e2d4926b3 100644
--- a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java
@@ -2,67 +2,59 @@ package org.briarproject.android.forum;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
-import org.briarproject.android.contact.SelectContactsDialog;
-import org.briarproject.android.invitation.AddContactActivity;
-import org.briarproject.android.util.LayoutUtils;
+import org.briarproject.android.contact.BaseContactListAdapter;
+import org.briarproject.android.contact.ContactListItem;
+import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.ForumSharingManager;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
-import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.forum.ForumActivity.FORUM_NAME;
-import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
-import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP;
-public class ShareForumActivity extends BriarActivity
-implements OnClickListener, NoContactsDialog.Listener,
-SelectContactsDialog.Listener {
+public class ShareForumActivity extends BriarActivity implements
+ BaseContactListAdapter.OnItemClickListener {
private static final Logger LOG =
Logger.getLogger(ShareForumActivity.class.getName());
- private RadioGroup radioGroup = null;
- private RadioButton shareWithAll = null, shareWithSome = null;
- private Button shareButton = null;
- private ProgressBar progress = null;
- private boolean changed = false;
+ private ContactSelectorAdapter adapter;
// Fields that are accessed from background threads must be volatile
+ @Inject protected volatile IdentityManager identityManager;
@Inject protected volatile ContactManager contactManager;
@Inject protected volatile ForumSharingManager forumSharingManager;
- private volatile GroupId groupId = null;
- private volatile Collection contacts = null;
- private volatile Collection selected = null;
+ private volatile GroupId groupId;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
+ setContentView(R.layout.introduction_contact_chooser);
+
Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID);
if (b == null) throw new IllegalStateException();
@@ -71,44 +63,43 @@ SelectContactsDialog.Listener {
if (forumName == null) throw new IllegalStateException();
setTitle(forumName);
- LinearLayout layout = new LinearLayout(this);
- layout.setLayoutParams(MATCH_MATCH);
- layout.setOrientation(VERTICAL);
- layout.setGravity(CENTER_HORIZONTAL);
- int pad = LayoutUtils.getPadding(this);
- layout.setPadding(pad, pad, pad, pad);
+ adapter = new ContactSelectorAdapter(this, this);
+ BriarRecyclerView list =
+ (BriarRecyclerView) findViewById(R.id.contactList);
+ list.setLayoutManager(new LinearLayoutManager(this));
+ list.setAdapter(adapter);
+ list.setEmptyText(getString(R.string.no_contacts));
+ }
- radioGroup = new RadioGroup(this);
- radioGroup.setOrientation(VERTICAL);
- radioGroup.setPadding(0, 0, 0, pad);
+ @Override
+ public void onResume() {
+ super.onResume();
- shareWithAll = new RadioButton(this);
- shareWithAll.setId(2);
- shareWithAll.setText(R.string.forum_share_with_all);
- shareWithAll.setOnClickListener(this);
- radioGroup.addView(shareWithAll);
+ loadContactsAndVisibility();
+ }
- shareWithSome = new RadioButton(this);
- shareWithSome.setId(3);
- shareWithSome.setText(R.string.forum_share_with_some);
- shareWithSome.setOnClickListener(this);
- radioGroup.addView(shareWithSome);
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu items for use in the action bar
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.forum_share_actions, menu);
- layout.addView(radioGroup);
+ return super.onCreateOptionsMenu(menu);
+ }
- shareButton = new Button(this);
- shareButton.setLayoutParams(WRAP_WRAP);
- shareButton.setText(R.string.forum_share_button);
- shareButton.setOnClickListener(this);
- layout.addView(shareButton);
-
- progress = new ProgressBar(this);
- progress.setLayoutParams(WRAP_WRAP);
- progress.setIndeterminate(true);
- progress.setVisibility(GONE);
- layout.addView(progress);
-
- setContentView(layout);
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // Handle presses on the action bar items
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ case R.id.action_share_forum:
+ storeVisibility();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
}
@Override
@@ -116,41 +107,29 @@ SelectContactsDialog.Listener {
component.inject(this);
}
- public void onClick(View view) {
- if (view == shareWithAll) {
- changed = true;
- } else if (view == shareWithSome) {
- changed = true;
- if (contacts == null) loadVisibility();
- else displayVisibility();
- } else if (view == shareButton) {
- if (changed) {
- share();
- } else {
- finish();
- }
- }
- }
-
- private void share() {
- // Replace the button with a progress bar
- shareButton.setVisibility(GONE);
- progress.setVisibility(VISIBLE);
- // Update the group in a background thread
- storeVisibility(shareWithAll.isChecked());
- }
-
- private void loadVisibility() {
+ private void loadContactsAndVisibility() {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
- contacts = contactManager.getActiveContacts();
- selected = forumSharingManager.getSharedWith(groupId);
+ List contacts =
+ new ArrayList();
+ Collection selectedContacts =
+ new HashSet(
+ forumSharingManager.getSharedWith(groupId));
+
+ for (Contact c : contactManager.getActiveContacts()) {
+ LocalAuthor localAuthor = identityManager
+ .getLocalAuthor(c.getLocalAuthorId());
+ boolean selected = selectedContacts.contains(c.getId());
+ contacts.add(
+ new SelectableContactListItem(c, localAuthor,
+ groupId, selected));
+ }
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
- displayVisibility();
+ displayContacts(contacts);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -159,32 +138,22 @@ SelectContactsDialog.Listener {
});
}
- private void displayVisibility() {
+ private void displayContacts(final List contact) {
runOnUiThread(new Runnable() {
public void run() {
- if (contacts.isEmpty()) {
- NoContactsDialog builder = new NoContactsDialog();
- builder.setListener(ShareForumActivity.this);
- builder.build(ShareForumActivity.this).show();
- } else {
- SelectContactsDialog builder = new SelectContactsDialog();
- builder.setListener(ShareForumActivity.this);
- builder.setContacts(contacts);
- builder.setSelected(selected);
- builder.build(ShareForumActivity.this).show();
- }
+ adapter.addAll(contact);
}
});
}
- private void storeVisibility(final boolean all) {
+ private void storeVisibility() {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
- if (all) forumSharingManager.setSharedWithAll(groupId);
- else forumSharingManager.setSharedWith(groupId,
- selected);
+ Collection selected =
+ adapter.getSelectedContactIds();
+ forumSharingManager.setSharedWith(groupId, selected);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Update took " + duration + " ms");
@@ -197,20 +166,10 @@ SelectContactsDialog.Listener {
});
}
- public void contactCreationSelected() {
- startActivity(new Intent(this, AddContactActivity.class));
+ @Override
+ public void onItemClick(View view, ContactListItem item) {
+ ((SelectableContactListItem) item).toggleSelected();
+ adapter.notifyItemChanged(adapter.findItemPosition(item), item);
}
- public void contactCreationCancelled() {
- radioGroup.clearCheck();
- }
-
- public void contactsSelected(Collection selected) {
- this.selected = Collections.unmodifiableCollection(selected);
- share();
- }
-
- public void contactSelectionCancelled() {
- radioGroup.clearCheck();
- }
}
diff --git a/briar-android/src/org/briarproject/android/introduction/ContactChooserAdapter.java b/briar-android/src/org/briarproject/android/introduction/ContactChooserAdapter.java
new file mode 100644
index 000000000..8c96163be
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/introduction/ContactChooserAdapter.java
@@ -0,0 +1,75 @@
+package org.briarproject.android.introduction;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.os.Build;
+import android.view.View;
+
+import org.briarproject.android.contact.ContactListAdapter;
+import org.briarproject.android.contact.ContactListItem;
+import org.briarproject.api.identity.AuthorId;
+
+public class ContactChooserAdapter extends ContactListAdapter {
+
+ private AuthorId localAuthorId;
+
+ public ContactChooserAdapter(Context context,
+ OnItemClickListener listener) {
+
+ super(context, listener);
+ }
+
+ @Override
+ public void onBindViewHolder(final ContactHolder ui, final int position) {
+ super.onBindViewHolder(ui, position);
+
+ final ContactListItem item = getItem(position);
+
+ ui.name.setText(item.getContact().getAuthor().getName());
+
+ ui.identity.setText(item.getLocalAuthor().getName());
+ ui.identity.setVisibility(View.VISIBLE);
+
+ if (!item.getLocalAuthor().getId().equals(localAuthorId)) {
+ grayOutItem(ui);
+ }
+ }
+
+ @Override
+ public int compareContactListItems(ContactListItem c1, ContactListItem c2) {
+ return compareByName(c1, c2);
+ }
+
+ /**
+ * Set the identity from whose perspective the contact shall be chosen.
+ * Contacts that belong to a different author will be shown grayed out,
+ * but are still clickable.
+ * @param authorId The ID of the local Author
+ */
+ public void setLocalAuthor(AuthorId authorId) {
+ localAuthorId = authorId;
+ notifyDataSetChanged();
+ }
+
+ private void grayOutItem(final ContactHolder ui) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ float alpha = 0.25f;
+ ui.bulb.setAlpha(alpha);
+ ui.avatar.setAlpha(alpha);
+ ui.name.setAlpha(alpha);
+ ui.date.setAlpha(alpha);
+ ui.identity.setAlpha(alpha);
+ } else {
+ ColorFilter colorFilter = new PorterDuffColorFilter(Color.GRAY,
+ PorterDuff.Mode.MULTIPLY);
+ ui.bulb.setColorFilter(colorFilter);
+ ui.avatar.setColorFilter(colorFilter);
+ ui.name.setEnabled(false);
+ ui.date.setEnabled(false);
+ }
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
index c381291f2..11e30d635 100644
--- a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java
@@ -47,7 +47,7 @@ public class ContactChooserFragment extends BaseFragment {
public final static String TAG = "ContactChooserFragment";
private IntroductionActivity introductionActivity;
private BriarRecyclerView list;
- private ContactListAdapter adapter;
+ private ContactChooserAdapter adapter;
private int contactId;
private static final Logger LOG =
@@ -111,8 +111,7 @@ public class ContactChooserFragment extends BaseFragment {
}
}
};
- adapter =
- new ContactListAdapter(getActivity(), onItemClickListener, true);
+ adapter = new ContactChooserAdapter(getActivity(), onItemClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
list.setLayoutManager(new LinearLayoutManager(getActivity()));