From 2f7d188a070db5d1247c4ff27841ac932ed6179b Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 1 Aug 2016 20:13:12 -0300 Subject: [PATCH 1/6] UI for Sharing Blogs This commit refactors the code for sharing forums, so it can be used for sharing blogs as well. It does not yet include code for responding to blog invitations. --- briar-android/AndroidManifest.xml | 4 +- .../res/layout/activity_share_forum.xml | 2 +- ...message.xml => fragment_share_message.xml} | 0 briar-android/res/menu/blogs_blog_actions.xml | 17 ++++ briar-android/res/values/strings.xml | 5 ++ .../android/ActivityComponent.java | 14 +-- .../android/AndroidComponent.java | 3 + .../android/blogs/BlogActivity.java | 2 + .../android/blogs/BlogFragment.java | 48 +++++++++- .../android/forum/ForumActivity.java | 9 +- .../ContactSelectorAdapter.java | 2 +- .../ContactSelectorFragment.java | 44 ++++++--- .../SelectableContactListItem.java | 2 +- .../ShareActivity.java} | 34 ++++--- .../ShareMessageFragment.java} | 90 ++++++++++++------- .../SharingStatusActivity.java} | 10 +-- .../SharingStatusAdapter.java} | 10 +-- .../sharing/BlogSharingManagerImpl.java | 11 +++ .../briarproject/sharing/SharingModule.java | 2 + 19 files changed, 220 insertions(+), 89 deletions(-) rename briar-android/res/layout/{share_forum_message.xml => fragment_share_message.xml} (100%) create mode 100644 briar-android/res/menu/blogs_blog_actions.xml rename briar-android/src/org/briarproject/android/{forum => sharing}/ContactSelectorAdapter.java (98%) rename briar-android/src/org/briarproject/android/{forum => sharing}/ContactSelectorFragment.java (83%) rename briar-android/src/org/briarproject/android/{forum => sharing}/SelectableContactListItem.java (95%) rename briar-android/src/org/briarproject/android/{forum/ShareForumActivity.java => sharing/ShareActivity.java} (73%) rename briar-android/src/org/briarproject/android/{forum/ShareForumMessageFragment.java => sharing/ShareMessageFragment.java} (61%) rename briar-android/src/org/briarproject/android/{forum/ForumSharingStatusActivity.java => sharing/SharingStatusActivity.java} (93%) rename briar-android/src/org/briarproject/android/{forum/ForumSharingStatusAdapter.java => sharing/SharingStatusAdapter.java} (72%) diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 0249275b5..55b48260e 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -133,7 +133,7 @@ \ No newline at end of file diff --git a/briar-android/res/layout/share_forum_message.xml b/briar-android/res/layout/fragment_share_message.xml similarity index 100% rename from briar-android/res/layout/share_forum_message.xml rename to briar-android/res/layout/fragment_share_message.xml diff --git a/briar-android/res/menu/blogs_blog_actions.xml b/briar-android/res/menu/blogs_blog_actions.xml new file mode 100644 index 000000000..ef84cfb20 --- /dev/null +++ b/briar-android/res/menu/blogs_blog_actions.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 0c9ce8a08..164dea6ac 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -113,6 +113,7 @@ Share Forum Forum shared with chosen contacts You may compose an optional invitation message that will be sent to the selected contacts. + There was an error sharing this forum. %1$s has shared the forum \"%2$s\" with you. You have shared the forum \"%1$s\" with %2$s. Show Forum Invitations @@ -304,6 +305,10 @@ Keep Blog Deleted Remove Blog + Share Blog + There was an error sharing this blog. + Share Blog + Blog shared with chosen contacts Blog List Available Blogs diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 7d902e6b4..8d4d85225 100644 --- a/briar-android/src/org/briarproject/android/ActivityComponent.java +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -16,13 +16,13 @@ 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.forum.ContactSelectorFragment; +import org.briarproject.android.sharing.ContactSelectorFragment; import org.briarproject.android.forum.CreateForumActivity; import org.briarproject.android.forum.ForumActivity; import org.briarproject.android.forum.ForumListFragment; -import org.briarproject.android.forum.ForumSharingStatusActivity; -import org.briarproject.android.forum.ShareForumActivity; -import org.briarproject.android.forum.ShareForumMessageFragment; +import org.briarproject.android.sharing.SharingStatusActivity; +import org.briarproject.android.sharing.ShareActivity; +import org.briarproject.android.sharing.ShareMessageFragment; import org.briarproject.android.identity.CreateIdentityActivity; import org.briarproject.android.introduction.ContactChooserFragment; import org.briarproject.android.introduction.IntroductionActivity; @@ -67,9 +67,9 @@ public interface ActivityComponent { void inject(CreateForumActivity activity); - void inject(ShareForumActivity activity); + void inject(ShareActivity activity); - void inject(ForumSharingStatusActivity activity); + void inject(SharingStatusActivity activity); void inject(ForumActivity activity); @@ -104,7 +104,7 @@ public interface ActivityComponent { void inject(ShowQrCodeFragment fragment); void inject(ContactChooserFragment fragment); void inject(ContactSelectorFragment fragment); - void inject(ShareForumMessageFragment fragment); + void inject(ShareMessageFragment fragment); void inject(IntroductionMessageFragment fragment); } diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java index dbe923c95..a5880d336 100644 --- a/briar-android/src/org/briarproject/android/AndroidComponent.java +++ b/briar-android/src/org/briarproject/android/AndroidComponent.java @@ -8,6 +8,7 @@ import org.briarproject.android.api.ReferenceManager; import org.briarproject.android.report.BriarReportSender; import org.briarproject.api.blogs.BlogManager; import org.briarproject.api.blogs.BlogPostFactory; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.contact.ContactExchangeTask; import org.briarproject.api.contact.ContactManager; import org.briarproject.api.crypto.CryptoComponent; @@ -93,6 +94,8 @@ public interface AndroidComponent extends CoreEagerSingletons { ForumSharingManager forumSharingManager(); + BlogSharingManager blogSharingManager(); + ForumPostFactory forumPostFactory(); BlogManager blogManager(); diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java index ceb80f61a..866b191d9 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java @@ -32,6 +32,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener, OnBlogPostClickListener, BaseFragmentListener { static final int REQUEST_WRITE_POST = 1; + static final int REQUEST_SHARE = 2; static final String BLOG_NAME = "briar.BLOG_NAME"; static final String IS_MY_BLOG = "briar.IS_MY_BLOG"; static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG"; @@ -185,6 +186,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener, @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); // The BlogPostAddedEvent arrives when the controller is not listening, // so we need to manually reload the blog posts :( diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java index ec3762b58..a6e46b3fe 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java @@ -23,6 +23,8 @@ import org.briarproject.android.blogs.BlogController.BlogPostListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.controller.handler.UiResultHandler; import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.android.sharing.ShareActivity; +import org.briarproject.android.sharing.SharingStatusActivity; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.sync.GroupId; @@ -30,6 +32,9 @@ import java.util.Collection; import javax.inject.Inject; +import static android.app.Activity.RESULT_OK; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; +import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import static android.support.design.widget.Snackbar.LENGTH_LONG; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.widget.Toast.LENGTH_SHORT; @@ -37,7 +42,10 @@ import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME; import static org.briarproject.android.blogs.BlogActivity.IS_MY_BLOG; import static org.briarproject.android.blogs.BlogActivity.IS_NEW_BLOG; +import static org.briarproject.android.blogs.BlogActivity.REQUEST_SHARE; import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST; +import static org.briarproject.android.sharing.ShareActivity.BLOG; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; public class BlogFragment extends BaseFragment implements BlogPostListener { @@ -136,12 +144,18 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (myBlog) { inflater.inflate(R.menu.blogs_my_blog_actions, menu); + } else { + inflater.inflate(R.menu.blogs_blog_actions, menu); } super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(final MenuItem item) { + ActivityOptionsCompat options = + makeCustomAnimation(getActivity(), + android.R.anim.slide_in_left, + android.R.anim.slide_out_right); switch (item.getItemId()) { case android.R.id.home: getActivity().onBackPressed(); @@ -151,18 +165,37 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { new Intent(getActivity(), WriteBlogPostActivity.class); i.putExtra(GROUP_ID, groupId.getBytes()); i.putExtra(BLOG_NAME, blogName); - ActivityOptionsCompat options = - makeCustomAnimation(getActivity(), - android.R.anim.slide_in_left, - android.R.anim.slide_out_right); ActivityCompat.startActivityForResult(getActivity(), i, REQUEST_WRITE_POST, options.toBundle()); return true; + case R.id.action_blog_share: + Intent i2 = new Intent(getActivity(), ShareActivity.class); + i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); + i2.putExtra(GROUP_ID, groupId.getBytes()); + i2.putExtra(SHAREABLE, BLOG); + startActivityForResult(i2, REQUEST_SHARE, options.toBundle()); + return true; + case R.id.action_blog_sharing_status: + Intent i3 = + new Intent(getActivity(), SharingStatusActivity.class); + i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); + i3.putExtra(GROUP_ID, groupId.getBytes()); + startActivity(i3, options.toBundle()); + return true; default: return super.onOptionsItemSelected(item); } } + @Override + public void onActivityResult(int request, int result, Intent data) { + super.onActivityResult(request, result, data); + + if (request == REQUEST_SHARE && result == RESULT_OK) { + displaySnackbar(R.string.blogs_sharing_snackbar); + } + } + @Override public String getUniqueTag() { return TAG; @@ -202,6 +235,13 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { loadData(true); } + private void displaySnackbar(int stringId) { + Snackbar snackbar = + Snackbar.make(list, stringId, Snackbar.LENGTH_SHORT); + snackbar.getView().setBackgroundResource(R.color.briar_primary); + snackbar.show(); + } + private void showDeleteDialog() { DialogInterface.OnClickListener okListener = new DialogInterface.OnClickListener() { diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java index 02a78cacc..383992a8e 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java @@ -33,6 +33,8 @@ import org.briarproject.android.ActivityComponent; import org.briarproject.android.BriarActivity; import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.controller.handler.UiResultHandler; +import org.briarproject.android.sharing.ShareActivity; +import org.briarproject.android.sharing.SharingStatusActivity; import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.android.util.TrustIndicatorView; @@ -57,6 +59,8 @@ import static android.view.View.GONE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_SHORT; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; public class ForumActivity extends BriarActivity implements ForumController.ForumPostListener { @@ -220,15 +224,16 @@ public class ForumActivity extends BriarActivity implements showTextInput(null); return true; case R.id.action_forum_share: - Intent i2 = new Intent(this, ShareForumActivity.class); + Intent i2 = new Intent(this, ShareActivity.class); i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); i2.putExtra(GROUP_ID, groupId.getBytes()); + i2.putExtra(SHAREABLE, FORUM); ActivityCompat .startActivityForResult(this, i2, REQUEST_FORUM_SHARED, options.toBundle()); return true; case R.id.action_forum_sharing_status: - Intent i3 = new Intent(this, ForumSharingStatusActivity.class); + Intent i3 = new Intent(this, SharingStatusActivity.class); i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); i3.putExtra(GROUP_ID, groupId.getBytes()); ActivityCompat.startActivity(this, i3, options.toBundle()); diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java similarity index 98% rename from briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java rename to briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java index 9a2fe4590..d940c48dc 100644 --- a/briar-android/src/org/briarproject/android/forum/ContactSelectorAdapter.java +++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Context; import android.graphics.Color; diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java similarity index 83% rename from briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java rename to briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java index 99f1f41c6..6ace473db 100644 --- a/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java +++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Context; import android.os.Build; @@ -18,6 +18,7 @@ import org.briarproject.android.contact.BaseContactListAdapter; import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.util.BriarRecyclerView; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactManager; @@ -37,8 +38,11 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -import static org.briarproject.android.forum.ShareForumActivity.CONTACTS; -import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds; +import static org.briarproject.android.sharing.ShareActivity.BLOG; +import static org.briarproject.android.sharing.ShareActivity.CONTACTS; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; +import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; public class ContactSelectorFragment extends BaseFragment implements @@ -49,7 +53,7 @@ public class ContactSelectorFragment extends BaseFragment implements private static final Logger LOG = Logger.getLogger(ContactSelectorFragment.class.getName()); - private ShareForumActivity shareForumActivity; + private ShareActivity shareActivity; private Menu menu; private BriarRecyclerView list; private ContactSelectorAdapter adapter; @@ -62,13 +66,18 @@ public class ContactSelectorFragment extends BaseFragment implements protected volatile IdentityManager identityManager; @Inject protected volatile ForumSharingManager forumSharingManager; + @Inject + volatile BlogSharingManager blogSharingManager; - protected volatile GroupId groupId; + private volatile GroupId groupId; + private volatile int shareable; - public static ContactSelectorFragment newInstance(GroupId groupId) { + public static ContactSelectorFragment newInstance(int shareable, + GroupId groupId) { Bundle args = new Bundle(); args.putByteArray(GROUP_ID, groupId.getBytes()); + args.putInt(SHAREABLE, shareable); ContactSelectorFragment fragment = new ContactSelectorFragment(); fragment.setArguments(args); return fragment; @@ -83,7 +92,7 @@ public class ContactSelectorFragment extends BaseFragment implements public void onAttach(Context context) { super.onAttach(context); try { - shareForumActivity = (ShareForumActivity) context; + shareActivity = (ShareActivity) context; } catch (ClassCastException e) { throw new InstantiationError( "This fragment is only meant to be attached to the ShareForumActivity"); @@ -97,6 +106,7 @@ public class ContactSelectorFragment extends BaseFragment implements setHasOptionsMenu(true); groupId = new GroupId(getArguments().getByteArray(GROUP_ID)); if (groupId == null) throw new IllegalStateException("No GroupId"); + shareable = getArguments().getInt(SHAREABLE); } @Override @@ -121,7 +131,7 @@ public class ContactSelectorFragment extends BaseFragment implements if (savedInstanceState != null) { ArrayList intContacts = savedInstanceState.getIntegerArrayList(CONTACTS); - selectedContacts = ShareForumActivity.getContactsFromIntegers( + selectedContacts = ShareActivity.getContactsFromIntegers( intContacts); } @@ -160,11 +170,11 @@ public class ContactSelectorFragment extends BaseFragment implements // Handle presses on the action bar items switch (item.getItemId()) { case android.R.id.home: - shareForumActivity.onBackPressed(); + shareActivity.onBackPressed(); return true; case R.id.action_share_forum: selectedContacts = adapter.getSelectedContactIds(); - shareForumActivity.showMessageScreen(groupId, selectedContacts); + shareActivity.showMessageScreen(groupId, selectedContacts); return true; default: return super.onOptionsItemSelected(item); @@ -185,7 +195,7 @@ public class ContactSelectorFragment extends BaseFragment implements } private void loadContacts(final Collection selection) { - shareForumActivity.runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { @Override public void run() { try { @@ -199,8 +209,14 @@ public class ContactSelectorFragment extends BaseFragment implements boolean selected = selection != null && selection.contains(c.getId()); // do we have already some sharing with that contact? - boolean disabled = - !forumSharingManager.canBeShared(groupId, c); + boolean disabled = true; + if (shareable == FORUM) { + disabled = !forumSharingManager + .canBeShared(groupId, c); + } else if (shareable == BLOG) { + disabled = !blogSharingManager + .canBeShared(groupId, c); + } contacts.add(new SelectableContactListItem(c, localAuthor, groupId, selected, disabled)); } @@ -218,7 +234,7 @@ public class ContactSelectorFragment extends BaseFragment implements } private void displayContacts(final List contacts) { - shareForumActivity.runOnUiThread(new Runnable() { + shareActivity.runOnUiThread(new Runnable() { @Override public void run() { if (!contacts.isEmpty()) adapter.addAll(contacts); diff --git a/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java b/briar-android/src/org/briarproject/android/sharing/SelectableContactListItem.java similarity index 95% rename from briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java rename to briar-android/src/org/briarproject/android/sharing/SelectableContactListItem.java index fa5ad2284..0d713fda2 100644 --- a/briar-android/src/org/briarproject/android/forum/SelectableContactListItem.java +++ b/briar-android/src/org/briarproject/android/sharing/SelectableContactListItem.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.contact.ConversationItem; diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java similarity index 73% rename from briar-android/src/org/briarproject/android/forum/ShareForumActivity.java rename to briar-android/src/org/briarproject/android/sharing/ShareActivity.java index d789e854f..be0a6e8db 100644 --- a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Intent; import android.os.Bundle; @@ -15,11 +15,16 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -// TODO extend the BriarFragmentActivity ? -public class ShareForumActivity extends BriarActivity implements +public class ShareActivity extends BriarActivity implements BaseFragment.BaseFragmentListener { - public final static String CONTACTS = "contacts"; + public final static String SHAREABLE = "shareable"; + public final static int FORUM = 1; + public final static int BLOG = 2; + + final static String CONTACTS = "contacts"; + + private int shareable; @Override public void onCreate(Bundle savedInstanceState) { @@ -32,11 +37,14 @@ public class ShareForumActivity extends BriarActivity implements if (b == null) throw new IllegalStateException("No GroupId"); GroupId groupId = new GroupId(b); + shareable = i.getIntExtra(SHAREABLE, 0); + if (shareable == 0) throw new IllegalStateException("No Shareable"); + if (savedInstanceState == null) { ContactSelectorFragment contactSelectorFragment = - ContactSelectorFragment.newInstance(groupId); + ContactSelectorFragment.newInstance(shareable, groupId); getSupportFragmentManager().beginTransaction() - .add(R.id.shareForumContainer, contactSelectorFragment) + .add(R.id.shareContainer, contactSelectorFragment) .commit(); } } @@ -46,24 +54,24 @@ public class ShareForumActivity extends BriarActivity implements component.inject(this); } - public void showMessageScreen(GroupId groupId, + void showMessageScreen(GroupId groupId, Collection contacts) { - ShareForumMessageFragment messageFragment = - ShareForumMessageFragment.newInstance(groupId, contacts); + ShareMessageFragment messageFragment = + ShareMessageFragment.newInstance(shareable, groupId, contacts); getSupportFragmentManager().beginTransaction() .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.slide_in_left, android.R.anim.slide_out_right) - .replace(R.id.shareForumContainer, messageFragment, + .replace(R.id.shareContainer, messageFragment, ContactSelectorFragment.TAG) .addToBackStack(null) .commit(); } - public static ArrayList getContactsFromIds( + static ArrayList getContactsFromIds( Collection contacts) { // transform ContactIds to Integers so they can be added to a bundle @@ -74,13 +82,13 @@ public class ShareForumActivity extends BriarActivity implements return intContacts; } - public void sharingSuccessful(View v) { + void sharingSuccessful(View v) { setResult(RESULT_OK); hideSoftKeyboard(v); supportFinishAfterTransition(); } - protected static Collection getContactsFromIntegers( + static Collection getContactsFromIntegers( ArrayList intContacts) { // turn contact integers from a bundle back to ContactIds diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java similarity index 61% rename from briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java rename to briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java index e8682a136..9e9fcac39 100644 --- a/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Context; import android.os.Bundle; @@ -14,6 +14,8 @@ import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.ActivityComponent; import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.api.blogs.BlogManager; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; import org.briarproject.api.forum.ForumSharingManager; @@ -27,31 +29,40 @@ import javax.inject.Inject; import static android.widget.Toast.LENGTH_SHORT; import static java.util.logging.Level.WARNING; -import static org.briarproject.android.forum.ShareForumActivity.CONTACTS; -import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds; +import static org.briarproject.android.sharing.ShareActivity.BLOG; +import static org.briarproject.android.sharing.ShareActivity.CONTACTS; +import static org.briarproject.android.sharing.ShareActivity.FORUM; +import static org.briarproject.android.sharing.ShareActivity.SHAREABLE; +import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; -public class ShareForumMessageFragment extends BaseFragment { +public class ShareMessageFragment extends BaseFragment { public final static String TAG = "IntroductionMessageFragment"; private static final Logger LOG = - Logger.getLogger(ShareForumMessageFragment.class.getName()); + Logger.getLogger(ShareMessageFragment.class.getName()); - private ShareForumActivity shareForumActivity; + private ShareActivity shareActivity; private ViewHolder ui; // Fields that are accessed from background threads must be volatile @Inject protected volatile ForumSharingManager forumSharingManager; + @Inject + protected volatile BlogSharingManager blogSharingManager; private volatile GroupId groupId; + private volatile int shareable; private volatile Collection contacts; - public static ShareForumMessageFragment newInstance(GroupId groupId, Collection contacts) { + public static ShareMessageFragment newInstance(int shareable, + GroupId groupId, Collection contacts) { + Bundle args = new Bundle(); args.putByteArray(GROUP_ID, groupId.getBytes()); + args.putInt(SHAREABLE, shareable); args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts)); - ShareForumMessageFragment fragment = new ShareForumMessageFragment(); + ShareMessageFragment fragment = new ShareMessageFragment(); fragment.setArguments(args); return fragment; } @@ -60,7 +71,7 @@ public class ShareForumMessageFragment extends BaseFragment { public void onAttach(Context context) { super.onAttach(context); try { - shareForumActivity = (ShareForumActivity) context; + shareActivity = (ShareActivity) context; } catch (ClassCastException e) { throw new InstantiationError( "This fragment is only meant to be attached to the ShareForumActivity"); @@ -71,17 +82,31 @@ public class ShareForumMessageFragment extends BaseFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - // change toolbar text - ActionBar actionBar = shareForumActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setTitle(R.string.forum_share_button); - } - // allow for home button to act as back button setHasOptionsMenu(true); + // get groupID, shareable type and contactIDs from fragment arguments + groupId = new GroupId(getArguments().getByteArray(GROUP_ID)); + shareable = getArguments().getInt(SHAREABLE); + ArrayList intContacts = + getArguments().getIntegerArrayList(CONTACTS); + if (intContacts == null) throw new IllegalArgumentException(); + contacts = ShareActivity.getContactsFromIntegers(intContacts); + + // change toolbar text + ActionBar actionBar = shareActivity.getSupportActionBar(); + if (actionBar != null) { + if (shareable == FORUM) { + actionBar.setTitle(R.string.forum_share_button); + } else if (shareable == BLOG) { + actionBar.setTitle(R.string.blogs_sharing_button); + } else { + throw new IllegalArgumentException("Invalid Shareable Type!"); + } + } + // inflate view - View v = inflater.inflate(R.layout.share_forum_message, container, + View v = inflater.inflate(R.layout.fragment_share_message, container, false); ui = new ViewHolder(v); ui.button.setOnClickListener(new View.OnClickListener() { @@ -90,13 +115,9 @@ public class ShareForumMessageFragment extends BaseFragment { onButtonClick(); } }); - - // get groupID and contactIDs from fragment arguments - groupId = new GroupId(getArguments().getByteArray(GROUP_ID)); - ArrayList intContacts = - getArguments().getIntegerArrayList(CONTACTS); - if (intContacts == null) throw new IllegalArgumentException(); - contacts = ShareForumActivity.getContactsFromIntegers(intContacts); + if (shareable == BLOG) { + ui.button.setText(getString(R.string.blogs_sharing_button)); + } return v; } @@ -105,7 +126,7 @@ public class ShareForumMessageFragment extends BaseFragment { public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - shareForumActivity.onBackPressed(); + shareActivity.onBackPressed(); return true; default: return super.onOptionsItemSelected(item); @@ -122,7 +143,7 @@ public class ShareForumMessageFragment extends BaseFragment { component.inject(this); } - public void onButtonClick() { + private void onButtonClick() { // disable button to prevent accidental double invitations ui.button.setEnabled(false); @@ -130,17 +151,21 @@ public class ShareForumMessageFragment extends BaseFragment { shareForum(msg); // don't wait for the introduction to be made before finishing activity - shareForumActivity.sharingSuccessful(ui.message); + shareActivity.sharingSuccessful(ui.message); } private void shareForum(final String msg) { - shareForumActivity.runOnDbThread(new Runnable() { + listener.runOnDbThread(new Runnable() { @Override public void run() { try { for (ContactId c : contacts) { - forumSharingManager.sendInvitation(groupId, c, - msg); + if (shareable == FORUM) { + forumSharingManager.sendInvitation(groupId, c, + msg); + } else if (shareable == BLOG) { + blogSharingManager.sendInvitation(groupId, c, msg); + } } } catch (DbException e) { sharingError(); @@ -152,11 +177,12 @@ public class ShareForumMessageFragment extends BaseFragment { } private void sharingError() { - shareForumActivity.runOnUiThread(new Runnable() { + shareActivity.runOnUiThread(new Runnable() { @Override public void run() { - Toast.makeText(shareForumActivity, - R.string.introduction_error, LENGTH_SHORT).show(); + int res = R.string.forum_share_error; + if (shareable == BLOG) res = R.string.blogs_sharing_error; + Toast.makeText(shareActivity, res, LENGTH_SHORT).show(); } }); } diff --git a/briar-android/src/org/briarproject/android/forum/ForumSharingStatusActivity.java b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java similarity index 93% rename from briar-android/src/org/briarproject/android/forum/ForumSharingStatusActivity.java rename to briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java index 5da3d212c..b9c51cbe6 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumSharingStatusActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java @@ -1,4 +1,4 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Intent; import android.os.Bundle; @@ -26,11 +26,11 @@ import javax.inject.Inject; import static java.util.logging.Level.WARNING; -public class ForumSharingStatusActivity extends BriarActivity { +public class SharingStatusActivity extends BriarActivity { private GroupId groupId; private BriarRecyclerView sharedByList, sharedWithList; - private ForumSharingStatusAdapter sharedByAdapter, sharedWithAdapter; + private SharingStatusAdapter sharedByAdapter, sharedWithAdapter; // Fields that are accessed from background threads must be volatile @Inject @@ -53,13 +53,13 @@ public class ForumSharingStatusActivity extends BriarActivity { groupId = new GroupId(b); sharedByList = (BriarRecyclerView) findViewById(R.id.sharedByView); - sharedByAdapter = new ForumSharingStatusAdapter(this); + sharedByAdapter = new SharingStatusAdapter(this); sharedByList.setLayoutManager(new LinearLayoutManager(this)); sharedByList.setAdapter(sharedByAdapter); sharedByList.setEmptyText(getString(R.string.nobody)); sharedWithList = (BriarRecyclerView) findViewById(R.id.sharedWithView); - sharedWithAdapter = new ForumSharingStatusAdapter(this); + sharedWithAdapter = new SharingStatusAdapter(this); sharedWithList.setLayoutManager(new LinearLayoutManager(this)); sharedWithList.setAdapter(sharedWithAdapter); sharedWithList.setEmptyText(getString(R.string.nobody)); diff --git a/briar-android/src/org/briarproject/android/forum/ForumSharingStatusAdapter.java b/briar-android/src/org/briarproject/android/sharing/SharingStatusAdapter.java similarity index 72% rename from briar-android/src/org/briarproject/android/forum/ForumSharingStatusAdapter.java rename to briar-android/src/org/briarproject/android/sharing/SharingStatusAdapter.java index 3d52480de..18f70fe78 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumSharingStatusAdapter.java +++ b/briar-android/src/org/briarproject/android/sharing/SharingStatusAdapter.java @@ -1,22 +1,18 @@ -package org.briarproject.android.forum; +package org.briarproject.android.sharing; import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; import org.briarproject.R; import org.briarproject.android.contact.BaseContactListAdapter; import org.briarproject.android.contact.ContactListItem; -public class ForumSharingStatusAdapter +class SharingStatusAdapter extends BaseContactListAdapter { - public ForumSharingStatusAdapter(Context context) { + SharingStatusAdapter(Context context) { super(context, null); } diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java index 5632ea994..550667641 100644 --- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java @@ -12,6 +12,7 @@ import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.MessageQueueManager; import org.briarproject.api.clients.PrivateGroupFactory; import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfList; @@ -47,6 +48,8 @@ class BlogSharingManagerImpl extends "bee438b5de0b3a685badc4e49d76e72d" + "21e01c4b569a775112756bdae267a028")); + private final BlogManager blogManager; + private final SFactory sFactory; private final IFactory iFactory; private final ISFactory isFactory; @@ -64,6 +67,7 @@ class BlogSharingManagerImpl extends super(db, messageQueueManager, clientHelper, metadataParser, metadataEncoder, random, privateGroupFactory, clock); + this.blogManager = blogManager; sFactory = new SFactory(authorFactory, blogFactory, blogManager); iFactory = new IFactory(); isFactory = new ISFactory(); @@ -77,6 +81,13 @@ class BlogSharingManagerImpl extends return CLIENT_ID; } + @Override + public boolean canBeShared(GroupId g, Contact c) throws DbException { + Blog b = blogManager.getPersonalBlog(c.getAuthor()); + if (b.getId().equals(g)) return false; + return super.canBeShared(g, c); + } + @Override protected BlogInvitationRequest createInvitationRequest(MessageId id, BlogInvitation msg, ContactId contactId, boolean available, diff --git a/briar-core/src/org/briarproject/sharing/SharingModule.java b/briar-core/src/org/briarproject/sharing/SharingModule.java index 3af07e129..a098fa1e1 100644 --- a/briar-core/src/org/briarproject/sharing/SharingModule.java +++ b/briar-core/src/org/briarproject/sharing/SharingModule.java @@ -27,6 +27,8 @@ public class SharingModule { ForumSharingValidator forumSharingValidator; @Inject ForumSharingManager forumSharingManager; + @Inject + BlogSharingManager blogSharingManager; } @Provides From a552d1b6a69c417d81381c620b0791930ec90b14 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 2 Aug 2016 11:38:01 -0300 Subject: [PATCH 2/6] Show blog invitation requests and responses in private conversation --- .../layout/list_item_forum_invitation_out.xml | 57 --------- ... => list_item_shareable_invitation_in.xml} | 0 briar-android/res/values/strings.xml | 7 ++ .../AndroidNotificationManagerImpl.java | 10 +- .../android/contact/ContactListFragment.java | 20 ++-- .../android/contact/ConversationActivity.java | 41 ++++--- .../android/contact/ConversationAdapter.java | 110 +++++++++++------- .../ConversationForumInvitationInItem.java | 32 ----- .../ConversationForumInvitationItem.java | 18 --- .../android/contact/ConversationItem.java | 56 ++++++++- ...ConversationShareableInvitationInItem.java | 43 +++++++ .../ConversationShareableInvitationItem.java | 18 +++ ...nversationShareableInvitationOutItem.java} | 26 +++-- .../api/blogs/BlogInvitationRequest.java | 10 +- .../api/blogs/BlogInvitationResponse.java | 17 +++ .../api/blogs/BlogSharingManager.java | 6 +- .../api/forum/ForumSharingManager.java | 2 +- .../api/sharing/SharingManager.java | 4 +- .../sharing/BlogSharingManagerImpl.java | 16 +-- .../sharing/ForumSharingManagerImpl.java | 2 +- .../sharing/SharingManagerImpl.java | 21 ++-- 21 files changed, 298 insertions(+), 218 deletions(-) delete mode 100644 briar-android/res/layout/list_item_forum_invitation_out.xml rename briar-android/res/layout/{list_item_forum_invitation_in.xml => list_item_shareable_invitation_in.xml} (100%) delete mode 100644 briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java delete mode 100644 briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java create mode 100644 briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java create mode 100644 briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java rename briar-android/src/org/briarproject/android/contact/{ConversationForumInvitationOutItem.java => ConversationShareableInvitationOutItem.java} (52%) create mode 100644 briar-api/src/org/briarproject/api/blogs/BlogInvitationResponse.java diff --git a/briar-android/res/layout/list_item_forum_invitation_out.xml b/briar-android/res/layout/list_item_forum_invitation_out.xml deleted file mode 100644 index 9784a1391..000000000 --- a/briar-android/res/layout/list_item_forum_invitation_out.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/briar-android/res/layout/list_item_forum_invitation_in.xml b/briar-android/res/layout/list_item_shareable_invitation_in.xml similarity index 100% rename from briar-android/res/layout/list_item_forum_invitation_in.xml rename to briar-android/res/layout/list_item_shareable_invitation_in.xml diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 164dea6ac..5a3f510b5 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -309,6 +309,13 @@ There was an error sharing this blog. Share Blog Blog shared with chosen contacts + You accepted the blog invitation from %s. + You declined the blog invitation from %s. + %s accepted the blog invitation. + %s declined the blog invitation. + %1$s has shared the personal blog of %2$s with you. + You have shared the personal blog of %1$s with %2$s. + Show Blog Invitations Blog List Available Blogs diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index f8874fd71..dc53745b9 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -21,11 +21,12 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.event.BlogPostAddedEvent; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventListener; -import org.briarproject.api.event.ForumInvitationReceivedEvent; import org.briarproject.api.event.ForumPostReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent; import org.briarproject.api.event.IntroductionSucceededEvent; +import org.briarproject.api.event.InvitationReceivedEvent; +import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.lifecycle.Service; @@ -174,8 +175,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager, } else if (e instanceof IntroductionSucceededEvent) { Contact c = ((IntroductionSucceededEvent) e).getContact(); showIntroductionSucceededNotification(c); - } else if (e instanceof ForumInvitationReceivedEvent) { - ContactId c = ((ForumInvitationReceivedEvent) e).getContactId(); + } else if (e instanceof InvitationReceivedEvent) { + ContactId c = ((InvitationReceivedEvent) e).getContactId(); + showNotificationForPrivateConversation(c); + } else if (e instanceof InvitationResponseReceivedEvent) { + ContactId c = ((InvitationResponseReceivedEvent) e).getContactId(); showNotificationForPrivateConversation(c); } } diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java index 582097c25..c9df74031 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java @@ -20,6 +20,7 @@ import org.briarproject.android.ActivityComponent; import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.keyagreement.KeyAgreementActivity; import org.briarproject.android.util.BriarRecyclerView; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactManager; @@ -37,6 +38,8 @@ import org.briarproject.api.event.ForumInvitationReceivedEvent; import org.briarproject.api.event.ForumInvitationResponseReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent; +import org.briarproject.api.event.InvitationReceivedEvent; +import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent; import org.briarproject.api.forum.ForumInvitationRequest; import org.briarproject.api.forum.ForumInvitationResponse; @@ -91,6 +94,8 @@ public class ContactListFragment extends BaseFragment implements EventListener { protected volatile IntroductionManager introductionManager; @Inject protected volatile ForumSharingManager forumSharingManager; + @Inject + protected volatile BlogSharingManager blogSharingManager; public static ContactListFragment newInstance() { @@ -277,14 +282,14 @@ public class ContactListFragment extends BaseFragment implements EventListener { (IntroductionResponseReceivedEvent) e; IntroductionResponse ir = m.getIntroductionResponse(); updateItem(m.getContactId(), ConversationItem.from(ir)); - } else if (e instanceof ForumInvitationReceivedEvent) { - LOG.info("Forum Invitation received, reloading conversation..."); - ForumInvitationReceivedEvent m = (ForumInvitationReceivedEvent) e; + } else if (e instanceof InvitationReceivedEvent) { + LOG.info("Invitation received, reloading conversation..."); + InvitationReceivedEvent m = (InvitationReceivedEvent) e; reloadConversation(m.getContactId()); - } else if (e instanceof ForumInvitationResponseReceivedEvent) { - LOG.info("Forum Invitation Response received, reloading ..."); - ForumInvitationResponseReceivedEvent m = - (ForumInvitationResponseReceivedEvent) e; + } else if (e instanceof InvitationResponseReceivedEvent) { + LOG.info("Invitation Response received, reloading ..."); + InvitationResponseReceivedEvent m = + (InvitationResponseReceivedEvent) e; reloadConversation(m.getContactId()); } } @@ -406,6 +411,7 @@ public class ContactListFragment extends BaseFragment implements EventListener { now = System.currentTimeMillis(); Collection invitations = forumSharingManager.getInvitationMessages(id); + invitations.addAll(blogSharingManager.getInvitationMessages(id)); for (InvitationMessage i : invitations) { messages.add(ConversationItem.from(i)); } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index 58b2084d9..b1cd0292d 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -30,6 +30,7 @@ import org.briarproject.android.api.AndroidNotificationManager; import org.briarproject.android.introduction.IntroductionActivity; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.FormatException; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.clients.SessionId; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; @@ -44,15 +45,13 @@ 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.ForumInvitationResponseReceivedEvent; import org.briarproject.api.event.IntroductionRequestReceivedEvent; import org.briarproject.api.event.IntroductionResponseReceivedEvent; +import org.briarproject.api.event.InvitationReceivedEvent; +import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.event.MessagesAckedEvent; import org.briarproject.api.event.MessagesSentEvent; import org.briarproject.api.event.PrivateMessageReceivedEvent; -import org.briarproject.api.forum.ForumInvitationRequest; -import org.briarproject.api.forum.ForumInvitationResponse; import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.introduction.IntroductionManager; import org.briarproject.api.introduction.IntroductionMessage; @@ -64,6 +63,8 @@ import org.briarproject.api.messaging.PrivateMessageFactory; import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.sharing.InvitationMessage; +import org.briarproject.api.sharing.InvitationRequest; +import org.briarproject.api.sharing.InvitationResponse; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; import org.briarproject.util.StringUtils; @@ -128,6 +129,8 @@ public class ConversationActivity extends BriarActivity protected volatile IntroductionManager introductionManager; @Inject protected volatile ForumSharingManager forumSharingManager; + @Inject + protected volatile BlogSharingManager blogSharingManager; private volatile GroupId groupId = null; private volatile ContactId contactId = null; @@ -337,6 +340,8 @@ public class ConversationActivity extends BriarActivity Collection invitations = forumSharingManager .getInvitationMessages(contactId); + invitations.addAll(blogSharingManager + .getInvitationMessages(contactId)); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) LOG.info("Loading headers took " + duration + " ms"); @@ -388,13 +393,13 @@ public class ConversationActivity extends BriarActivity items.add(item); } for (InvitationMessage i : invitations) { - if (i instanceof ForumInvitationRequest) { - ForumInvitationRequest r = - (ForumInvitationRequest) i; + if (i instanceof InvitationRequest) { + InvitationRequest r = + (InvitationRequest) i; items.add(ConversationItem.from(r)); - } else if (i instanceof ForumInvitationResponse) { - ForumInvitationResponse r = - (ForumInvitationResponse) i; + } else if (i instanceof InvitationResponse) { + InvitationResponse r = + (InvitationResponse) i; items.add(ConversationItem .from(ConversationActivity.this, contactName, r)); @@ -541,6 +546,7 @@ public class ConversationActivity extends BriarActivity IntroductionRequestReceivedEvent event = (IntroductionRequestReceivedEvent) e; if (event.getContactId().equals(contactId)) { + LOG.info("Introduction request received, adding..."); IntroductionRequest ir = event.getIntroductionRequest(); ConversationItem item = new ConversationIntroductionInItem(ir); addConversationItem(item); @@ -549,21 +555,24 @@ public class ConversationActivity extends BriarActivity IntroductionResponseReceivedEvent event = (IntroductionResponseReceivedEvent) e; if (event.getContactId().equals(contactId)) { + LOG.info("Introduction response received, adding..."); IntroductionResponse ir = event.getIntroductionResponse(); ConversationItem item = ConversationItem.from(this, contactName, ir); addConversationItem(item); } - } else if (e instanceof ForumInvitationReceivedEvent) { - ForumInvitationReceivedEvent event = - (ForumInvitationReceivedEvent) e; + } else if (e instanceof InvitationReceivedEvent) { + InvitationReceivedEvent event = + (InvitationReceivedEvent) e; if (event.getContactId().equals(contactId)) { + LOG.info("Invitation received, reloading..."); loadMessages(); } - } else if (e instanceof ForumInvitationResponseReceivedEvent) { - ForumInvitationResponseReceivedEvent event = - (ForumInvitationResponseReceivedEvent) e; + } else if (e instanceof InvitationResponseReceivedEvent) { + InvitationResponseReceivedEvent event = + (InvitationResponseReceivedEvent) e; if (event.getContactId().equals(contactId)) { + LOG.info("Invitation response received, reloading..."); loadMessages(); } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java index 15b905908..6741890c5 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationAdapter.java @@ -15,16 +15,22 @@ import android.widget.TextView; import org.briarproject.R; import org.briarproject.android.forum.ForumInvitationsActivity; import org.briarproject.android.util.AndroidUtils; +import org.briarproject.api.blogs.BlogInvitationRequest; import org.briarproject.api.clients.SessionId; import org.briarproject.api.forum.ForumInvitationRequest; import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.messaging.PrivateMessageHeader; +import org.briarproject.api.sharing.InvitationRequest; import org.briarproject.util.StringUtils; import java.util.List; import static android.support.v7.util.SortedList.INVALID_POSITION; import static android.support.v7.widget.RecyclerView.ViewHolder; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_IN; +import static org.briarproject.android.contact.ConversationItem.BLOG_INVITATION_OUT; import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_IN; import static org.briarproject.android.contact.ConversationItem.FORUM_INVITATION_OUT; import static org.briarproject.android.contact.ConversationItem.INTRODUCTION_IN; @@ -46,13 +52,13 @@ class ConversationAdapter extends RecyclerView.Adapter { private IntroductionHandler intro; private String contactName; - public ConversationAdapter(Context context, + ConversationAdapter(Context context, IntroductionHandler introductionHandler) { ctx = context; intro = introductionHandler; } - public void setContactName(String contactName) { + void setContactName(String contactName) { this.contactName = contactName; notifyDataSetChanged(); } @@ -87,13 +93,15 @@ class ConversationAdapter extends RecyclerView.Adapter { v = LayoutInflater.from(viewGroup.getContext()).inflate( R.layout.list_item_notice_out, viewGroup, false); return new NoticeHolder(v, type); - } else if (type == FORUM_INVITATION_IN) { + } else if (type == FORUM_INVITATION_IN || type == BLOG_INVITATION_IN) { v = LayoutInflater.from(viewGroup.getContext()).inflate( - R.layout.list_item_forum_invitation_in, viewGroup, false); + R.layout.list_item_shareable_invitation_in, viewGroup, + false); return new InvitationHolder(v, type); - } else if (type == FORUM_INVITATION_OUT) { + } else if (type == FORUM_INVITATION_OUT || + type == BLOG_INVITATION_OUT) { v = LayoutInflater.from(viewGroup.getContext()).inflate( - R.layout.list_item_forum_invitation_out, viewGroup, false); + R.layout.list_item_introduction_out, viewGroup, false); return new InvitationHolder(v, type); } // incoming message (non-local) @@ -119,12 +127,12 @@ class ConversationAdapter extends RecyclerView.Adapter { bindNotice((NoticeHolder) ui, (ConversationNoticeOutItem) item); } else if (item instanceof ConversationNoticeInItem) { bindNotice((NoticeHolder) ui, (ConversationNoticeInItem) item); - } else if (item instanceof ConversationForumInvitationOutItem) { + } else if (item instanceof ConversationShareableInvitationOutItem) { bindInvitation((InvitationHolder) ui, - (ConversationForumInvitationOutItem) item); - } else if (item instanceof ConversationForumInvitationInItem) { + (ConversationShareableInvitationOutItem) item); + } else if (item instanceof ConversationShareableInvitationInItem) { bindInvitation((InvitationHolder) ui, - (ConversationForumInvitationInItem) item); + (ConversationShareableInvitationInItem) item); } else { throw new IllegalArgumentException("Unhandled Conversation Item"); } @@ -180,9 +188,9 @@ class ConversationAdapter extends RecyclerView.Adapter { String message = ir.getMessage(); if (StringUtils.isNullOrEmpty(message)) { - ui.messageLayout.setVisibility(View.GONE); + ui.messageLayout.setVisibility(GONE); } else { - ui.messageLayout.setVisibility(View.VISIBLE); + ui.messageLayout.setVisibility(VISIBLE); ui.message.body.setText(StringUtils.trim(message)); ui.message.date .setText(AndroidUtils.formatDate(ctx, item.getTime())); @@ -213,8 +221,8 @@ class ConversationAdapter extends RecyclerView.Adapter { ui.text.setText(ctx.getString( R.string.introduction_request_answered_received, contactName, ir.getName())); - ui.acceptButton.setVisibility(View.GONE); - ui.declineButton.setVisibility(View.GONE); + ui.acceptButton.setVisibility(GONE); + ui.declineButton.setVisibility(GONE); } // Incoming Introduction Request (Not Answered) else { @@ -230,12 +238,12 @@ class ConversationAdapter extends RecyclerView.Adapter { if (item.getIntroductionRequest().doesIntroduceOtherIdentity()) { // don't allow accept when one of our identities is introduced - ui.acceptButton.setVisibility(View.GONE); + ui.acceptButton.setVisibility(GONE); ui.text.setText(ctx.getString( R.string.introduction_request_for_our_identity_received, contactName, ir.getName())); } else { - ui.acceptButton.setVisibility(View.VISIBLE); + ui.acceptButton.setVisibility(VISIBLE); ui.acceptButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -245,7 +253,7 @@ class ConversationAdapter extends RecyclerView.Adapter { } }); } - ui.declineButton.setVisibility(View.VISIBLE); + ui.declineButton.setVisibility(VISIBLE); ui.declineButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -276,26 +284,38 @@ class ConversationAdapter extends RecyclerView.Adapter { } private void bindInvitation(InvitationHolder ui, - final ConversationForumInvitationItem item) { + final ConversationShareableInvitationItem item) { - ForumInvitationRequest fim = item.getForumInvitationMessage(); + InvitationRequest ir = item.getInvitationRequest(); + String name = ""; + int receivedRes = 0, sentRes = 0, buttonRes = 0; + if (ir instanceof ForumInvitationRequest) { + name = ((ForumInvitationRequest) ir).getForumName(); + receivedRes = R.string.forum_invitation_received; + sentRes = R.string.forum_invitation_sent; + buttonRes = R.string.forum_show_invitations; + } else if (ir instanceof BlogInvitationRequest) { + name = ((BlogInvitationRequest) ir).getBlogAuthorName(); + receivedRes = R.string.blogs_sharing_invitation_received; + sentRes = R.string.blogs_sharing_invitation_sent; + buttonRes = R.string.blogs_sharing_show_invitations; + } - String message = fim.getMessage(); + String message = ir.getMessage(); if (StringUtils.isNullOrEmpty(message)) { - ui.messageLayout.setVisibility(View.GONE); + ui.messageLayout.setVisibility(GONE); } else { - ui.messageLayout.setVisibility(View.VISIBLE); + ui.messageLayout.setVisibility(VISIBLE); ui.message.body.setText(StringUtils.trim(message)); ui.message.date .setText(AndroidUtils.formatDate(ctx, item.getTime())); } // Outgoing Invitation - if (item instanceof ConversationForumInvitationOutItem) { - ui.text.setText(ctx.getString(R.string.forum_invitation_sent, - fim.getForumName(), contactName)); - ConversationForumInvitationOutItem i = - (ConversationForumInvitationOutItem) item; + if (item instanceof ConversationShareableInvitationOutItem) { + ui.text.setText(ctx.getString(sentRes, name, contactName)); + ConversationShareableInvitationOutItem i = + (ConversationShareableInvitationOutItem) item; if (i.isSeen()) { ui.status.setImageResource(R.drawable.message_delivered); ui.message.status.setImageResource( @@ -312,12 +332,12 @@ class ConversationAdapter extends RecyclerView.Adapter { } // Incoming Invitation else { - ui.text.setText(ctx.getString(R.string.forum_invitation_received, - contactName, fim.getForumName())); + ui.text.setText(ctx.getString(receivedRes, contactName, name)); - if (fim.isAvailable()) { - ui.showForumsButton.setVisibility(View.VISIBLE); - ui.showForumsButton + if (ir.isAvailable()) { + ui.showInvitationsButton.setText(ctx.getString(buttonRes)); + ui.showInvitationsButton.setVisibility(VISIBLE); + ui.showInvitationsButton .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -327,7 +347,7 @@ class ConversationAdapter extends RecyclerView.Adapter { } }); } else { - ui.showForumsButton.setVisibility(View.GONE); + ui.showInvitationsButton.setVisibility(GONE); } } ui.date.setText(AndroidUtils.formatDate(ctx, item.getTime())); @@ -345,7 +365,7 @@ class ConversationAdapter extends RecyclerView.Adapter { return items.get(position); } - public ConversationItem getLastItem() { + ConversationItem getLastItem() { if (items.size() > 0) { return items.get(items.size() - 1); } else { @@ -353,7 +373,7 @@ class ConversationAdapter extends RecyclerView.Adapter { } } - public SparseArray getIncomingMessages() { + SparseArray getIncomingMessages() { SparseArray messages = new SparseArray<>(); for (int i = 0; i < items.size(); i++) { @@ -365,7 +385,7 @@ class ConversationAdapter extends RecyclerView.Adapter { return messages; } - public SparseArray getOutgoingMessages() { + SparseArray getOutgoingMessages() { SparseArray messages = new SparseArray<>(); for (int i = 0; i < items.size(); i++) { @@ -377,7 +397,7 @@ class ConversationAdapter extends RecyclerView.Adapter { return messages; } - public SparseArray getPrivateMessages() { + SparseArray getPrivateMessages() { SparseArray messages = new SparseArray<>(); for (int i = 0; i < items.size(); i++) { @@ -408,7 +428,7 @@ class ConversationAdapter extends RecyclerView.Adapter { public TextView date; public ImageView status; - public MessageHolder(View v, int type) { + MessageHolder(View v, int type) { super(v); layout = (ViewGroup) v.findViewById(R.id.msgLayout); @@ -432,7 +452,7 @@ class ConversationAdapter extends RecyclerView.Adapter { private final TextView date; private final ImageView status; - public IntroductionHolder(View v, int type) { + IntroductionHolder(View v, int type) { super(v); messageLayout = v.findViewById(R.id.messageLayout); @@ -457,7 +477,7 @@ class ConversationAdapter extends RecyclerView.Adapter { private final TextView date; private final ImageView status; - public NoticeHolder(View v, int type) { + NoticeHolder(View v, int type) { super(v); text = (TextView) v.findViewById(R.id.noticeText); @@ -476,21 +496,21 @@ class ConversationAdapter extends RecyclerView.Adapter { private final View messageLayout; private final MessageHolder message; private final TextView text; - private final Button showForumsButton; + private final Button showInvitationsButton; private final TextView date; private final ImageView status; - public InvitationHolder(View v, int type) { + InvitationHolder(View v, int type) { super(v); messageLayout = v.findViewById(R.id.messageLayout); message = new MessageHolder(messageLayout, type == FORUM_INVITATION_IN ? MSG_IN : MSG_OUT); text = (TextView) v.findViewById(R.id.introductionText); - showForumsButton = (Button) v.findViewById(R.id.showForumsButton); + showInvitationsButton = (Button) v.findViewById(R.id.showForumsButton); date = (TextView) v.findViewById(R.id.introductionTime); - if (type == FORUM_INVITATION_OUT) { + if (type == FORUM_INVITATION_OUT || type == BLOG_INVITATION_OUT) { status = (ImageView) v.findViewById(R.id.introductionStatus); } else { status = null; @@ -543,7 +563,7 @@ class ConversationAdapter extends RecyclerView.Adapter { } } - public interface IntroductionHandler { + interface IntroductionHandler { void respondToIntroduction(SessionId sessionId, boolean accept); } } diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java deleted file mode 100644 index bfa5da545..000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationInItem.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.briarproject.android.contact; - -import org.briarproject.api.forum.ForumInvitationRequest; - -// This class is not thread-safe -public class ConversationForumInvitationInItem - extends ConversationForumInvitationItem - implements ConversationItem.IncomingItem { - - private boolean read; - - public ConversationForumInvitationInItem(ForumInvitationRequest fim) { - super(fim); - - this.read = fim.isRead(); - } - - @Override - int getType() { - return FORUM_INVITATION_IN; - } - - @Override - public boolean isRead() { - return read; - } - - @Override - public void setRead(boolean read) { - this.read = read; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java deleted file mode 100644 index 50c5c64d2..000000000 --- a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationItem.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.briarproject.android.contact; - -import org.briarproject.api.forum.ForumInvitationRequest; - -abstract class ConversationForumInvitationItem extends ConversationItem { - - private final ForumInvitationRequest fim; - - public ConversationForumInvitationItem(ForumInvitationRequest fim) { - super(fim.getId(), fim.getTimestamp()); - - this.fim = fim; - } - - public ForumInvitationRequest getForumInvitationMessage() { - return fim; - } -} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationItem.java index dbc81c25a..321cceab3 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationItem.java @@ -3,13 +3,15 @@ package org.briarproject.android.contact; import android.content.Context; import org.briarproject.R; -import org.briarproject.api.forum.ForumInvitationRequest; +import org.briarproject.api.blogs.BlogInvitationResponse; import org.briarproject.api.forum.ForumInvitationResponse; import org.briarproject.api.introduction.IntroductionMessage; import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionResponse; import org.briarproject.api.messaging.PrivateMessageHeader; import org.briarproject.api.sharing.InvitationMessage; +import org.briarproject.api.sharing.InvitationRequest; +import org.briarproject.api.sharing.InvitationResponse; import org.briarproject.api.sync.MessageId; // This class is not thread-safe @@ -25,6 +27,8 @@ public abstract class ConversationItem { final static int NOTICE_OUT = 6; final static int FORUM_INVITATION_IN = 7; final static int FORUM_INVITATION_OUT = 8; + final static int BLOG_INVITATION_IN = 9; + final static int BLOG_INVITATION_OUT = 10; private MessageId id; private long time; @@ -97,15 +101,27 @@ public abstract class ConversationItem { } } - public static ConversationItem from(ForumInvitationRequest fim) { + public static ConversationItem from(InvitationRequest fim) { if (fim.isLocal()) { - return new ConversationForumInvitationOutItem(fim); + return new ConversationShareableInvitationOutItem(fim); } else { - return new ConversationForumInvitationInItem(fim); + return new ConversationShareableInvitationInItem(fim); } } public static ConversationItem from(Context ctx, String contactName, + InvitationResponse ir) { + + if (ir instanceof ForumInvitationResponse) { + return from(ctx, contactName, (ForumInvitationResponse) ir); + } else if (ir instanceof BlogInvitationResponse) { + return from(ctx, contactName, (BlogInvitationResponse) ir); + } else { + throw new IllegalArgumentException("Unknown Invitation Response."); + } + } + + private static ConversationItem from(Context ctx, String contactName, ForumInvitationResponse fir) { if (fir.isLocal()) { @@ -137,6 +153,38 @@ public abstract class ConversationItem { } } + private static ConversationItem from(Context ctx, String contactName, + BlogInvitationResponse fir) { + + if (fir.isLocal()) { + String text; + if (fir.wasAccepted()) { + text = ctx.getString( + R.string.blogs_sharing_response_accepted_sent, + contactName); + } else { + text = ctx.getString( + R.string.blogs_sharing_response_declined_sent, + contactName); + } + return new ConversationNoticeOutItem(fir.getId(), text, + fir.getTimestamp(), fir.isSent(), fir.isSeen()); + } else { + String text; + if (fir.wasAccepted()) { + text = ctx.getString( + R.string.blogs_sharing_response_accepted_received, + contactName); + } else { + text = ctx.getString( + R.string.blogs_sharing_response_declined_received, + contactName); + } + return new ConversationNoticeInItem(fir.getId(), text, + fir.getTimestamp(), fir.isRead()); + } + } + /** * This method should not be used to get user-facing objects, * Its purpose is only to provide data for the contact list. diff --git a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java new file mode 100644 index 000000000..6e46b7b41 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationInItem.java @@ -0,0 +1,43 @@ +package org.briarproject.android.contact; + +import org.briarproject.api.blogs.BlogInvitationRequest; +import org.briarproject.api.forum.ForumInvitationRequest; +import org.briarproject.api.sharing.InvitationRequest; + +// This class is not thread-safe +class ConversationShareableInvitationInItem + extends ConversationShareableInvitationItem + implements ConversationItem.IncomingItem { + + private final int type; + private boolean read; + + ConversationShareableInvitationInItem(InvitationRequest ir) { + super(ir); + + if (ir instanceof ForumInvitationRequest) { + this.type = FORUM_INVITATION_IN; + } else if (ir instanceof BlogInvitationRequest) { + this.type = BLOG_INVITATION_IN; + } else { + throw new IllegalArgumentException("Unknown Invitation Type."); + } + + this.read = ir.isRead(); + } + + @Override + int getType() { + return type; + } + + @Override + public boolean isRead() { + return read; + } + + @Override + public void setRead(boolean read) { + this.read = read; + } +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java new file mode 100644 index 000000000..a2e9b91cf --- /dev/null +++ b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationItem.java @@ -0,0 +1,18 @@ +package org.briarproject.android.contact; + +import org.briarproject.api.sharing.InvitationRequest; + +abstract class ConversationShareableInvitationItem extends ConversationItem { + + private final InvitationRequest fim; + + ConversationShareableInvitationItem(InvitationRequest fim) { + super(fim.getId(), fim.getTimestamp()); + + this.fim = fim; + } + + InvitationRequest getInvitationRequest() { + return fim; + } +} diff --git a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java similarity index 52% rename from briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java rename to briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java index 5551fa60c..426403ab3 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationForumInvitationOutItem.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationShareableInvitationOutItem.java @@ -1,6 +1,8 @@ package org.briarproject.android.contact; +import org.briarproject.api.blogs.BlogInvitationRequest; import org.briarproject.api.forum.ForumInvitationRequest; +import org.briarproject.api.sharing.InvitationRequest; /** * This class is needed and can not be replaced by an ConversationNoticeOutItem, @@ -9,21 +11,31 @@ import org.briarproject.api.forum.ForumInvitationRequest; *

