From e4f5d8e6e7c768b7930b6eac8d85df97d4669729 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Tue, 2 Aug 2016 16:48:27 -0300 Subject: [PATCH] Show Blog Invitations This refactors the forum invitation code, so it can be used by both: forums and blogs. --- briar-android/AndroidManifest.xml | 2 +- ...le_forums.xml => activity_invitations.xml} | 2 +- ...le_forum.xml => list_item_invitations.xml} | 0 briar-android/res/values/strings.xml | 4 + .../android/ActivityComponent.java | 4 +- .../android/contact/ConversationAdapter.java | 16 +- .../forum/ForumInvitationsActivity.java | 188 ----------- .../android/forum/ForumListFragment.java | 7 +- .../sharing/BlogInvitationAdapter.java | 37 +++ .../sharing/ForumInvitationAdapter.java | 31 ++ .../InvitationAdapter.java} | 91 +++--- .../InvitationItem.java} | 18 +- .../android/sharing/InvitationsActivity.java | 308 ++++++++++++++++++ .../android/util/TextAvatarView.java | 9 + 14 files changed, 461 insertions(+), 256 deletions(-) rename briar-android/res/layout/{activity_available_forums.xml => activity_invitations.xml} (85%) rename briar-android/res/layout/{list_item_available_forum.xml => list_item_invitations.xml} (100%) delete mode 100644 briar-android/src/org/briarproject/android/forum/ForumInvitationsActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/BlogInvitationAdapter.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ForumInvitationAdapter.java rename briar-android/src/org/briarproject/android/{forum/ForumInvitationAdapter.java => sharing/InvitationAdapter.java} (55%) rename briar-android/src/org/briarproject/android/{forum/ForumInvitationItem.java => sharing/InvitationItem.java} (53%) create mode 100644 briar-android/src/org/briarproject/android/sharing/InvitationsActivity.java diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 55b48260ee..7b2c1ceacf 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -102,7 +102,7 @@ </activity> <activity - android:name=".android.forum.ForumInvitationsActivity" + android:name=".android.sharing.InvitationsActivity" android:label="@string/forum_invitations_title" android:parentActivityName=".android.NavDrawerActivity"> <meta-data diff --git a/briar-android/res/layout/activity_available_forums.xml b/briar-android/res/layout/activity_invitations.xml similarity index 85% rename from briar-android/res/layout/activity_available_forums.xml rename to briar-android/res/layout/activity_invitations.xml index 7177fe175f..25cd76af43 100644 --- a/briar-android/res/layout/activity_available_forums.xml +++ b/briar-android/res/layout/activity_invitations.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <org.briarproject.android.util.BriarRecyclerView - android:id="@+id/availableForumsView" + android:id="@+id/invitationsView" 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_invitations.xml similarity index 100% rename from briar-android/res/layout/list_item_available_forum.xml rename to briar-android/res/layout/list_item_invitations.xml diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 5a3f510b53..54c7316a6b 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -316,6 +316,10 @@ <string name="blogs_sharing_invitation_received">%1$s has shared the personal blog of %2$s with you.</string> <string name="blogs_sharing_invitation_sent">You have shared the personal blog of %1$s with %2$s.</string> <string name="blogs_sharing_show_invitations">Show Blog Invitations</string> + <string name="blogs_sharing_invitations_title">Blog Invitations</string> + <string name="blogs_sharing_exists">You are subscribed to this blog already.\nWarning: Accepting again might reveal that %s is your contact.</string> + <string name="blogs_sharing_joined_toast">Subscribed to Blog</string> + <string name="blogs_sharing_declined_toast">Blog Invitation Declined</string> <string name="blogs_blog_list">Blog List</string> <string name="blogs_available_blogs">Available Blogs</string> diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 8d4d85225e..81ef8bd57e 100644 --- a/briar-android/src/org/briarproject/android/ActivityComponent.java +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -15,7 +15,7 @@ import org.briarproject.android.blogs.RssFeedManageActivity; import org.briarproject.android.blogs.WriteBlogPostActivity; import org.briarproject.android.contact.ContactListFragment; import org.briarproject.android.contact.ConversationActivity; -import org.briarproject.android.forum.ForumInvitationsActivity; +import org.briarproject.android.sharing.InvitationsActivity; import org.briarproject.android.sharing.ContactSelectorFragment; import org.briarproject.android.forum.CreateForumActivity; import org.briarproject.android.forum.ForumActivity; @@ -63,7 +63,7 @@ public interface ActivityComponent { void inject(CreateIdentityActivity activity); - void inject(ForumInvitationsActivity activity); + void inject(InvitationsActivity activity); void inject(CreateForumActivity activity); diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java index 6741890c5e..353a7e1ce2 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java @@ -13,7 +13,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.briarproject.R; -import org.briarproject.android.forum.ForumInvitationsActivity; +import org.briarproject.android.sharing.InvitationsActivity; import org.briarproject.android.util.AndroidUtils; import org.briarproject.api.blogs.BlogInvitationRequest; import org.briarproject.api.clients.SessionId; @@ -42,6 +42,9 @@ import static org.briarproject.android.contact.ConversationItem.MSG_OUT; import static org.briarproject.android.contact.ConversationItem.NOTICE_IN; import static org.briarproject.android.contact.ConversationItem.NOTICE_OUT; import static org.briarproject.android.contact.ConversationItem.OutgoingItem; +import static org.briarproject.android.sharing.ShareActivity.BLOG; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; class ConversationAdapter extends RecyclerView.Adapter { @@ -286,7 +289,7 @@ class ConversationAdapter extends RecyclerView.Adapter { private void bindInvitation(InvitationHolder ui, final ConversationShareableInvitationItem item) { - InvitationRequest ir = item.getInvitationRequest(); + final InvitationRequest ir = item.getInvitationRequest(); String name = ""; int receivedRes = 0, sentRes = 0, buttonRes = 0; if (ir instanceof ForumInvitationRequest) { @@ -335,15 +338,18 @@ class ConversationAdapter extends RecyclerView.Adapter { ui.text.setText(ctx.getString(receivedRes, contactName, name)); if (ir.isAvailable()) { + final int type = + ir instanceof ForumInvitationRequest ? FORUM : BLOG; ui.showInvitationsButton.setText(ctx.getString(buttonRes)); ui.showInvitationsButton.setVisibility(VISIBLE); ui.showInvitationsButton .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Intent intent = new Intent(ctx, - ForumInvitationsActivity.class); - ctx.startActivity(intent); + Intent i = new Intent(ctx, + InvitationsActivity.class); + i.putExtra(SHAREABLE, type); + ctx.startActivity(i); } }); } else { diff --git a/briar-android/src/org/briarproject/android/forum/ForumInvitationsActivity.java b/briar-android/src/org/briarproject/android/forum/ForumInvitationsActivity.java deleted file mode 100644 index fd3e24196b..0000000000 --- a/briar-android/src/org/briarproject/android/forum/ForumInvitationsActivity.java +++ /dev/null @@ -1,188 +0,0 @@ -package org.briarproject.android.forum; - -import android.os.Bundle; -import android.support.v7.widget.LinearLayoutManager; -import android.widget.Toast; - -import org.briarproject.R; -import org.briarproject.android.ActivityComponent; -import org.briarproject.android.BriarActivity; -import org.briarproject.android.util.BriarRecyclerView; -import org.briarproject.api.contact.Contact; -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.forum.Forum; -import org.briarproject.api.forum.ForumManager; -import org.briarproject.api.forum.ForumSharingManager; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.logging.Logger; - -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.forum.ForumInvitationAdapter.AvailableForumClickListener; - -public class ForumInvitationsActivity extends BriarActivity - implements EventListener, AvailableForumClickListener { - - private static final Logger LOG = - Logger.getLogger(ForumInvitationsActivity.class.getName()); - - private ForumInvitationAdapter adapter; - - // Fields that are accessed from background threads must be volatile - @Inject - protected volatile ForumManager forumManager; - @Inject - protected volatile ForumSharingManager forumSharingManager; - @Inject - protected volatile EventBus eventBus; - - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - - setContentView(R.layout.activity_available_forums); - - adapter = new ForumInvitationAdapter(this, this); - BriarRecyclerView list = - (BriarRecyclerView) findViewById(R.id.availableForumsView); - list.setLayoutManager(new LinearLayoutManager(this)); - list.setAdapter(adapter); - } - - @Override - public void injectActivity(ActivityComponent component) { - component.inject(this); - } - - @Override - public void onResume() { - super.onResume(); - eventBus.addListener(this); - loadForums(false); - } - - @Override - public void onPause() { - super.onPause(); - eventBus.removeListener(this); - adapter.clear(); - } - - private void loadForums(final boolean clear) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - Collection<ForumInvitationItem> forums = new ArrayList<>(); - long now = System.currentTimeMillis(); - for (Forum f : forumSharingManager.getInvited()) { - boolean subscribed; - try { - forumManager.getForum(f.getId()); - subscribed = true; - } catch (NoSuchGroupException e) { - subscribed = false; - } - Collection<Contact> c = - forumSharingManager.getSharedBy(f.getId()); - forums.add( - new ForumInvitationItem(f, subscribed, c)); - } - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Load took " + duration + " ms"); - displayForums(forums, clear); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void displayForums(final Collection<ForumInvitationItem> forums, - final boolean clear) { - runOnUiThread(new Runnable() { - @Override - public void run() { - if (forums.isEmpty()) { - LOG.info("No forums available, finishing"); - finish(); - } else { - if (clear) adapter.clear(); - adapter.addAll(forums); - } - } - }); - } - - @Override - public void eventOccurred(Event e) { - if (e instanceof ContactRemovedEvent) { - LOG.info("Contact removed, reloading"); - loadForums(true); - } else if (e instanceof GroupAddedEvent) { - GroupAddedEvent g = (GroupAddedEvent) e; - if (g.getGroup().getClientId().equals(forumManager.getClientId())) { - LOG.info("Forum added, reloading"); - loadForums(false); - } - } else if (e instanceof GroupRemovedEvent) { - GroupRemovedEvent g = (GroupRemovedEvent) e; - if (g.getGroup().getClientId().equals(forumManager.getClientId())) { - LOG.info("Forum removed, reloading"); - loadForums(true); - } - } else if (e instanceof ForumInvitationReceivedEvent) { - LOG.info("Available forums updated, reloading"); - loadForums(false); - } - } - - @Override - public void onItemClick(ForumInvitationItem item, boolean accept) { - respondToInvitation(item, 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(); - - // remove item and finish if it was the last - adapter.remove(item); - if (adapter.getItemCount() == 0) { - supportFinishAfterTransition(); - } - } - - private void respondToInvitation(final ForumInvitationItem item, - final boolean accept) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - Forum f = item.getForum(); - for (Contact c : item.getContacts()) { - forumSharingManager.respondToInvitation(f, c, accept); - } - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } -} diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java index cca381a80f..26721db36d 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java @@ -16,6 +16,7 @@ import android.view.ViewGroup; import org.briarproject.R; import org.briarproject.android.ActivityComponent; import org.briarproject.android.fragment.BaseEventFragment; +import org.briarproject.android.sharing.InvitationsActivity; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.db.DbException; import org.briarproject.api.db.NoSuchGroupException; @@ -40,6 +41,8 @@ import javax.inject.Inject; import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; public class ForumListFragment extends BaseEventFragment implements View.OnClickListener { @@ -285,6 +288,8 @@ public class ForumListFragment extends BaseEventFragment implements @Override public void onClick(View view) { // snackbar click - startActivity(new Intent(getContext(), ForumInvitationsActivity.class)); + Intent i = new Intent(getContext(), InvitationsActivity.class); + i.putExtra(SHAREABLE, FORUM); + startActivity(i); } } diff --git a/briar-android/src/org/briarproject/android/sharing/BlogInvitationAdapter.java b/briar-android/src/org/briarproject/android/sharing/BlogInvitationAdapter.java new file mode 100644 index 0000000000..c6127143e6 --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/BlogInvitationAdapter.java @@ -0,0 +1,37 @@ +package org.briarproject.android.sharing; + +import android.content.Context; + +import org.briarproject.R; +import org.briarproject.api.blogs.Blog; + +class BlogInvitationAdapter extends InvitationAdapter { + + BlogInvitationAdapter(Context ctx, AvailableForumClickListener listener) { + super(ctx, listener); + } + + @Override + public void onBindViewHolder(InvitationsViewHolder ui, int position) { + super.onBindViewHolder(ui, position); + InvitationItem item = getItem(position); + Blog blog = (Blog) item.getShareable(); + + ui.avatar.setAuthorAvatar(blog.getAuthor()); + + ui.name.setText(ctx.getString(R.string.blogs_personal_blog, + blog.getAuthor().getName())); + + if (item.isSubscribed()) { + ui.subscribed.setText(ctx.getString(R.string.blogs_sharing_exists, + blog.getAuthor().getName())); + } + } + + int compareInvitations(InvitationItem o1, InvitationItem o2) { + return String.CASE_INSENSITIVE_ORDER + .compare(((Blog) o1.getShareable()).getAuthor().getName(), + ((Blog) o2.getShareable()).getAuthor().getName()); + } + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ForumInvitationAdapter.java b/briar-android/src/org/briarproject/android/sharing/ForumInvitationAdapter.java new file mode 100644 index 0000000000..a0e898715e --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/ForumInvitationAdapter.java @@ -0,0 +1,31 @@ +package org.briarproject.android.sharing; + +import android.content.Context; + +import org.briarproject.api.forum.Forum; + +class ForumInvitationAdapter extends InvitationAdapter { + + ForumInvitationAdapter(Context ctx, AvailableForumClickListener listener) { + super(ctx, listener); + } + + @Override + public void onBindViewHolder(InvitationsViewHolder ui, int position) { + super.onBindViewHolder(ui, position); + InvitationItem item = getItem(position); + Forum forum = (Forum) item.getShareable(); + + ui.avatar.setText(forum.getName().substring(0, 1)); + ui.avatar.setBackgroundBytes(item.getShareable().getId().getBytes()); + + ui.name.setText(forum.getName()); + } + + int compareInvitations(InvitationItem o1, InvitationItem o2) { + return String.CASE_INSENSITIVE_ORDER + .compare(((Forum) o1.getShareable()).getName(), + ((Forum) o2.getShareable()).getName()); + } + +} diff --git a/briar-android/src/org/briarproject/android/forum/ForumInvitationAdapter.java b/briar-android/src/org/briarproject/android/sharing/InvitationAdapter.java similarity index 55% rename from briar-android/src/org/briarproject/android/forum/ForumInvitationAdapter.java rename to briar-android/src/org/briarproject/android/sharing/InvitationAdapter.java index 28ef9e2f1c..22a5f3e023 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumInvitationAdapter.java +++ b/briar-android/src/org/briarproject/android/sharing/InvitationAdapter.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Context; import android.support.v7.util.SortedList; @@ -12,7 +12,6 @@ import android.widget.TextView; import org.briarproject.R; import org.briarproject.android.util.TextAvatarView; import org.briarproject.api.contact.Contact; -import org.briarproject.api.forum.ForumSharingMessage; import org.briarproject.util.StringUtils; import java.util.ArrayList; @@ -21,37 +20,32 @@ import java.util.Collection; import static android.view.View.GONE; import static android.view.View.VISIBLE; -class ForumInvitationAdapter extends - RecyclerView.Adapter<ForumInvitationAdapter.AvailableForumViewHolder> { +abstract class InvitationAdapter extends + RecyclerView.Adapter<InvitationAdapter.InvitationsViewHolder> { - private final Context ctx; + protected final Context ctx; private final AvailableForumClickListener listener; - private final SortedList<ForumInvitationItem> forums = - new SortedList<>(ForumInvitationItem.class, + private final SortedList<InvitationItem> invitations = + new SortedList<>(InvitationItem.class, new SortedListCallBacks()); - ForumInvitationAdapter(Context ctx, AvailableForumClickListener listener) { + InvitationAdapter(Context ctx, AvailableForumClickListener listener) { this.ctx = ctx; this.listener = listener; } @Override - public AvailableForumViewHolder onCreateViewHolder(ViewGroup parent, + public InvitationsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(ctx) - .inflate(R.layout.list_item_available_forum, parent, false); - return new AvailableForumViewHolder(v); + .inflate(R.layout.list_item_invitations, parent, false); + return new InvitationsViewHolder(v); } @Override - public void onBindViewHolder(AvailableForumViewHolder ui, int position) { - final ForumInvitationItem item = getItem(position); - - ui.avatar.setText(item.getForum().getName().substring(0, 1)); - ui.avatar.setBackgroundBytes(item.getForum().getId().getBytes()); - - ui.name.setText(item.getForum().getName()); + public void onBindViewHolder(InvitationsViewHolder ui, int position) { + final InvitationItem item = getItem(position); Collection<String> names = new ArrayList<>(); for (Contact c : item.getContacts()) names.add(c.getAuthor().getName()); @@ -80,40 +74,39 @@ class ForumInvitationAdapter extends @Override public int getItemCount() { - return forums.size(); + return invitations.size(); } - public ForumInvitationItem getItem(int position) { - return forums.get(position); + public InvitationItem getItem(int position) { + return invitations.get(position); } - public void add(ForumInvitationItem item) { - forums.add(item); + public void add(InvitationItem item) { + invitations.add(item); } - public void addAll(Collection<ForumInvitationItem> list) { - forums.addAll(list); + public void addAll(Collection<InvitationItem> list) { + invitations.addAll(list); } - public void remove(ForumInvitationItem item) { - forums.remove(item); + public void remove(InvitationItem item) { + invitations.remove(item); } public void clear() { - forums.clear(); + invitations.clear(); } - static class AvailableForumViewHolder - extends RecyclerView.ViewHolder { + static class InvitationsViewHolder extends RecyclerView.ViewHolder { - private final TextAvatarView avatar; - private final TextView name; - private final TextView sharedBy; - private final TextView subscribed; - private final Button accept; - private final Button decline; + final TextAvatarView avatar; + final TextView name; + final TextView sharedBy; + final TextView subscribed; + final Button accept; + final Button decline; - AvailableForumViewHolder(View v) { + InvitationsViewHolder(View v) { super(v); avatar = (TextAvatarView) v.findViewById(R.id.avatarView); @@ -125,15 +118,15 @@ class ForumInvitationAdapter extends } } + abstract int compareInvitations(InvitationItem o1, InvitationItem o2); + private class SortedListCallBacks - extends SortedList.Callback<ForumInvitationItem> { + extends SortedList.Callback<InvitationItem> { @Override - public int compare(ForumInvitationItem o1, - ForumInvitationItem o2) { - return String.CASE_INSENSITIVE_ORDER - .compare(o1.getForum().getName(), - o2.getForum().getName()); + public int compare(InvitationItem o1, + InvitationItem o2) { + return compareInvitations(o1, o2); } @Override @@ -157,21 +150,21 @@ class ForumInvitationAdapter extends } @Override - public boolean areContentsTheSame(ForumInvitationItem oldItem, - ForumInvitationItem newItem) { + public boolean areContentsTheSame(InvitationItem oldItem, + InvitationItem newItem) { return oldItem.isSubscribed() == newItem.isSubscribed() && oldItem.getContacts().equals(newItem.getContacts()); } @Override - public boolean areItemsTheSame(ForumInvitationItem oldItem, - ForumInvitationItem newItem) { - return oldItem.getForum().equals(newItem.getForum()); + public boolean areItemsTheSame(InvitationItem oldItem, + InvitationItem newItem) { + return oldItem.getShareable().equals(newItem.getShareable()); } } interface AvailableForumClickListener { - void onItemClick(ForumInvitationItem item, boolean accept); + void onItemClick(InvitationItem item, boolean accept); } } diff --git a/briar-android/src/org/briarproject/android/forum/ForumInvitationItem.java b/briar-android/src/org/briarproject/android/sharing/InvitationItem.java similarity index 53% rename from briar-android/src/org/briarproject/android/forum/ForumInvitationItem.java rename to briar-android/src/org/briarproject/android/sharing/InvitationItem.java index 9db87abe73..65dccd4ffd 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumInvitationItem.java +++ b/briar-android/src/org/briarproject/android/sharing/InvitationItem.java @@ -1,29 +1,29 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import org.briarproject.api.contact.Contact; -import org.briarproject.api.forum.Forum; +import org.briarproject.api.sharing.Shareable; import java.util.Collection; -class ForumInvitationItem { +class InvitationItem { - private final Forum forum; + private final Shareable shareable; private final boolean subscribed; private final Collection<Contact> contacts; - ForumInvitationItem(Forum forum, boolean subscribed, + InvitationItem(Shareable shareable, boolean subscribed, Collection<Contact> contacts) { - this.forum = forum; + this.shareable = shareable; this.subscribed = subscribed; this.contacts = contacts; } - Forum getForum() { - return forum; + Shareable getShareable() { + return shareable; } - public boolean isSubscribed() { + boolean isSubscribed() { return subscribed; } diff --git a/briar-android/src/org/briarproject/android/sharing/InvitationsActivity.java b/briar-android/src/org/briarproject/android/sharing/InvitationsActivity.java new file mode 100644 index 0000000000..614f54cbf3 --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/InvitationsActivity.java @@ -0,0 +1,308 @@ +package org.briarproject.android.sharing; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.widget.Toast; + +import org.briarproject.R; +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.BriarActivity; +import org.briarproject.android.util.BriarRecyclerView; +import org.briarproject.api.blogs.Blog; +import org.briarproject.api.blogs.BlogManager; +import org.briarproject.api.blogs.BlogSharingManager; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.NoSuchGroupException; +import org.briarproject.api.event.BlogInvitationReceivedEvent; +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.InvitationReceivedEvent; +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.logging.Logger; + +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.sharing.InvitationAdapter.AvailableForumClickListener; +import static org.briarproject.android.sharing.ShareActivity.BLOG; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; + +public class InvitationsActivity extends BriarActivity + implements EventListener, AvailableForumClickListener { + + private static final Logger LOG = + Logger.getLogger(InvitationsActivity.class.getName()); + + private int shareable; + private InvitationAdapter adapter; + + // Fields that are accessed from background threads must be volatile + @Inject + protected volatile ForumManager forumManager; + @Inject + protected volatile ForumSharingManager forumSharingManager; + @Inject + protected volatile BlogManager blogManager; + @Inject + protected volatile BlogSharingManager blogSharingManager; + @Inject + protected volatile EventBus eventBus; + + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + + setContentView(R.layout.activity_invitations); + + Intent i = getIntent(); + shareable = i.getIntExtra(SHAREABLE, 0); + if (shareable == 0) throw new IllegalStateException("No Shareable"); + + if (shareable == FORUM) { + adapter = new ForumInvitationAdapter(this, this); + } else if (shareable == BLOG) { + adapter = new BlogInvitationAdapter(this, this); + setTitle(getString(R.string.blogs_sharing_invitations_title)); + } else { + throw new IllegalArgumentException("Unknown Shareable Type"); + } + + BriarRecyclerView list = + (BriarRecyclerView) findViewById(R.id.invitationsView); + if (list != null) { + list.setLayoutManager(new LinearLayoutManager(this)); + list.setAdapter(adapter); + } + } + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onResume() { + super.onResume(); + eventBus.addListener(this); + loadShareables(false); + } + + @Override + public void onPause() { + super.onPause(); + eventBus.removeListener(this); + adapter.clear(); + } + + private void loadShareables(boolean clear) { + if (shareable == FORUM) { + loadForums(clear); + } else if (shareable == BLOG) { + loadBlogs(clear); + } + } + + private void loadForums(final boolean clear) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection<InvitationItem> forums = new ArrayList<>(); + long now = System.currentTimeMillis(); + for (Forum f : forumSharingManager.getInvited()) { + boolean subscribed; + try { + forumManager.getForum(f.getId()); + subscribed = true; + } catch (NoSuchGroupException e) { + subscribed = false; + } + Collection<Contact> c = + forumSharingManager.getSharedBy(f.getId()); + forums.add( + new InvitationItem(f, subscribed, c)); + } + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Load took " + duration + " ms"); + displayInvitations(forums, clear); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + + private void loadBlogs(final boolean clear) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection<InvitationItem> invitations = new ArrayList<>(); + long now = System.currentTimeMillis(); + for (Blog b : blogSharingManager.getInvited()) { + boolean subscribed; + try { + blogManager.getBlog(b.getId()); + subscribed = true; + } catch (NoSuchGroupException e) { + subscribed = false; + } + Collection<Contact> c = + blogSharingManager.getSharedBy(b.getId()); + invitations.add( + new InvitationItem(b, subscribed, c)); + } + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Load took " + duration + " ms"); + displayInvitations(invitations, clear); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + + private void displayInvitations(final Collection<InvitationItem> invitations, + final boolean clear) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (invitations.isEmpty()) { + LOG.info("No more invitations available, finishing"); + finish(); + } else { + if (clear) adapter.clear(); + adapter.addAll(invitations); + } + } + }); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof ContactRemovedEvent) { + LOG.info("Contact removed, reloading"); + loadShareables(true); + } else if (e instanceof GroupAddedEvent) { + GroupAddedEvent g = (GroupAddedEvent) e; + ClientId cId = g.getGroup().getClientId(); + if (cId.equals(forumManager.getClientId()) && shareable == FORUM) { + LOG.info("Forum added, reloading"); + loadShareables(false); + } else if (cId.equals(blogManager.getClientId()) && + shareable == BLOG) { + LOG.info("Blog added, reloading"); + loadShareables(true); + } + } else if (e instanceof GroupRemovedEvent) { + GroupRemovedEvent g = (GroupRemovedEvent) e; + ClientId cId = g.getGroup().getClientId(); + if (cId.equals(forumManager.getClientId()) && shareable == FORUM) { + LOG.info("Forum removed, reloading"); + loadShareables(true); + } else if (cId.equals(blogManager.getClientId()) && + shareable == BLOG) { + LOG.info("Blog removed, reloading"); + loadShareables(true); + } + } else if (e instanceof InvitationReceivedEvent) { + if (e instanceof ForumInvitationReceivedEvent && + shareable == FORUM) { + LOG.info("Forum invitation received, reloading"); + loadShareables(false); + } else if (e instanceof BlogInvitationReceivedEvent && + shareable == BLOG) { + LOG.info("Blog invitation received, reloading"); + loadShareables(false); + } + } + } + + @Override + public void onItemClick(InvitationItem item, boolean accept) { + respondToInvitation(item, accept); + + // show toast + int res; + if (shareable == FORUM) { + res = R.string.forum_declined_toast; + if (accept) res = R.string.forum_joined_toast; + } else { + res = R.string.blogs_sharing_declined_toast; + if (accept) res = R.string.blogs_sharing_joined_toast; + } + Toast.makeText(this, res, LENGTH_SHORT).show(); + + // remove item and finish if it was the last + adapter.remove(item); + if (adapter.getItemCount() == 0) { + supportFinishAfterTransition(); + } + } + + private void respondToInvitation(final InvitationItem item, + final boolean accept) { + + if (shareable == FORUM) { + respondToForumInvitation(item, accept); + } else if (shareable == BLOG) { + respondToBlogInvitation(item, accept); + } + } + + private void respondToForumInvitation(final InvitationItem item, + final boolean accept) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Forum f = (Forum) item.getShareable(); + for (Contact c : item.getContacts()) { + forumSharingManager.respondToInvitation(f, c, accept); + } + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + + private void respondToBlogInvitation(final InvitationItem item, + final boolean accept) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Blog b = (Blog) item.getShareable(); + for (Contact c : item.getContacts()) { + blogSharingManager.respondToInvitation(b, c, accept); + } + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } +} diff --git a/briar-android/src/org/briarproject/android/util/TextAvatarView.java b/briar-android/src/org/briarproject/android/util/TextAvatarView.java index 6033021eb2..b48d0e7ad1 100644 --- a/briar-android/src/org/briarproject/android/util/TextAvatarView.java +++ b/briar-android/src/org/briarproject/android/util/TextAvatarView.java @@ -2,6 +2,7 @@ package org.briarproject.android.util; import android.content.Context; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.support.v4.content.ContextCompat; import android.support.v7.widget.AppCompatTextView; import android.util.AttributeSet; @@ -10,8 +11,10 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.briarproject.R; +import org.briarproject.api.identity.Author; import de.hdodenhof.circleimageview.CircleImageView; +import im.delight.android.identicons.IdenticonDrawable; public class TextAvatarView extends FrameLayout { @@ -83,4 +86,10 @@ public class TextAvatarView extends FrameLayout { } } + public void setAuthorAvatar(Author author) { + Drawable drawable = new IdenticonDrawable(author.getId().getBytes()); + background.setImageDrawable(drawable); + character.setVisibility(GONE); + } + } -- GitLab