diff --git a/briar-android/res/layout/activity_available_forums.xml b/briar-android/res/layout/activity_available_forums.xml new file mode 100644 index 0000000000000000000000000000000000000000..7177fe175f97b59be6996a4e1ad6df6983f9d197 --- /dev/null +++ b/briar-android/res/layout/activity_available_forums.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<org.briarproject.android.util.BriarRecyclerView + android:id="@+id/availableForumsView" + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/briar-android/res/layout/list_item_available_forum.xml b/briar-android/res/layout/list_item_available_forum.xml new file mode 100644 index 0000000000000000000000000000000000000000..0129b9d6bfd84809d74ddedcfe219c0cd6149c02 --- /dev/null +++ b/briar-android/res/layout/list_item_available_forum.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="@dimen/listitem_horizontal_margin" + android:layout_marginStart="@dimen/listitem_horizontal_margin" + android:paddingTop="@dimen/listitem_horizontal_margin" + android:background="?attr/selectableItemBackground"> + + <TextView + android:id="@+id/forumNameView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:maxLines="2" + android:textColor="@android:color/primary_text_light" + android:textSize="@dimen/text_size_medium" + tools:text="This is a name of a forum that is available"/> + + <TextView + android:id="@+id/sharedByView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_below="@+id/forumNameView" + android:layout_marginBottom="-8dp" + android:paddingTop="@dimen/margin_medium" + android:textColor="@android:color/secondary_text_light" + android:textSize="@dimen/text_size_small" + tools:text="Shared by Megalox"/> + + <Button + android:id="@+id/acceptButton" + style="@style/BriarButtonFlat.Positive" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dialog_button_accept" + android:layout_below="@+id/sharedByView" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true"/> + + <Button + android:id="@+id/declineButton" + style="@style/BriarButtonFlat.Negative" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/dialog_button_decline" + android:layout_below="@+id/sharedByView" + android:layout_toLeftOf="@+id/acceptButton" + android:layout_toStartOf="@+id/acceptButton"/> + + <View style="@style/Divider.ForumList" + android:layout_below="@+id/acceptButton" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true"/> + +</RelativeLayout> + diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index e5d21dec3eef771234840b9167a10436bda80f47..e0daa614ee482e2e339368664b921378b7bd76c0 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -102,6 +102,7 @@ <string name="forum_post_hint">Type forum post</string> <string name="available_forums_title">Available Forums</string> <string name="forum_joined_toast">Joined Forum</string> + <string name="forum_declined_toast">Forum Invitation Declined</string> <string name="shared_by_format">Shared by %s</string> <string name="no_contacts_prompt">You don\'t have any contacts. Add a contact now?</string> <string name="add_button">Add</string> diff --git a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java index 590c7a2d747c8c5936786a717d83d1d64ae13b78..908afe48307fa1bd9ed3ce80baac447a9be1fe2a 100644 --- a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java @@ -21,14 +21,14 @@ import static android.support.v7.util.SortedList.INVALID_POSITION; public abstract class BaseContactListAdapter<VH extends BaseContactListAdapter.BaseContactHolder> extends RecyclerView.Adapter<VH> { - protected SortedList<ContactListItem> contacts; + protected final SortedList<ContactListItem> 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>(ContactListItem.class, + this.contacts = new SortedList<>(ContactListItem.class, new SortedListCallBacks()); } diff --git a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java index f685e0d0670c8e169aff0a2e89de7b9b37d6db97..dd7a722e7518cdbf206f410a65b8e8e40f278c95 100644 --- a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java +++ b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java @@ -1,34 +1,30 @@ package org.briarproject.android.forum; import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ListView; +import android.support.v7.widget.LinearLayoutManager; import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.AndroidComponent; import org.briarproject.android.BriarActivity; -import org.briarproject.android.util.ListLoadingProgressBar; +import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.contact.Contact; -import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; import org.briarproject.api.db.NoSuchGroupException; import org.briarproject.api.event.ContactRemovedEvent; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.ForumInvitationReceivedEvent; import org.briarproject.api.event.GroupAddedEvent; import org.briarproject.api.event.GroupRemovedEvent; -import org.briarproject.api.event.MessageValidatedEvent; import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumSharingManager; -import org.briarproject.api.sync.ClientId; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.logging.Logger; import javax.inject.Inject; @@ -36,16 +32,15 @@ import javax.inject.Inject; import static android.widget.Toast.LENGTH_SHORT; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; +import static org.briarproject.android.forum.AvailableForumsAdapter.AvailableForumClickListener; public class AvailableForumsActivity extends BriarActivity -implements EventListener, OnItemClickListener { + implements EventListener, AvailableForumClickListener { private static final Logger LOG = Logger.getLogger(AvailableForumsActivity.class.getName()); - private AvailableForumsAdapter adapter = null; - private ListView list = null; + private AvailableForumsAdapter adapter; // Fields that are accessed from background threads must be volatile @Inject protected volatile ForumManager forumManager; @@ -56,15 +51,13 @@ implements EventListener, OnItemClickListener { public void onCreate(Bundle state) { super.onCreate(state); - adapter = new AvailableForumsAdapter(this); - list = new ListView(this); - list.setLayoutParams(MATCH_MATCH); - list.setAdapter(adapter); - list.setOnItemClickListener(this); + setContentView(R.layout.activity_available_forums); - // Show a progress bar while the list is loading - ListLoadingProgressBar loading = new ListLoadingProgressBar(this); - setContentView(loading); + adapter = new AvailableForumsAdapter(this, this); + BriarRecyclerView list = + (BriarRecyclerView) findViewById(R.id.availableForumsView); + list.setLayoutManager(new LinearLayoutManager(this)); + list.setAdapter(adapter); } @Override @@ -113,11 +106,12 @@ implements EventListener, OnItemClickListener { LOG.info("No forums available, finishing"); finish(); } else { - setContentView(list); adapter.clear(); + List<AvailableForumsItem> list = + new ArrayList<>(available.size()); for (ForumContacts f : available) - adapter.add(new AvailableForumsItem(f)); - adapter.sort(AvailableForumsItemComparator.INSTANCE); + list.add(new AvailableForumsItem(f)); + adapter.addAll(list); } } }); @@ -145,37 +139,33 @@ implements EventListener, OnItemClickListener { LOG.info("Forum removed, reloading"); loadForums(); } - } else if (e instanceof MessageValidatedEvent) { - MessageValidatedEvent m = (MessageValidatedEvent) e; - ClientId c = m.getClientId(); - if (m.isValid() && !m.isLocal() - && c.equals(forumSharingManager.getClientId())) { - LOG.info("Available forums updated, reloading"); - loadForums(); - } + } else if (e instanceof ForumInvitationReceivedEvent) { + LOG.info("Available forums updated, reloading"); + loadForums(); } } - public void onItemClick(AdapterView<?> parent, View view, int position, - long id) { - AvailableForumsItem item = adapter.getItem(position); - Collection<ContactId> shared = new ArrayList<>(); - for (Contact c : item.getContacts()) shared.add(c.getId()); - subscribe(item.getForum(), shared); - String joined = getString(R.string.forum_joined_toast); - Toast.makeText(this, joined, LENGTH_SHORT).show(); + public void onItemClick(AvailableForumsItem item, boolean accept) { + respondToInvitation(item.getForum(), accept); + + // show toast + int res = R.string.forum_declined_toast; + if (accept) res = R.string.forum_joined_toast; + Toast.makeText(this, res, LENGTH_SHORT).show(); } - private void subscribe(final Forum f, final Collection<ContactId> shared) { + private void respondToInvitation(final Forum f, final boolean accept) { runOnDbThread(new Runnable() { public void run() { try { - forumManager.addForum(f); + forumSharingManager.respondToInvitation(f, accept); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } + loadForums(); } }); } + } diff --git a/briar-android/src/org/briarproject/android/forum/AvailableForumsAdapter.java b/briar-android/src/org/briarproject/android/forum/AvailableForumsAdapter.java index e5969e3bea3b795970c4fc82daef77734ed2de17..0cd0e818123de7c70ef8c405786a1599d99a51c9 100644 --- a/briar-android/src/org/briarproject/android/forum/AvailableForumsAdapter.java +++ b/briar-android/src/org/briarproject/android/forum/AvailableForumsAdapter.java @@ -1,57 +1,160 @@ package org.briarproject.android.forum; import android.content.Context; +import android.support.v7.util.SortedList; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.LinearLayout; +import android.widget.Button; import android.widget.TextView; import org.briarproject.R; -import org.briarproject.android.util.LayoutUtils; import org.briarproject.api.contact.Contact; import org.briarproject.util.StringUtils; import java.util.ArrayList; import java.util.Collection; -import static android.text.TextUtils.TruncateAt.END; -import static android.widget.LinearLayout.VERTICAL; +class AvailableForumsAdapter extends + RecyclerView.Adapter<AvailableForumsAdapter.AvailableForumViewHolder> { -class AvailableForumsAdapter extends ArrayAdapter<AvailableForumsItem> { + private final Context ctx; + private final AvailableForumClickListener listener; + private final SortedList<AvailableForumsItem> forums = + new SortedList<>(AvailableForumsItem.class, + new SortedListCallBacks()); - private final int pad; + AvailableForumsAdapter(Context ctx, + AvailableForumClickListener listener) { - AvailableForumsAdapter(Context ctx) { - super(ctx, android.R.layout.simple_expandable_list_item_1, - new ArrayList<AvailableForumsItem>()); - pad = LayoutUtils.getPadding(ctx); + this.ctx = ctx; + this.listener = listener; } @Override - public View getView(int position, View convertView, ViewGroup parent) { - AvailableForumsItem item = getItem(position); - Context ctx = getContext(); - - LinearLayout layout = new LinearLayout(ctx); - layout.setOrientation(VERTICAL); - - TextView name = new TextView(ctx); - name.setTextSize(18); - name.setSingleLine(); - name.setEllipsize(END); - name.setPadding(pad, pad, pad, pad); - name.setText(item.getForum().getName()); - layout.addView(name); - - TextView status = new TextView(ctx); - status.setPadding(pad, 0, pad, pad); - Collection<String> names = new ArrayList<String>(); + public AvailableForumViewHolder onCreateViewHolder(ViewGroup parent, + int viewType) { + + View v = LayoutInflater.from(ctx) + .inflate(R.layout.list_item_available_forum, parent, false); + return new AvailableForumViewHolder(v); + } + + @Override + public void onBindViewHolder(AvailableForumViewHolder ui, int position) { + final AvailableForumsItem item = getItem(position); + + ui.name.setText(item.getForum().getName()); + + Collection<String> names = new ArrayList<>(); for (Contact c : item.getContacts()) names.add(c.getAuthor().getName()); - String format = ctx.getString(R.string.shared_by_format); - status.setText(String.format(format, StringUtils.join(names, ", "))); - layout.addView(status); + ui.sharedBy.setText(ctx.getString(R.string.shared_by_format, + StringUtils.join(names, ", "))); + + ui.accept.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.onItemClick(item, true); + } + }); + ui.decline.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + listener.onItemClick(item, false); + } + }); + } + + @Override + public int getItemCount() { + return forums.size(); + } - return layout; + public AvailableForumsItem getItem(int position) { + return forums.get(position); } + + public void add(AvailableForumsItem item) { + forums.add(item); + } + + public void addAll(Collection<AvailableForumsItem> list) { + forums.addAll(list); + } + + public void clear() { + forums.clear(); + } + + protected static class AvailableForumViewHolder + extends RecyclerView.ViewHolder { + + public final ViewGroup layout; + public final TextView name; + public final TextView sharedBy; + public final Button accept; + public final Button decline; + + public AvailableForumViewHolder(View v) { + super(v); + + layout = (ViewGroup) v; + name = (TextView) v.findViewById(R.id.forumNameView); + sharedBy = (TextView) v.findViewById(R.id.sharedByView); + accept = (Button) v.findViewById(R.id.acceptButton); + decline = (Button) v.findViewById(R.id.declineButton); + } + } + + private class SortedListCallBacks + extends SortedList.Callback<AvailableForumsItem> { + + @Override + public int compare(AvailableForumsItem o1, + AvailableForumsItem o2) { + return String.CASE_INSENSITIVE_ORDER + .compare(o1.getForum().getName(), + o2.getForum().getName()); + } + + @Override + public void onInserted(int position, int count) { + notifyItemRangeInserted(position, count); + } + + @Override + public void onRemoved(int position, int count) { + notifyItemRangeRemoved(position, count); + } + + @Override + public void onMoved(int fromPosition, int toPosition) { + notifyItemMoved(fromPosition, toPosition); + } + + @Override + public void onChanged(int position, int count) { + notifyItemRangeChanged(position, count); + } + + @Override + public boolean areContentsTheSame(AvailableForumsItem oldItem, + AvailableForumsItem newItem) { + return oldItem.getForum().equals(newItem.getForum()) && + oldItem.getContacts().equals(newItem.getContacts()); + } + + @Override + public boolean areItemsTheSame(AvailableForumsItem oldItem, + AvailableForumsItem newItem) { + return oldItem.getForum().equals(newItem.getForum()); + } + } + + + interface AvailableForumClickListener { + void onItemClick(AvailableForumsItem item, boolean accept); + } + } diff --git a/briar-android/src/org/briarproject/android/forum/AvailableForumsItemComparator.java b/briar-android/src/org/briarproject/android/forum/AvailableForumsItemComparator.java deleted file mode 100644 index c9098edb00538c785a876f99238690c7165b33e0..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/forum/AvailableForumsItemComparator.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.briarproject.android.forum; - -import java.util.Comparator; - -class AvailableForumsItemComparator implements Comparator<AvailableForumsItem> { - - static final AvailableForumsItemComparator INSTANCE = - new AvailableForumsItemComparator(); - - public int compare(AvailableForumsItem a, AvailableForumsItem b) { - if (a == b) return 0; - String aName = a.getForum().getName(); - String bName = b.getForum().getName(); - return String.CASE_INSENSITIVE_ORDER.compare(aName, bName); - } -}