* This class is not thread-safe */ -public class ConversationForumInvitationOutItem - extends ConversationForumInvitationItem +class ConversationShareableInvitationOutItem + extends ConversationShareableInvitationItem implements ConversationItem.OutgoingItem { + private final int type; private boolean sent, seen; - public ConversationForumInvitationOutItem(ForumInvitationRequest fim) { - super(fim); - this.sent = fim.isSent(); - this.seen = fim.isSeen(); + ConversationShareableInvitationOutItem(InvitationRequest ir) { + super(ir); + + if (ir instanceof ForumInvitationRequest) { + this.type = FORUM_INVITATION_OUT; + } else if (ir instanceof BlogInvitationRequest) { + this.type = BLOG_INVITATION_OUT; + } else { + throw new IllegalArgumentException("Unknown Invitation Type."); + } + + this.sent = ir.isSent(); + this.seen = ir.isSeen(); } @Override int getType() { - return FORUM_INVITATION_OUT; + return type; } @Override diff --git a/briar-api/src/org/briarproject/api/blogs/BlogInvitationRequest.java b/briar-api/src/org/briarproject/api/blogs/BlogInvitationRequest.java index 473955960..0f2970b25 100644 --- a/briar-api/src/org/briarproject/api/blogs/BlogInvitationRequest.java +++ b/briar-api/src/org/briarproject/api/blogs/BlogInvitationRequest.java @@ -8,20 +8,20 @@ import org.briarproject.api.sync.MessageId; public class BlogInvitationRequest extends InvitationRequest { - private final String blogTitle; + private final String blogAuthorName; public BlogInvitationRequest(MessageId id, SessionId sessionId, - ContactId contactId, String blogTitle, String message, + ContactId contactId, String blogAuthorName, String message, boolean available, long time, boolean local, boolean sent, boolean seen, boolean read) { super(id, sessionId, contactId, message, available, time, local, sent, seen, read); - this.blogTitle = blogTitle; + this.blogAuthorName = blogAuthorName; } - public String getBlogTitle() { - return blogTitle; + public String getBlogAuthorName() { + return blogAuthorName; } } diff --git a/briar-api/src/org/briarproject/api/blogs/BlogInvitationResponse.java b/briar-api/src/org/briarproject/api/blogs/BlogInvitationResponse.java new file mode 100644 index 000000000..e3e7cdf69 --- /dev/null +++ b/briar-api/src/org/briarproject/api/blogs/BlogInvitationResponse.java @@ -0,0 +1,17 @@ +package org.briarproject.api.blogs; + +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.sharing.InvitationResponse; +import org.briarproject.api.sync.MessageId; + +public class BlogInvitationResponse extends InvitationResponse { + + public BlogInvitationResponse(MessageId id, SessionId sessionId, + ContactId contactId, boolean accept, long time, boolean local, + boolean sent, boolean seen, boolean read) { + + super(id, sessionId, contactId, accept, time, local, sent, seen, read); + } + +} diff --git a/briar-api/src/org/briarproject/api/blogs/BlogSharingManager.java b/briar-api/src/org/briarproject/api/blogs/BlogSharingManager.java index 59e06c712..4e21557f8 100644 --- a/briar-api/src/org/briarproject/api/blogs/BlogSharingManager.java +++ b/briar-api/src/org/briarproject/api/blogs/BlogSharingManager.java @@ -3,14 +3,14 @@ package org.briarproject.api.blogs; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; +import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.SharingManager; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import java.util.Collection; -public interface BlogSharingManager - extends SharingManager { +public interface BlogSharingManager extends SharingManager { /** * Returns the unique ID of the blog sharing client. @@ -34,7 +34,7 @@ public interface BlogSharingManager * Returns all blogs sharing messages sent by the Contact * identified by contactId. */ - Collection getInvitationMessages( + Collection getInvitationMessages( ContactId contactId) throws DbException; /** diff --git a/briar-api/src/org/briarproject/api/forum/ForumSharingManager.java b/briar-api/src/org/briarproject/api/forum/ForumSharingManager.java index 2ad3228dd..5f1f623c2 100644 --- a/briar-api/src/org/briarproject/api/forum/ForumSharingManager.java +++ b/briar-api/src/org/briarproject/api/forum/ForumSharingManager.java @@ -10,7 +10,7 @@ import org.briarproject.api.sync.GroupId; import java.util.Collection; -public interface ForumSharingManager extends SharingManager { +public interface ForumSharingManager extends SharingManager { /** Returns the unique ID of the forum sharing client. */ ClientId getClientId(); diff --git a/briar-api/src/org/briarproject/api/sharing/SharingManager.java b/briar-api/src/org/briarproject/api/sharing/SharingManager.java index 100ab5dc4..861edfc4c 100644 --- a/briar-api/src/org/briarproject/api/sharing/SharingManager.java +++ b/briar-api/src/org/briarproject/api/sharing/SharingManager.java @@ -8,7 +8,7 @@ import org.briarproject.api.sync.GroupId; import java.util.Collection; -public interface SharingManager { +public interface SharingManager { /** Returns the unique ID of the group sharing client. */ ClientId getClientId(); @@ -30,7 +30,7 @@ public interface SharingManager getInvitationMessages( + Collection getInvitationMessages( ContactId contactId) throws DbException; /** Returns all shareables to which the user has been invited. */ diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java index 550667641..19f9eef34 100644 --- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java @@ -4,6 +4,7 @@ import org.briarproject.api.FormatException; import org.briarproject.api.blogs.Blog; import org.briarproject.api.blogs.BlogFactory; import org.briarproject.api.blogs.BlogInvitationRequest; +import org.briarproject.api.blogs.BlogInvitationResponse; import org.briarproject.api.blogs.BlogManager; import org.briarproject.api.blogs.BlogManager.RemoveBlogHook; import org.briarproject.api.blogs.BlogSharingManager; @@ -25,6 +26,7 @@ import org.briarproject.api.event.BlogInvitationReceivedEvent; import org.briarproject.api.event.BlogInvitationResponseReceivedEvent; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; @@ -41,7 +43,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY; import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE; class BlogSharingManagerImpl extends - SharingManagerImpl + SharingManagerImpl implements BlogSharingManager, RemoveBlogHook { static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( @@ -89,21 +91,21 @@ class BlogSharingManagerImpl extends } @Override - protected BlogInvitationRequest createInvitationRequest(MessageId id, + protected InvitationMessage createInvitationRequest(MessageId id, BlogInvitation msg, ContactId contactId, boolean available, long time, boolean local, boolean sent, boolean seen, boolean read) { return new BlogInvitationRequest(id, msg.getSessionId(), contactId, - msg.getBlogTitle(), msg.getMessage(), available, time, local, - sent, seen, read); + msg.getBlogAuthorName(), msg.getMessage(), available, time, + local, sent, seen, read); } @Override - protected BlogInvitationRequest createInvitationResponse(MessageId id, + protected InvitationMessage createInvitationResponse(MessageId id, SessionId sessionId, ContactId contactId, boolean accept, long time, boolean local, boolean sent, boolean seen, boolean read) { - // TODO implement when doing blog sharing - return null; + return new BlogInvitationResponse(id, sessionId, contactId, accept, + time, local, sent, seen, read); } @Override diff --git a/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java index 95f9f0ef5..eb0724001 100644 --- a/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java @@ -37,7 +37,7 @@ import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; class ForumSharingManagerImpl extends - SharingManagerImpl + SharingManagerImpl implements ForumSharingManager, ForumManager.RemoveForumHook { static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString( diff --git a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java index ce34826ec..5f6fd4d41 100644 --- a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java @@ -83,9 +83,9 @@ import static org.briarproject.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.Invitation; import static org.briarproject.sharing.InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; -abstract class SharingManagerImpl +abstract class SharingManagerImpl extends BdfIncomingMessageHook - implements SharingManager, Client, AddContactHook, + implements SharingManager, Client, AddContactHook, RemoveContactHook { private static final Logger LOG = @@ -117,11 +117,11 @@ abstract class SharingManagerImpl getInvitationMessages(ContactId contactId) + public Collection getInvitationMessages(ContactId contactId) throws DbException { Transaction txn = db.startTransaction(true); @@ -334,7 +334,8 @@ abstract class SharingManagerImpl list = new ArrayList(); + Collection list = + new ArrayList(); Map map = clientHelper .getMessageMetadataAsDictionary(txn, group.getId()); for (Map.Entry m : map.entrySet()) { @@ -362,7 +363,7 @@ abstract class SharingManagerImpl Date: Tue, 2 Aug 2016 15:16:03 -0300 Subject: [PATCH 3/6] Fix Blog Sharing Backend and Add Blog Sharing Integration Tests --- .../BlogSharingIntegrationTest.java | 658 ++++++++++++++++++ .../BlogSharingIntegrationTestComponent.java | 100 +++ .../ForumManagerTestComponent.java | 4 +- .../ForumSharingIntegrationTestComponent.java | 4 +- .../briarproject/api/blogs/BlogManager.java | 2 +- .../api/identity/IdentityManager.java | 6 + .../briarproject/blogs/BlogFactoryImpl.java | 2 +- .../briarproject/blogs/BlogManagerImpl.java | 2 +- .../identity/IdentityManagerImpl.java | 20 +- .../sharing/BlogSharingManagerImpl.java | 19 +- .../sharing/BlogSharingValidator.java | 2 +- .../sharing/SharingManagerImpl.java | 2 +- 12 files changed, 808 insertions(+), 13 deletions(-) create mode 100644 briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java create mode 100644 briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java diff --git a/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java new file mode 100644 index 000000000..96a114d74 --- /dev/null +++ b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java @@ -0,0 +1,658 @@ +package org.briarproject; + +import net.jodah.concurrentunit.Waiter; + +import org.briarproject.api.blogs.Blog; +import org.briarproject.api.blogs.BlogInvitationRequest; +import org.briarproject.api.blogs.BlogInvitationResponse; +import org.briarproject.api.blogs.BlogManager; +import org.briarproject.api.blogs.BlogPostFactory; +import org.briarproject.api.blogs.BlogSharingManager; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.crypto.CryptoComponent; +import org.briarproject.api.crypto.KeyPair; +import org.briarproject.api.crypto.SecretKey; +import org.briarproject.api.db.DbException; +import org.briarproject.api.event.BlogInvitationReceivedEvent; +import org.briarproject.api.event.BlogInvitationResponseReceivedEvent; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.EventListener; +import org.briarproject.api.event.MessageStateChangedEvent; +import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.sharing.InvitationMessage; +import org.briarproject.api.sync.ClientId; +import org.briarproject.api.sync.SyncSession; +import org.briarproject.api.sync.SyncSessionFactory; +import org.briarproject.api.sync.ValidationManager.State; +import org.briarproject.api.system.Clock; +import org.briarproject.blogs.BlogsModule; +import org.briarproject.contact.ContactModule; +import org.briarproject.crypto.CryptoModule; +import org.briarproject.lifecycle.LifecycleModule; +import org.briarproject.properties.PropertiesModule; +import org.briarproject.sharing.SharingModule; +import org.briarproject.sync.SyncModule; +import org.briarproject.transport.TransportModule; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeoutException; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import static org.briarproject.TestPluginsModule.MAX_LATENCY; +import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; +import static org.briarproject.api.sync.ValidationManager.State.INVALID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class BlogSharingIntegrationTest extends BriarTestCase { + + private LifecycleManager lifecycleManager0, lifecycleManager1, + lifecycleManager2; + private SyncSessionFactory sync0, sync1, sync2; + private BlogManager blogManager0, blogManager1; + private ContactManager contactManager0, contactManager1, contactManager2; + private Contact contact1, contact2, contact01, contact02; + private ContactId contactId1, contactId2, contactId01, contactId02; + private IdentityManager identityManager0, identityManager1, + identityManager2; + private LocalAuthor author0, author1, author2; + private Blog blog0, blog1, blog2; + private SharerListener listener0, listener2; + private InviteeListener listener1; + + @Inject + Clock clock; + @Inject + AuthorFactory authorFactory; + @Inject + BlogPostFactory blogPostFactory; + @Inject + CryptoComponent cryptoComponent; + + // objects accessed from background threads need to be volatile + private volatile BlogSharingManager blogSharingManager0; + private volatile BlogSharingManager blogSharingManager1; + private volatile BlogSharingManager blogSharingManager2; + private volatile Waiter eventWaiter; + private volatile Waiter msgWaiter; + + private final File testDir = TestUtils.getTestDirectory(); + private final SecretKey master = TestUtils.getSecretKey(); + private final int TIMEOUT = 15000; + private final String SHARER = "Sharer"; + private final String INVITEE = "Invitee"; + private final String CONTACT2 = "Contact2"; + + private static final Logger LOG = + Logger.getLogger(BlogSharingIntegrationTest.class.getName()); + + private BlogSharingIntegrationTestComponent t0, t1, t2; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Before + public void setUp() { + BlogSharingIntegrationTestComponent component = + DaggerBlogSharingIntegrationTestComponent.builder().build(); + component.inject(this); + injectEagerSingletons(component); + + assertTrue(testDir.mkdirs()); + File t0Dir = new File(testDir, SHARER); + t0 = DaggerBlogSharingIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t0Dir)).build(); + injectEagerSingletons(t0); + File t1Dir = new File(testDir, INVITEE); + t1 = DaggerBlogSharingIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t1Dir)).build(); + injectEagerSingletons(t1); + File t2Dir = new File(testDir, CONTACT2); + t2 = DaggerBlogSharingIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(t2Dir)).build(); + injectEagerSingletons(t2); + + identityManager0 = t0.getIdentityManager(); + identityManager1 = t1.getIdentityManager(); + identityManager2 = t2.getIdentityManager(); + contactManager0 = t0.getContactManager(); + contactManager1 = t1.getContactManager(); + contactManager2 = t2.getContactManager(); + blogManager0 = t0.getBlogManager(); + blogManager1 = t1.getBlogManager(); + blogSharingManager0 = t0.getBlogSharingManager(); + blogSharingManager1 = t1.getBlogSharingManager(); + blogSharingManager2 = t2.getBlogSharingManager(); + sync0 = t0.getSyncSessionFactory(); + sync1 = t1.getSyncSessionFactory(); + sync2 = t2.getSyncSessionFactory(); + + // initialize waiters fresh for each test + eventWaiter = new Waiter(); + msgWaiter = new Waiter(); + } + + @Test + public void testPersonalBlogCannotBeSharedWithOwner() throws Exception { + startLifecycles(); + defaultInit(true); + + assertFalse(blogSharingManager0.canBeShared(blog1.getId(), contact1)); + assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact2)); + assertFalse(blogSharingManager1.canBeShared(blog0.getId(), contact01)); + assertFalse(blogSharingManager2.canBeShared(blog0.getId(), contact02)); + + // create invitation + blogSharingManager0 + .sendInvitation(blog1.getId(), contactId1, "Hi!"); + + // sync invitation + sync0To1(); + // make sure the invitee ignored the request for their own blog + assertFalse(listener1.requestReceived); + + stopLifecycles(); + } + + @Test + public void testSuccessfulSharing() throws Exception { + startLifecycles(); + + // initialize and let invitee accept all requests + defaultInit(true); + + // send invitation + blogSharingManager0 + .sendInvitation(blog2.getId(), contactId1, "Hi!"); + + // invitee has own blog and that of the sharer + assertEquals(2, blogManager1.getBlogs().size()); + + // sync first request message + sync0To1(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + + // sync response back + sync1To0(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.responseReceived); + + // blog was added successfully + assertEquals(0, blogSharingManager0.getInvited().size()); + assertEquals(3, blogManager1.getBlogs().size()); + + // invitee has one invitation message from sharer + List list = + new ArrayList<>(blogSharingManager1 + .getInvitationMessages(contactId01)); + assertEquals(2, list.size()); + // check other things are alright with the message + for (InvitationMessage m : list) { + if (m instanceof BlogInvitationRequest) { + BlogInvitationRequest invitation = + (BlogInvitationRequest) m; + assertFalse(invitation.isAvailable()); + assertEquals(blog2.getAuthor().getName(), + invitation.getBlogAuthorName()); + assertEquals(contactId1, invitation.getContactId()); + assertEquals("Hi!", invitation.getMessage()); + } else { + BlogInvitationResponse response = + (BlogInvitationResponse) m; + assertEquals(contactId01, response.getContactId()); + assertTrue(response.wasAccepted()); + assertTrue(response.isLocal()); + } + } + // sharer has own invitation message and response + assertEquals(2, + blogSharingManager0.getInvitationMessages(contactId1) + .size()); + // blog can not be shared again + assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1)); + assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01)); + + stopLifecycles(); + } + + @Test + public void testDeclinedSharing() throws Exception { + startLifecycles(); + + // initialize and let invitee deny all requests + defaultInit(false); + + // send invitation + blogSharingManager0 + .sendInvitation(blog2.getId(), contactId1, null); + + // sync first request message + sync0To1(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + + // sync response back + sync1To0(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.responseReceived); + + // blog was not added + assertEquals(0, blogSharingManager0.getInvited().size()); + assertEquals(2, blogManager1.getBlogs().size()); + // blog is no longer available to invitee who declined + assertEquals(0, blogSharingManager1.getInvited().size()); + + // invitee has one invitation message from sharer and one response + List list = + new ArrayList<>(blogSharingManager1 + .getInvitationMessages(contactId01)); + assertEquals(2, list.size()); + // check things are alright with the message + for (InvitationMessage m : list) { + if (m instanceof BlogInvitationRequest) { + BlogInvitationRequest invitation = + (BlogInvitationRequest) m; + assertFalse(invitation.isAvailable()); + assertEquals(blog2.getAuthor().getName(), + invitation.getBlogAuthorName()); + assertEquals(contactId1, invitation.getContactId()); + assertEquals(null, invitation.getMessage()); + } else { + BlogInvitationResponse response = + (BlogInvitationResponse) m; + assertEquals(contactId01, response.getContactId()); + assertFalse(response.wasAccepted()); + assertTrue(response.isLocal()); + } + } + // sharer has own invitation message and response + assertEquals(2, + blogSharingManager0.getInvitationMessages(contactId1) + .size()); + // blog can be shared again + assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1)); + + stopLifecycles(); + } + + @Test + public void testInviteeLeavesAfterFinished() throws Exception { + startLifecycles(); + + // initialize and let invitee accept all requests + defaultInit(true); + + // send invitation + blogSharingManager0 + .sendInvitation(blog2.getId(), contactId1, "Hi!"); + + // sync first request message + sync0To1(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + + // sync response back + sync1To0(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.responseReceived); + + // blog was added successfully + assertEquals(0, blogSharingManager0.getInvited().size()); + assertEquals(3, blogManager1.getBlogs().size()); + assertTrue(blogManager1.getBlogs().contains(blog2)); + + // sharer shares blog with invitee + assertTrue(blogSharingManager0.getSharedWith(blog2.getId()) + .contains(contact1)); + // invitee gets blog shared by sharer + assertTrue(blogSharingManager1.getSharedBy(blog2.getId()) + .contains(contact01)); + + // invitee un-subscribes from blog + blogManager1.removeBlog(blog2); + + // send leave message to sharer + sync1To0(); + + // blog is gone + assertEquals(0, blogSharingManager0.getInvited().size()); + assertEquals(2, blogManager1.getBlogs().size()); + + // sharer no longer shares blog with invitee + assertFalse(blogSharingManager0.getSharedWith(blog2.getId()) + .contains(contact1)); + // invitee no longer gets blog shared by sharer + assertFalse(blogSharingManager1.getSharedBy(blog2.getId()) + .contains(contact01)); + // blog can be shared again + assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1)); + assertTrue(blogSharingManager1.canBeShared(blog2.getId(), contact01)); + + stopLifecycles(); + } + + @Test + public void testInvitationForExistingBlog() throws Exception { + startLifecycles(); + + // initialize and let invitee accept all requests + defaultInit(true); + + // 1 and 2 are adding each other + contactManager1.addContact(author2, + author1.getId(), master, clock.currentTimeMillis(), true, + true + ); + contactManager2.addContact(author1, + author2.getId(), master, clock.currentTimeMillis(), true, + true + ); + assertEquals(3, blogManager1.getBlogs().size()); + + // sharer sends invitation for 2's blog to 1 + blogSharingManager0 + .sendInvitation(blog2.getId(), contactId1, "Hi!"); + + // sync first request message + sync0To1(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener1.requestReceived); + + // make sure blog2 is shared by 0 + Collection contacts = + blogSharingManager1.getSharedBy(blog2.getId()); + assertEquals(1, contacts.size()); + assertTrue(contacts.contains(contact01)); + + // make sure 1 knows that they have blog2 already + Collection messages = + blogSharingManager1.getInvitationMessages(contactId01); + assertEquals(2, messages.size()); + assertEquals(blog2, blogManager1.getBlog(blog2.getId())); + + // sync response back + sync1To0(); + eventWaiter.await(TIMEOUT, 1); + assertTrue(listener0.responseReceived); + + // blog was not added, because it was there already + assertEquals(0, blogSharingManager0.getInvited().size()); + assertEquals(3, blogManager1.getBlogs().size()); + + stopLifecycles(); + } + + + @After + public void tearDown() throws InterruptedException { + TestUtils.deleteTestDirectory(testDir); + } + + private class SharerListener implements EventListener { + + volatile boolean requestReceived = false; + volatile boolean responseReceived = false; + + public void eventOccurred(Event e) { + if (e instanceof MessageStateChangedEvent) { + MessageStateChangedEvent event = (MessageStateChangedEvent) e; + State s = event.getState(); + ClientId c = event.getClientId(); + if ((s == DELIVERED || s == INVALID) && + c.equals(blogSharingManager0.getClientId()) && + !event.isLocal()) { + LOG.info("TEST: Sharer received message in group " + + event.getMessage().getGroupId().hashCode()); + msgWaiter.resume(); + } else if (s == DELIVERED && !event.isLocal() && + c.equals(blogManager0.getClientId())) { + LOG.info("TEST: Sharer received blog post"); + msgWaiter.resume(); + } + } else if (e instanceof BlogInvitationResponseReceivedEvent) { + BlogInvitationResponseReceivedEvent event = + (BlogInvitationResponseReceivedEvent) e; + eventWaiter.assertEquals(contactId1, event.getContactId()); + responseReceived = true; + eventWaiter.resume(); + } + // this is only needed for tests where a blog is re-shared + else if (e instanceof BlogInvitationReceivedEvent) { + BlogInvitationReceivedEvent event = + (BlogInvitationReceivedEvent) e; + eventWaiter.assertEquals(contactId1, event.getContactId()); + requestReceived = true; + Blog b = event.getBlog(); + try { + Contact c = contactManager0.getContact(contactId1); + blogSharingManager0.respondToInvitation(b, c, true); + } catch (DbException ex) { + eventWaiter.rethrow(ex); + } finally { + eventWaiter.resume(); + } + } + } + } + + private class InviteeListener implements EventListener { + + volatile boolean requestReceived = false; + volatile boolean responseReceived = false; + + private final boolean accept, answer; + + InviteeListener(boolean accept, boolean answer) { + this.accept = accept; + this.answer = answer; + } + + InviteeListener(boolean accept) { + this(accept, true); + } + + public void eventOccurred(Event e) { + if (e instanceof MessageStateChangedEvent) { + MessageStateChangedEvent event = (MessageStateChangedEvent) e; + State s = event.getState(); + ClientId c = event.getClientId(); + if ((s == DELIVERED || s == INVALID) && + c.equals(blogSharingManager0.getClientId()) && + !event.isLocal()) { + LOG.info("TEST: Invitee received message in group " + + event.getMessage().getGroupId().hashCode()); + msgWaiter.resume(); + } else if (s == DELIVERED && !event.isLocal() && + c.equals(blogManager0.getClientId())) { + LOG.info("TEST: Invitee received blog post"); + msgWaiter.resume(); + } + } else if (e instanceof BlogInvitationReceivedEvent) { + BlogInvitationReceivedEvent event = + (BlogInvitationReceivedEvent) e; + requestReceived = true; + if (!answer) return; + Blog b = event.getBlog(); + try { + eventWaiter.assertEquals(1, + blogSharingManager1.getInvited().size()); + Contact c = + contactManager1.getContact(event.getContactId()); + blogSharingManager1.respondToInvitation(b, c, accept); + } catch (DbException ex) { + eventWaiter.rethrow(ex); + } finally { + eventWaiter.resume(); + } + } + // this is only needed for tests where a blog is re-shared + else if (e instanceof BlogInvitationResponseReceivedEvent) { + BlogInvitationResponseReceivedEvent event = + (BlogInvitationResponseReceivedEvent) e; + eventWaiter.assertEquals(contactId01, event.getContactId()); + responseReceived = true; + eventWaiter.resume(); + } + } + } + + private void startLifecycles() throws InterruptedException { + // Start the lifecycle manager and wait for it to finish + lifecycleManager0 = t0.getLifecycleManager(); + lifecycleManager1 = t1.getLifecycleManager(); + lifecycleManager2 = t2.getLifecycleManager(); + lifecycleManager0.startServices(); + lifecycleManager1.startServices(); + lifecycleManager2.startServices(); + lifecycleManager0.waitForStartup(); + lifecycleManager1.waitForStartup(); + lifecycleManager2.waitForStartup(); + } + + private void stopLifecycles() throws InterruptedException { + // Clean up + lifecycleManager0.stopServices(); + lifecycleManager1.stopServices(); + lifecycleManager2.stopServices(); + lifecycleManager0.waitForShutdown(); + lifecycleManager1.waitForShutdown(); + lifecycleManager2.waitForShutdown(); + } + + private void defaultInit(boolean accept) throws DbException { + addDefaultIdentities(); + addDefaultContacts(); + getPersonalBlogOfSharer(); + listenToEvents(accept); + } + + private void addDefaultIdentities() throws DbException { + KeyPair keyPair = cryptoComponent.generateSignatureKeyPair(); + author0 = authorFactory.createLocalAuthor(SHARER, + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded()); + identityManager0.addLocalAuthor(author0); + + keyPair = cryptoComponent.generateSignatureKeyPair(); + author1 = authorFactory.createLocalAuthor(INVITEE, + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded()); + identityManager1.addLocalAuthor(author1); + + keyPair = cryptoComponent.generateSignatureKeyPair(); + author2 = authorFactory.createLocalAuthor(CONTACT2, + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded()); + identityManager2.addLocalAuthor(author2); + } + + private void addDefaultContacts() throws DbException { + // sharer adds invitee as contact + contactId1 = contactManager0.addContact(author1, + author0.getId(), master, clock.currentTimeMillis(), true, + true + ); + contact1 = contactManager0.getContact(contactId1); + // sharer adds second contact + contactId2 = contactManager0.addContact(author2, + author0.getId(), master, clock.currentTimeMillis(), true, + true + ); + contact2 = contactManager0.getContact(contactId2); + // contacts add sharer back + contactId01 = contactManager1.addContact(author0, + author1.getId(), master, clock.currentTimeMillis(), true, + true + ); + contact01 = contactManager1.getContact(contactId01); + contactId02 = contactManager2.addContact(author0, + author2.getId(), master, clock.currentTimeMillis(), true, + true + ); + contact02 = contactManager2.getContact(contactId02); + } + + private void getPersonalBlogOfSharer() throws DbException { + blog0 = blogManager0.getPersonalBlog(author0); + blog1 = blogManager0.getPersonalBlog(author1); + blog2 = blogManager0.getPersonalBlog(author2); + } + + private void listenToEvents(boolean accept) { + listener0 = new SharerListener(); + t0.getEventBus().addListener(listener0); + listener1 = new InviteeListener(accept); + t1.getEventBus().addListener(listener1); + listener2 = new SharerListener(); + t2.getEventBus().addListener(listener2); + } + + private void sync0To1() throws IOException, TimeoutException { + deliverMessage(sync0, contactId01, sync1, contactId1, + "Sharer to Invitee"); + } + + private void sync1To0() throws IOException, TimeoutException { + deliverMessage(sync1, contactId1, sync0, contactId01, + "Invitee to Sharer"); + } + + private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId, + SyncSessionFactory toSync, ContactId toId, String debug) + throws IOException, TimeoutException { + + if (debug != null) LOG.info("TEST: Sending message from " + debug); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Create an outgoing sync session + SyncSession sessionFrom = + fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out); + // Write whatever needs to be written + sessionFrom.run(); + out.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + // Create an incoming sync session + SyncSession sessionTo = toSync.createIncomingSession(fromId, in); + // Read whatever needs to be read + sessionTo.run(); + in.close(); + + // wait for message to actually arrive + msgWaiter.await(TIMEOUT, 1); + } + + private void injectEagerSingletons( + BlogSharingIntegrationTestComponent component) { + + component.inject(new LifecycleModule.EagerSingletons()); + component.inject(new BlogsModule.EagerSingletons()); + component.inject(new CryptoModule.EagerSingletons()); + component.inject(new ContactModule.EagerSingletons()); + component.inject(new TransportModule.EagerSingletons()); + component.inject(new SharingModule.EagerSingletons()); + component.inject(new SyncModule.EagerSingletons()); + component.inject(new PropertiesModule.EagerSingletons()); + } + +} diff --git a/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java new file mode 100644 index 000000000..1f738830e --- /dev/null +++ b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java @@ -0,0 +1,100 @@ +package org.briarproject; + +import org.briarproject.api.blogs.BlogManager; +import org.briarproject.api.blogs.BlogSharingManager; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.clients.MessageQueueManager; +import org.briarproject.api.clients.PrivateGroupFactory; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.forum.ForumManager; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.sync.SyncSessionFactory; +import org.briarproject.blogs.BlogsModule; +import org.briarproject.clients.ClientsModule; +import org.briarproject.contact.ContactModule; +import org.briarproject.crypto.CryptoModule; +import org.briarproject.data.DataModule; +import org.briarproject.db.DatabaseModule; +import org.briarproject.event.EventModule; +import org.briarproject.forum.ForumModule; +import org.briarproject.identity.IdentityModule; +import org.briarproject.lifecycle.LifecycleModule; +import org.briarproject.properties.PropertiesModule; +import org.briarproject.sharing.SharingModule; +import org.briarproject.sync.SyncModule; +import org.briarproject.system.SystemModule; +import org.briarproject.transport.TransportModule; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = { + TestDatabaseModule.class, + TestPluginsModule.class, + TestSeedProviderModule.class, + ClientsModule.class, + ContactModule.class, + CryptoModule.class, + DataModule.class, + DatabaseModule.class, + EventModule.class, + BlogsModule.class, + ForumModule.class, + IdentityModule.class, + LifecycleModule.class, + PropertiesModule.class, + SharingModule.class, + SyncModule.class, + SystemModule.class, + TransportModule.class +}) +interface BlogSharingIntegrationTestComponent { + + void inject(BlogSharingIntegrationTest testCase); + + void inject(ContactModule.EagerSingletons init); + + void inject(CryptoModule.EagerSingletons init); + + void inject(BlogsModule.EagerSingletons init); + + void inject(LifecycleModule.EagerSingletons init); + + void inject(PropertiesModule.EagerSingletons init); + + void inject(SharingModule.EagerSingletons init); + + void inject(SyncModule.EagerSingletons init); + + void inject(TransportModule.EagerSingletons init); + + LifecycleManager getLifecycleManager(); + + EventBus getEventBus(); + + IdentityManager getIdentityManager(); + + ContactManager getContactManager(); + + BlogSharingManager getBlogSharingManager(); + + BlogManager getBlogManager(); + + SyncSessionFactory getSyncSessionFactory(); + + /* the following methods are only needed to manually construct messages */ + + DatabaseComponent getDatabaseComponent(); + + PrivateGroupFactory getPrivateGroupFactory(); + + ClientHelper getClientHelper(); + + MessageQueueManager getMessageQueueManager(); + +} diff --git a/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java index 7e74d841b..74c771488 100644 --- a/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java +++ b/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java @@ -7,6 +7,7 @@ import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.sync.SyncSessionFactory; +import org.briarproject.blogs.BlogsModule; import org.briarproject.clients.ClientsModule; import org.briarproject.contact.ContactModule; import org.briarproject.crypto.CryptoModule; @@ -38,6 +39,7 @@ import dagger.Component; DatabaseModule.class, EventModule.class, ForumModule.class, + BlogsModule.class, IdentityModule.class, LifecycleModule.class, PropertiesModule.class, @@ -46,7 +48,7 @@ import dagger.Component; SystemModule.class, TransportModule.class }) -public interface ForumManagerTestComponent { +interface ForumManagerTestComponent { void inject(ForumManagerTest testCase); diff --git a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java index 8e3ecdf9f..6f3da3c1c 100644 --- a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java +++ b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java @@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.lifecycle.LifecycleManager; import org.briarproject.api.sync.SyncSessionFactory; +import org.briarproject.blogs.BlogsModule; import org.briarproject.clients.ClientsModule; import org.briarproject.contact.ContactModule; import org.briarproject.crypto.CryptoModule; @@ -42,6 +43,7 @@ import dagger.Component; DatabaseModule.class, EventModule.class, ForumModule.class, + BlogsModule.class, IdentityModule.class, LifecycleModule.class, PropertiesModule.class, @@ -50,7 +52,7 @@ import dagger.Component; SystemModule.class, TransportModule.class }) -public interface ForumSharingIntegrationTestComponent { +interface ForumSharingIntegrationTestComponent { void inject(ForumSharingIntegrationTest testCase); diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java index 7d6188fed..f39a90d62 100644 --- a/briar-api/src/org/briarproject/api/blogs/BlogManager.java +++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java @@ -36,7 +36,7 @@ public interface BlogManager { Collection getBlogs(LocalAuthor localAuthor) throws DbException; /** Returns only the personal blog of the given author. */ - Blog getPersonalBlog(Author author) throws DbException; + Blog getPersonalBlog(Author author); /** Returns all blogs to which the user subscribes. */ Collection getBlogs() throws DbException; diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java index e89f49d8a..3c2b10953 100644 --- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java +++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java @@ -20,6 +20,12 @@ public interface IdentityManager { /** Returns the local pseudonym with the given ID. */ LocalAuthor getLocalAuthor(AuthorId a) throws DbException; + /** Returns the main local identity. */ + LocalAuthor getLocalAuthor() throws DbException; + + /** Returns the main local identity within the given Transaction. */ + LocalAuthor getLocalAuthor(Transaction txn) throws DbException; + /** Returns all local pseudonyms. */ Collection getLocalAuthors() throws DbException; diff --git a/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java index 9a3e0583a..68fbe1258 100644 --- a/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java +++ b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java @@ -69,7 +69,7 @@ class BlogFactoryImpl implements BlogFactory { Author a = authorFactory.createAuthor(blog.getString(1), blog.getRaw(2)); // TODO change permanent depending on how this will be used - boolean permanent = false; + boolean permanent = true; return new Blog(g, blog.getString(0), description, a, permanent); } diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java index a35bc0bc7..5a20d8934 100644 --- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java +++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java @@ -282,7 +282,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, } @Override - public Blog getPersonalBlog(Author author) throws DbException { + public Blog getPersonalBlog(Author author) { return blogFactory.createPersonalBlog(author); } diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java index bd14c3340..235448b46 100644 --- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java +++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java @@ -10,9 +10,7 @@ import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; import java.util.Collection; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import javax.inject.Inject; @@ -69,12 +67,22 @@ class IdentityManagerImpl implements IdentityManager { return author; } + @Override + public LocalAuthor getLocalAuthor() throws DbException { + return getLocalAuthors().iterator().next(); + } + + @Override + public LocalAuthor getLocalAuthor(Transaction txn) throws DbException { + return getLocalAuthors(txn).iterator().next(); + } + @Override public Collection getLocalAuthors() throws DbException { Collection authors; Transaction txn = db.startTransaction(true); try { - authors = db.getLocalAuthors(txn); + authors = getLocalAuthors(txn); txn.setComplete(); } finally { db.endTransaction(txn); @@ -82,6 +90,12 @@ class IdentityManagerImpl implements IdentityManager { return authors; } + private Collection getLocalAuthors(Transaction txn) + throws DbException { + + return db.getLocalAuthors(txn); + } + @Override public void removeLocalAuthor(AuthorId a) throws DbException { Transaction txn = db.startTransaction(false); diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java index 19f9eef34..7c1ce43bb 100644 --- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java @@ -26,6 +26,8 @@ import org.briarproject.api.event.BlogInvitationReceivedEvent; import org.briarproject.api.event.BlogInvitationResponseReceivedEvent; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.GroupId; @@ -50,6 +52,8 @@ class BlogSharingManagerImpl extends "bee438b5de0b3a685badc4e49d76e72d" + "21e01c4b569a775112756bdae267a028")); + @Inject + IdentityManager identityManager; private final BlogManager blogManager; private final SFactory sFactory; @@ -84,10 +88,19 @@ class BlogSharingManagerImpl extends } @Override - public boolean canBeShared(GroupId g, Contact c) throws DbException { - Blog b = blogManager.getPersonalBlog(c.getAuthor()); + protected boolean canBeShared(Transaction txn, GroupId g, Contact c) + throws DbException { + + // check if g is our personal blog + LocalAuthor author = identityManager.getLocalAuthor(txn); + Blog b = blogManager.getPersonalBlog(author); if (b.getId().equals(g)) return false; - return super.canBeShared(g, c); + + // check if g is c's personal blog + b = blogManager.getPersonalBlog(c.getAuthor()); + if (b.getId().equals(g)) return false; + + return super.canBeShared(txn, g, c); } @Override diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java b/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java index 26b89bb52..1a9f0ada3 100644 --- a/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java +++ b/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java @@ -58,7 +58,7 @@ class BlogSharingValidator extends BdfMessageValidator { checkLength(name, 1, MAX_BLOG_TITLE_LENGTH); String desc = body.getString(3); - checkLength(desc, 1, MAX_BLOG_DESC_LENGTH); + checkLength(desc, 0, MAX_BLOG_DESC_LENGTH); BdfList author = body.getList(4); checkSize(author, 2); diff --git a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java index 5f6fd4d41..24b2b5223 100644 --- a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java @@ -500,7 +500,7 @@ abstract class SharingManagerImpl Date: Tue, 2 Aug 2016 16:48:27 -0300 Subject: [PATCH 4/6] 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 55b48260e..7b2c1ceac 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -102,7 +102,7 @@ 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 5a3f510b5..54c7316a6 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -316,6 +316,10 @@ %1$s has shared the personal blog of %2$s with you. You have shared the personal blog of %1$s with %2$s. Show Blog Invitations + Blog Invitations + You are subscribed to this blog already.\nWarning: Accepting again might reveal that %s is your contact. + Subscribed to Blog + Blog Invitation Declined Blog List Available Blogs diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 8d4d85225..81ef8bd57 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 6741890c5..353a7e1ce 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 fd3e24196..000000000 --- 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 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 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 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 cca381a80..26721db36 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 000000000..c6127143e --- /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 000000000..a0e898715 --- /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 28ef9e2f1..22a5f3e02 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 { +abstract class InvitationAdapter extends + RecyclerView.Adapter { - private final Context ctx; + protected final Context ctx; private final AvailableForumClickListener listener; - private final SortedList forums = - new SortedList<>(ForumInvitationItem.class, + private final SortedList 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 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 list) { - forums.addAll(list); + public void addAll(Collection 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 { + extends SortedList.Callback { @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 9db87abe7..65dccd4ff 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 contacts; - ForumInvitationItem(Forum forum, boolean subscribed, + InvitationItem(Shareable shareable, boolean subscribed, Collection 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 000000000..614f54cbf --- /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 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 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 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 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 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 6033021eb..b48d0e7ad 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); + } + } From a3b2358164d146954a72dd518a14db67a593e118 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 2 Aug 2016 17:23:11 -0300 Subject: [PATCH 5/6] Show Sharing Status for Blogs This refactors the current SharingStatusActivity so it can be used for forums and blogs. --- .../android/blogs/BlogFragment.java | 1 + .../android/forum/ForumActivity.java | 1 + .../sharing/SharingStatusActivity.java | 47 ++++++++++++++++--- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java index a6e46b3fe..e58877708 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java @@ -180,6 +180,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { new Intent(getActivity(), SharingStatusActivity.class); i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); i3.putExtra(GROUP_ID, groupId.getBytes()); + i3.putExtra(SHAREABLE, BLOG); startActivity(i3, options.toBundle()); return true; default: diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java index 383992a8e..77d3a713a 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java @@ -236,6 +236,7 @@ public class ForumActivity extends BriarActivity implements Intent i3 = new Intent(this, SharingStatusActivity.class); i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP); i3.putExtra(GROUP_ID, groupId.getBytes()); + i3.putExtra(SHAREABLE, FORUM); ActivityCompat.startActivity(this, i3, options.toBundle()); return true; case R.id.action_forum_delete: diff --git a/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java index b9c51cbe6..7d8bf5b31 100644 --- a/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java @@ -10,6 +10,7 @@ import org.briarproject.android.ActivityComponent; import org.briarproject.android.BriarActivity; import org.briarproject.android.contact.ContactListItem; import org.briarproject.android.util.BriarRecyclerView; +import org.briarproject.api.blogs.BlogSharingManager; import org.briarproject.api.contact.Contact; import org.briarproject.api.db.DbException; import org.briarproject.api.forum.ForumSharingManager; @@ -25,6 +26,9 @@ import java.util.logging.Logger; import javax.inject.Inject; import static java.util.logging.Level.WARNING; +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 SharingStatusActivity extends BriarActivity { @@ -36,10 +40,13 @@ public class SharingStatusActivity extends BriarActivity { @Inject protected volatile ForumSharingManager forumSharingManager; @Inject + protected volatile BlogSharingManager blogSharingManager; + @Inject protected volatile IdentityManager identityManager; public final static String TAG = "ForumSharingStatusActivity"; private static final Logger LOG = Logger.getLogger(TAG); + private int shareable; @Override public void onCreate(Bundle savedInstanceState) { @@ -51,6 +58,8 @@ public class SharingStatusActivity extends BriarActivity { byte[] b = i.getByteArrayExtra(GROUP_ID); if (b == null) throw new IllegalStateException("No GroupId"); groupId = new GroupId(b); + shareable = i.getIntExtra(SHAREABLE, 0); + if (shareable == 0) throw new IllegalStateException("No Shareable"); sharedByList = (BriarRecyclerView) findViewById(R.id.sharedByView); sharedByAdapter = new SharingStatusAdapter(this); @@ -96,9 +105,7 @@ public class SharingStatusActivity extends BriarActivity { public void run() { List contactItems = new ArrayList<>(); try { - Collection contacts = - forumSharingManager.getSharedBy(groupId); - for (Contact c : contacts) { + for (Contact c : getSharedBy()) { LocalAuthor localAuthor = identityManager .getLocalAuthor(c.getLocalAuthorId()); ContactListItem item = @@ -134,9 +141,7 @@ public class SharingStatusActivity extends BriarActivity { public void run() { List contactItems = new ArrayList<>(); try { - Collection contacts = - forumSharingManager.getSharedWith(groupId); - for (Contact c : contacts) { + for (Contact c : getSharedWith()) { LocalAuthor localAuthor = identityManager .getLocalAuthor(c.getLocalAuthorId()); ContactListItem item = @@ -153,6 +158,36 @@ public class SharingStatusActivity extends BriarActivity { }); } + /** + * This must only be called from the DbThread + */ + private Collection getSharedWith() throws DbException { + Collection contacts; + if (shareable == FORUM) { + contacts = forumSharingManager.getSharedWith(groupId); + } else if (shareable == BLOG) { + contacts = blogSharingManager.getSharedWith(groupId); + } else { + throw new IllegalArgumentException("Unknown Shareable"); + } + return contacts; + } + + /** + * This must only be called from the DbThread + */ + private Collection getSharedBy() throws DbException { + Collection contacts; + if (shareable == FORUM) { + contacts = forumSharingManager.getSharedBy(groupId); + } else if (shareable == BLOG) { + contacts = blogSharingManager.getSharedBy(groupId); + } else { + throw new IllegalArgumentException("Unknown Shareable"); + } + return contacts; + } + private void displaySharedWith(final List contacts) { runOnUiThread(new Runnable() { @Override From a4cf91fba540187e2f361724c7f2b671ec319986 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 3 Aug 2016 13:00:24 -0300 Subject: [PATCH 6/6] Use Inheritence for shared Forum and Blog Sharing Code --- briar-android/AndroidManifest.xml | 40 ++- ...ity_share_forum.xml => activity_share.xml} | 0 ...status.xml => activity_sharing_status.xml} | 0 ...n_out.xml => list_item_msg_notice_out.xml} | 0 .../list_item_shareable_invitation_in.xml | 6 +- briar-android/res/menu/blogs_blog_actions.xml | 2 +- briar-android/res/menu/forum_actions.xml | 2 +- briar-android/res/values-pt-rBR/strings.xml | 2 +- briar-android/res/values/strings.xml | 7 +- .../android/ActivityComponent.java | 29 +- .../android/blogs/BlogFragment.java | 14 +- .../android/contact/ContactListFragment.java | 16 +- .../android/contact/ConversationActivity.java | 11 +- .../android/contact/ConversationAdapter.java | 21 +- .../android/forum/ForumActivity.java | 12 +- .../android/forum/ForumListFragment.java | 7 +- .../sharing/ContactSelectorFragment.java | 30 +- .../android/sharing/InvitationsActivity.java | 267 +++--------------- .../sharing/InvitationsBlogActivity.java | 127 +++++++++ .../sharing/InvitationsForumActivity.java | 127 +++++++++ .../android/sharing/ShareActivity.java | 30 +- .../android/sharing/ShareBlogActivity.java | 35 +++ .../sharing/ShareBlogMessageFragment.java | 81 ++++++ .../android/sharing/ShareForumActivity.java | 34 +++ .../sharing/ShareForumMessageFragment.java | 79 ++++++ .../android/sharing/ShareMessageFragment.java | 104 ++----- .../sharing/SharingStatusActivity.java | 64 +---- .../sharing/SharingStatusBlogActivity.java | 37 +++ .../sharing/SharingStatusForumActivity.java | 37 +++ 29 files changed, 755 insertions(+), 466 deletions(-) rename briar-android/res/layout/{activity_share_forum.xml => activity_share.xml} (100%) rename briar-android/res/layout/{activity_forum_sharing_status.xml => activity_sharing_status.xml} (100%) rename briar-android/res/layout/{list_item_introduction_out.xml => list_item_msg_notice_out.xml} (100%) create mode 100644 briar-android/src/org/briarproject/android/sharing/InvitationsBlogActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/InvitationsForumActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java create mode 100644 briar-android/src/org/briarproject/android/sharing/SharingStatusBlogActivity.java create mode 100644 briar-android/src/org/briarproject/android/sharing/SharingStatusForumActivity.java diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 7b2c1ceac..df2a916d3 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -102,7 +102,7 @@ + + + + + + + + + + + +