From 9664aea520f7980783682c7a33222303aa313853 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 16 Aug 2016 14:10:54 -0300 Subject: [PATCH 1/5] Introduce and use CardView and AuthorView for Blog Posts --- briar-android/build.gradle | 2 + briar-android/res/layout/author_view.xml | 49 ++++++ .../res/layout/list_item_blog_post.xml | 140 ++++-------------- briar-android/res/values/styles.xml | 6 + .../android/blogs/BlogPostAdapter.java | 32 ++-- .../briarproject/android/util/AuthorView.java | 60 ++++++++ 6 files changed, 159 insertions(+), 130 deletions(-) create mode 100644 briar-android/res/layout/author_view.xml create mode 100644 briar-android/src/org/briarproject/android/util/AuthorView.java diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 8e75abfe4..adea13909 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -27,6 +27,7 @@ dependencies { exclude module: 'support-v4' exclude module: 'recyclerview-v7' } + compile "com.android.support:cardview-v7:$supportVersion" compile('ch.acra:acra:4.8.5') { exclude module: 'support-v4' exclude module: 'support-annotations' @@ -61,6 +62,7 @@ dependencyVerification { 'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1', 'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1', 'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe', + 'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b', ] } diff --git a/briar-android/res/layout/author_view.xml b/briar-android/res/layout/author_view.xml new file mode 100644 index 000000000..2f4a33564 --- /dev/null +++ b/briar-android/res/layout/author_view.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/briar-android/res/layout/list_item_blog_post.xml b/briar-android/res/layout/list_item_blog_post.xml index 0f7e1d532..2408a1923 100644 --- a/briar-android/res/layout/list_item_blog_post.xml +++ b/briar-android/res/layout/list_item_blog_post.xml @@ -1,118 +1,42 @@ - + android:layout_height="wrap_content"> - - - - - - - - - - - - - - - + android:layout_height="wrap_content"> - + - + - + + + + \ No newline at end of file diff --git a/briar-android/res/values/styles.xml b/briar-android/res/values/styles.xml index 98751d04c..c432dddfa 100644 --- a/briar-android/res/values/styles.xml +++ b/briar-android/res/values/styles.xml @@ -124,6 +124,12 @@ @color/briar_text_primary_inverse + + diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 3e491d25b..2a0d82c4b 100644 --- a/briar-android/src/org/briarproject/android/ActivityComponent.java +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -6,10 +6,10 @@ import org.briarproject.android.blogs.BlogActivity; import org.briarproject.android.blogs.BlogFragment; import org.briarproject.android.blogs.BlogListFragment; import org.briarproject.android.blogs.BlogPostFragment; -import org.briarproject.android.blogs.BlogsFragment; import org.briarproject.android.blogs.CreateBlogActivity; import org.briarproject.android.blogs.FeedFragment; -import org.briarproject.android.blogs.MyBlogsFragment; +import org.briarproject.android.blogs.ReblogActivity; +import org.briarproject.android.blogs.ReblogFragment; import org.briarproject.android.blogs.RssFeedImportActivity; import org.briarproject.android.blogs.RssFeedManageActivity; import org.briarproject.android.blogs.WriteBlogPostActivity; @@ -93,6 +93,10 @@ public interface ActivityComponent { void inject(BlogPostFragment fragment); + void inject(ReblogFragment fragment); + + void inject(ReblogActivity activity); + void inject(SettingsActivity activity); void inject(ChangePasswordActivity activity); @@ -106,10 +110,8 @@ public interface ActivityComponent { // Fragments void inject(ContactListFragment fragment); void inject(ForumListFragment fragment); - void inject(BlogsFragment fragment); void inject(BlogListFragment fragment); void inject(FeedFragment fragment); - void inject(MyBlogsFragment fragment); void inject(IntroFragment fragment); void inject(ShowQrCodeFragment fragment); void inject(ContactChooserFragment fragment); diff --git a/briar-android/src/org/briarproject/android/BriarFragmentActivity.java b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java index 0a9f22a2e..513ace7b5 100644 --- a/briar-android/src/org/briarproject/android/BriarFragmentActivity.java +++ b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java @@ -7,7 +7,7 @@ import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import org.briarproject.R; -import org.briarproject.android.blogs.BlogsFragment; +import org.briarproject.android.blogs.FeedFragment; import org.briarproject.android.contact.ContactListFragment; import org.briarproject.android.forum.ForumListFragment; import org.briarproject.android.fragment.BaseFragment; @@ -27,7 +27,7 @@ public abstract class BriarFragmentActivity extends BriarActivity { actionBar.setTitle(R.string.contact_list_button); } else if (fragmentTag.equals(ForumListFragment.TAG)) { actionBar.setTitle(R.string.forums_button); - } else if (fragmentTag.equals(BlogsFragment.TAG)) { + } else if (fragmentTag.equals(FeedFragment.TAG)) { actionBar.setTitle(R.string.blogs_button); } } diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java index 8de76247a..6381bc9cd 100644 --- a/briar-android/src/org/briarproject/android/NavDrawerActivity.java +++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java @@ -21,7 +21,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.briarproject.R; -import org.briarproject.android.blogs.BlogsFragment; +import org.briarproject.android.blogs.FeedFragment; import org.briarproject.android.contact.ContactListFragment; import org.briarproject.android.controller.NavDrawerController; import org.briarproject.android.controller.TransportStateListener; @@ -82,7 +82,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements startFragment(ContactListFragment.newInstance()); } else if (intent.getBooleanExtra(INTENT_BLOGS, false)) { - startFragment(BlogsFragment.newInstance()); + startFragment(FeedFragment.newInstance()); } setIntent(null); } @@ -186,7 +186,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements startFragment(ForumListFragment.newInstance()); break; case R.id.nav_btn_blogs: - startFragment(BlogsFragment.newInstance()); + startFragment(FeedFragment.newInstance()); break; case R.id.nav_btn_settings: startActivity(new Intent(this, SettingsActivity.class)); diff --git a/briar-android/src/org/briarproject/android/blogs/BaseController.java b/briar-android/src/org/briarproject/android/blogs/BaseController.java new file mode 100644 index 000000000..e81c44d60 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BaseController.java @@ -0,0 +1,39 @@ +package org.briarproject.android.blogs; + +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; + +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.blogs.BlogPostHeader; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; + +import java.util.Collection; + +public interface BaseController { + + void onStart(); + + void onStop(); + + void loadBlogPosts(GroupId g, + ResultExceptionHandler, DbException> handler); + + void loadBlogPost(BlogPostHeader header, + ResultExceptionHandler handler); + + void loadBlogPost(GroupId g, MessageId m, + ResultExceptionHandler handler); + + void repeatPost(BlogPostItem item, @Nullable String comment, + ResultExceptionHandler resultHandler); + + void setOnBlogPostAddedListener(OnBlogPostAddedListener listener); + + interface OnBlogPostAddedListener { + @UiThread + void onBlogPostAdded(BlogPostHeader header, boolean local); + } + +} diff --git a/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java new file mode 100644 index 000000000..ac5ce7b72 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java @@ -0,0 +1,250 @@ +package org.briarproject.android.blogs; + +import android.app.Activity; +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; + +import org.briarproject.android.api.AndroidNotificationManager; +import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.blogs.Blog; +import org.briarproject.api.blogs.BlogCommentHeader; +import org.briarproject.api.blogs.BlogManager; +import org.briarproject.api.blogs.BlogPostHeader; +import org.briarproject.api.db.DbException; +import org.briarproject.api.event.BlogPostAddedEvent; +import org.briarproject.api.event.Event; +import org.briarproject.api.event.EventBus; +import org.briarproject.api.event.EventListener; +import org.briarproject.api.identity.IdentityManager; +import org.briarproject.api.identity.LocalAuthor; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; + +abstract class BaseControllerImpl extends DbControllerImpl + implements BaseController, EventListener { + + private static final Logger LOG = + Logger.getLogger(BaseControllerImpl.class.getName()); + + @Inject + protected Activity activity; + @Inject + protected EventBus eventBus; + @Inject + protected AndroidNotificationManager notificationManager; + @Inject + protected IdentityManager identityManager; + + @Inject + protected volatile BlogManager blogManager; + + private final Map bodyCache = new ConcurrentHashMap<>(); + private final Map headerCache = + new ConcurrentHashMap<>(); + + protected volatile OnBlogPostAddedListener listener; + + @Override + @CallSuper + public void onStart() { + eventBus.addListener(this); + } + + @Override + @CallSuper + public void onStop() { + eventBus.removeListener(this); + } + + @Override + @CallSuper + public void eventOccurred(Event e) { + if (e instanceof BlogPostAddedEvent) { + final BlogPostAddedEvent m = (BlogPostAddedEvent) e; + LOG.info("New blog post added"); + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + listener.onBlogPostAdded(m.getHeader(), m.isLocal()); + } + }); + } + } + + @Override + public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) { + if (this.listener != null) + throw new IllegalStateException("Listener was already set"); + this.listener = listener; + } + + @Override + public void loadBlogPosts(final GroupId groupId, + final ResultExceptionHandler, DbException> handler) { + if (groupId == null) throw new IllegalStateException(); + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection items = loadItems(groupId); + handler.onResult(items); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + protected Collection loadItems(GroupId groupId) + throws DbException { + long now = System.currentTimeMillis(); + Collection headers = + blogManager.getPostHeaders(groupId); + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Loading headers took " + duration + " ms"); + Collection items = new ArrayList<>(headers.size()); + now = System.currentTimeMillis(); + for (BlogPostHeader h : headers) { + headerCache.put(h.getId(), h); + BlogPostItem item = getItem(h); + items.add(item); + } + duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Loading bodies took " + duration + " ms"); + return items; + } + + @Override + public void loadBlogPost(final BlogPostHeader header, + final ResultExceptionHandler handler) { + + String body = bodyCache.get(header.getId()); + if (body != null) { + LOG.info("Loaded body from cache"); + handler.onResult(new BlogPostItem(header, body)); + return; + } + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + long now = System.currentTimeMillis(); + BlogPostItem item = getItem(header); + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Loading body took " + duration + " ms"); + handler.onResult(item); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + @Override + public void loadBlogPost(final GroupId g, final MessageId m, + final ResultExceptionHandler handler) { + + BlogPostHeader header = headerCache.get(m); + if (header != null) { + LOG.info("Loaded header from cache"); + loadBlogPost(header, handler); + return; + } + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + long now = System.currentTimeMillis(); + BlogPostHeader header = getPostHeader(g, m); + BlogPostItem item = getItem(header); + long duration = System.currentTimeMillis() - now; + if (LOG.isLoggable(INFO)) + LOG.info("Loading post took " + duration + " ms"); + handler.onResult(item); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + @Override + public void repeatPost(final BlogPostItem item, + final @Nullable String comment, + final ResultExceptionHandler handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + LocalAuthor a = identityManager.getLocalAuthor(); + Blog b = blogManager.getPersonalBlog(a); + BlogPostHeader h = item.getHeader(); + blogManager.addLocalComment(a, b.getId(), comment, h); + handler.onResult(null); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + private BlogPostHeader getPostHeader(GroupId g, MessageId m) + throws DbException { + + if (g == null) throw new IllegalStateException(); + BlogPostHeader header = headerCache.get(m); + if (header == null) { + header = blogManager.getPostHeader(g, m); + headerCache.put(m, header); + } + return header; + } + + private BlogPostItem getItem(BlogPostHeader h) throws DbException { + String body; + if (h instanceof BlogCommentHeader) { + BlogCommentHeader c = (BlogCommentHeader) h; + BlogCommentItem item = new BlogCommentItem(c); + body = getPostBody(item.getPostHeader().getId()); + item.setBody(body); + return item; + } else { + body = getPostBody(h.getId()); + return new BlogPostItem(h, body); + } + } + + private String getPostBody(MessageId m) throws DbException { + String body = bodyCache.get(m); + if (body == null) { + body = blogManager.getPostBody(m); + if (body != null) bodyCache.put(m, body); + } + //noinspection ConstantConditions + return body; + } + +} diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java index 7c3309010..7e14c9d8e 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java @@ -14,7 +14,7 @@ import android.widget.ProgressBar; import org.briarproject.R; import org.briarproject.android.ActivityComponent; import org.briarproject.android.BriarActivity; -import org.briarproject.android.blogs.BlogController.BlogPostListener; +import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener; @@ -33,16 +33,16 @@ import javax.inject.Inject; import static android.view.View.GONE; import static android.view.View.VISIBLE; -public class BlogActivity extends BriarActivity implements BlogPostListener, +public class BlogActivity extends BriarActivity implements + OnBlogPostAddedListener, 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"; + public static final String BLOG_NAME = "briar.BLOG_NAME"; static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG"; - private static final String POST_ID = "briar.POST_ID"; + public static final String POST_ID = "briar.POST_ID"; private GroupId groupId; private ProgressBar progressBar; @@ -50,7 +50,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener, private BlogPagerAdapter blogPagerAdapter; private BlogPostPagerAdapter postPagerAdapter; private String blogName; - private boolean myBlog, isNew; + private boolean isNew; private MessageId savedPostId; @Inject @@ -72,7 +72,6 @@ public class BlogActivity extends BriarActivity implements BlogPostListener, if (blogName != null) setTitle(blogName); // Is this our blog and was it just created? - myBlog = i.getBooleanExtra(IS_MY_BLOG, false); isNew = i.getBooleanExtra(IS_NEW_BLOG, false); setContentView(R.layout.activity_blog); @@ -254,7 +253,7 @@ public class BlogActivity extends BriarActivity implements BlogPostListener, @Override public Fragment getItem(int position) { - return BlogFragment.newInstance(groupId, blogName, myBlog, isNew); + return BlogFragment.newInstance(groupId, blogName, isNew); } @Override diff --git a/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java b/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java new file mode 100644 index 000000000..f499d1246 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java @@ -0,0 +1,63 @@ +package org.briarproject.android.blogs; + +import android.support.annotation.UiThread; + +import org.briarproject.api.blogs.BlogCommentHeader; +import org.briarproject.api.blogs.BlogPostHeader; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +@UiThread +class BlogCommentItem extends BlogPostItem { + + private final BlogPostHeader postHeader; + private final List comments = new ArrayList<>(); + + BlogCommentItem(BlogCommentHeader header) { + super(header, null); + postHeader = collectComments(header); + Collections.sort(comments, new BlogCommentComparator()); + } + + private BlogPostHeader collectComments(BlogPostHeader header) { + if (header instanceof BlogCommentHeader) { + BlogCommentHeader comment = (BlogCommentHeader) header; + if (comment.getComment() != null) + comments.add(comment); + return collectComments(comment.getParent()); + } else { + return header; + } + } + + public void setBody(String body) { + this.body = body; + } + + @Override + public BlogCommentHeader getHeader() { + return (BlogCommentHeader) super.getHeader(); + } + + @Override + BlogPostHeader getPostHeader() { + return postHeader; + } + + List getComments() { + return comments; + } + + private static class BlogCommentComparator + implements Comparator { + @Override + public int compare(org.briarproject.api.blogs.BlogCommentHeader h1, + org.briarproject.api.blogs.BlogCommentHeader h2) { + // re-use same comparator used for blog posts, but reverse it + return BlogCommentItem.compare(h2, h1); + } + } +} diff --git a/briar-android/src/org/briarproject/android/blogs/BlogController.java b/briar-android/src/org/briarproject/android/blogs/BlogController.java index 90fbb61cf..768d29c14 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogController.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogController.java @@ -11,26 +11,20 @@ import org.briarproject.api.sync.MessageId; import java.util.Collection; -public interface BlogController extends ActivityLifecycleController { +public interface BlogController extends BaseController { void setGroupId(GroupId g); void loadBlogPosts( ResultExceptionHandler, DbException> handler); - void loadBlogPost(BlogPostHeader header, - ResultExceptionHandler handler); - void loadBlogPost(MessageId m, ResultExceptionHandler handler); + void isMyBlog(ResultExceptionHandler handler); + void canDeleteBlog(ResultExceptionHandler handler); void deleteBlog(ResultExceptionHandler handler); - interface BlogPostListener { - @UiThread - void onBlogPostAdded(BlogPostHeader header, boolean local); - } - } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java index 9906e511e..a85b48734 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java @@ -1,70 +1,40 @@ package org.briarproject.android.blogs; -import android.app.Activity; - -import org.briarproject.android.api.AndroidNotificationManager; -import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.controller.ActivityLifecycleController; import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.api.blogs.Blog; -import org.briarproject.api.blogs.BlogManager; -import org.briarproject.api.blogs.BlogPostHeader; import org.briarproject.api.db.DbException; import org.briarproject.api.event.BlogPostAddedEvent; import org.briarproject.api.event.Event; -import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; import org.briarproject.api.event.GroupRemovedEvent; +import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import javax.inject.Inject; -import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -public class BlogControllerImpl extends DbControllerImpl - implements BlogController, EventListener { +public class BlogControllerImpl extends BaseControllerImpl + implements ActivityLifecycleController, BlogController, EventListener { private static final Logger LOG = Logger.getLogger(BlogControllerImpl.class.getName()); - @Inject - protected Activity activity; - @Inject - protected EventBus eventBus; - @Inject - protected AndroidNotificationManager notificationManager; - - @Inject - protected volatile BlogManager blogManager; - - private final Map bodyCache = new ConcurrentHashMap<>(); - private final Map headerCache = - new ConcurrentHashMap<>(); - - private volatile BlogPostListener listener; private volatile GroupId groupId = null; @Inject BlogControllerImpl() { } - @Override - public void setGroupId(GroupId g) { - groupId = g; - } - @Override public void onActivityCreate() { - if (activity instanceof BlogPostListener) { - listener = (BlogPostListener) activity; + if (activity instanceof OnBlogPostAddedListener) { + listener = (OnBlogPostAddedListener) activity; } else { throw new IllegalStateException( "An activity that injects the BlogController must " + @@ -74,34 +44,33 @@ public class BlogControllerImpl extends DbControllerImpl @Override public void onActivityResume() { + super.onStart(); notificationManager.blockNotification(groupId); notificationManager.clearBlogPostNotification(groupId); - eventBus.addListener(this); } @Override public void onActivityPause() { + super.onStop(); notificationManager.unblockNotification(groupId); - eventBus.removeListener(this); } @Override public void onActivityDestroy() { } + @Override + public void setGroupId(GroupId g) { + groupId = g; + } + @Override public void eventOccurred(Event e) { if (groupId == null) throw new IllegalStateException(); if (e instanceof BlogPostAddedEvent) { - final BlogPostAddedEvent m = (BlogPostAddedEvent) e; - if (m.getGroupId().equals(groupId)) { - LOG.info("New blog post added"); - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - listener.onBlogPostAdded(m.getHeader(), m.isLocal()); - } - }); + BlogPostAddedEvent s = (BlogPostAddedEvent) e; + if (s.getGroupId().equals(groupId)) { + super.eventOccurred(e); } } else if (e instanceof GroupRemovedEvent) { GroupRemovedEvent s = (GroupRemovedEvent) e; @@ -122,86 +91,27 @@ public class BlogControllerImpl extends DbControllerImpl public void loadBlogPosts( final ResultExceptionHandler, DbException> handler) { if (groupId == null) throw new IllegalStateException(); - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - long now = System.currentTimeMillis(); - Collection headers = - blogManager.getPostHeaders(groupId); - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Loading headers took " + duration + " ms"); - List items = new ArrayList<>(headers.size()); - now = System.currentTimeMillis(); - for (BlogPostHeader h : headers) { - headerCache.put(h.getId(), h); - byte[] body = getPostBody(h.getId()); - items.add(new BlogPostItem(groupId, h, body)); - } - duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Loading bodies took " + duration + " ms"); - handler.onResult(items); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - handler.onException(e); - } - } - }); - } - - @Override - public void loadBlogPost(final BlogPostHeader header, - final ResultExceptionHandler handler) { - if (groupId == null) throw new IllegalStateException(); - byte[] body = bodyCache.get(header.getId()); - if (body != null) { - LOG.info("Loaded body from cache"); - handler.onResult(new BlogPostItem(groupId, header, body)); - return; - } - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - long now = System.currentTimeMillis(); - byte[] body = getPostBody(header.getId()); - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Loading body took " + duration + " ms"); - handler.onResult(new BlogPostItem(groupId, header, body)); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - handler.onException(e); - } - } - }); + loadBlogPosts(groupId, handler); } @Override public void loadBlogPost(final MessageId m, final ResultExceptionHandler handler) { if (groupId == null) throw new IllegalStateException(); - BlogPostHeader header = headerCache.get(m); - if (header != null) { - LOG.info("Loaded header from cache"); - loadBlogPost(header, handler); - return; - } + loadBlogPost(groupId, m, handler); + } + + @Override + public void isMyBlog( + final ResultExceptionHandler handler) { + if (groupId == null) throw new IllegalStateException(); runOnDbThread(new Runnable() { @Override public void run() { try { - long now = System.currentTimeMillis(); - BlogPostHeader header = getPostHeader(m); - byte[] body = getPostBody(m); - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Loading post took " + duration + " ms"); - handler.onResult(new BlogPostItem(groupId, header, body)); + LocalAuthor a = identityManager.getLocalAuthor(); + Blog b = blogManager.getBlog(groupId); + handler.onResult(b.getAuthor().getId().equals(a.getId())); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -209,25 +119,7 @@ public class BlogControllerImpl extends DbControllerImpl } } }); - } - private BlogPostHeader getPostHeader(MessageId m) throws DbException { - if (groupId == null) throw new IllegalStateException(); - BlogPostHeader header = headerCache.get(m); - if (header == null) { - header = blogManager.getPostHeader(groupId, m); - headerCache.put(m, header); - } - return header; - } - - private byte[] getPostBody(MessageId m) throws DbException { - byte[] body = bodyCache.get(m); - if (body == null) { - body = blogManager.getPostBody(m); - if (body != null) bodyCache.put(m, body); - } - return body; } @Override diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java index 0e89542af..b3b87a113 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java @@ -19,7 +19,7 @@ import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.ActivityComponent; -import org.briarproject.android.blogs.BlogController.BlogPostListener; +import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.fragment.BaseFragment; @@ -42,12 +42,12 @@ import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.widget.Toast.LENGTH_SHORT; 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; -public class BlogFragment extends BaseFragment implements BlogPostListener { +public class BlogFragment extends BaseFragment implements + OnBlogPostAddedListener { public final static String TAG = BlogFragment.class.getName(); @@ -56,20 +56,18 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { private GroupId groupId; private String blogName; - private boolean myBlog; private BlogPostAdapter adapter; private BriarRecyclerView list; - private MenuItem deleteButton; + private MenuItem writeButton, deleteButton; static BlogFragment newInstance(GroupId groupId, String name, - boolean myBlog, boolean isNew) { + boolean isNew) { BlogFragment f = new BlogFragment(); Bundle bundle = new Bundle(); bundle.putByteArray(GROUP_ID, groupId.getBytes()); bundle.putString(BLOG_NAME, name); - bundle.putBoolean(IS_MY_BLOG, myBlog); bundle.putBoolean(IS_NEW_BLOG, isNew); f.setArguments(bundle); @@ -88,7 +86,6 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { if (b == null) throw new IllegalStateException("No group ID in args"); groupId = new GroupId(b); blogName = args.getString(BLOG_NAME); - myBlog = args.getBoolean(IS_MY_BLOG); boolean isNew = args.getBoolean(IS_NEW_BLOG); View v = inflater.inflate(R.layout.fragment_blog, container, false); @@ -99,12 +96,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { list.setLayoutManager(new LinearLayoutManager(getActivity())); list.setAdapter(adapter); list.showProgressBar(); - if (myBlog) { - list.setEmptyText( - getString(R.string.blogs_my_blogs_blog_empty_state)); - } else { - list.setEmptyText(getString(R.string.blogs_other_blog_empty_state)); - } + list.setEmptyText(getString(R.string.blogs_other_blog_empty_state)); // show snackbar if this blog was just created if (isNew) { @@ -128,7 +120,8 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { @Override public void onStart() { super.onStart(); - if (!myBlog) checkIfBlogCanBeDeleted(); + checkIfThisIsMyBlog(); + checkIfBlogCanBeDeleted(); } @Override @@ -146,13 +139,10 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { @Override 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); - deleteButton = menu.findItem(R.id.action_blog_delete); - deleteButton.setVisible(false); - } + inflater.inflate(R.menu.blogs_blog_actions, menu); + writeButton = menu.findItem(R.id.action_write_blog_post); + deleteButton = menu.findItem(R.id.action_blog_delete); + super.onCreateOptionsMenu(menu, inflater); } @@ -199,7 +189,9 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { public void onActivityResult(int request, int result, Intent data) { super.onActivityResult(request, result, data); - if (request == REQUEST_SHARE && result == RESULT_OK) { + if (request == REQUEST_WRITE_POST && result == RESULT_OK) { + displaySnackbar(R.string.blogs_blog_post_created); + } else if (request == REQUEST_SHARE && result == RESULT_OK) { displaySnackbar(R.string.blogs_sharing_snackbar); } } @@ -217,7 +209,12 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { @Override public void onResultUi(BlogPostItem post) { adapter.add(post); - if (local) list.scrollToPosition(0); + if (local) { + list.scrollToPosition(0); + displaySnackbar(R.string.blogs_blog_post_created); + } else { + displaySnackbar(R.string.blogs_blog_post_received); + } } @Override @@ -251,6 +248,25 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { }); } + private void checkIfThisIsMyBlog() { + blogController.canDeleteBlog( + new UiResultExceptionHandler( + getActivity()) { + @Override + public void onResultUi(Boolean isMyBlog) { + if (isMyBlog) { + showWriteButton(); + } + } + + @Override + public void onExceptionUi(DbException exception) { + // TODO: Decide how to handle errors in the UI + getActivity().finish(); + } + }); + } + private void checkIfBlogCanBeDeleted() { blogController.canDeleteBlog( new UiResultExceptionHandler( @@ -270,6 +286,11 @@ public class BlogFragment extends BaseFragment implements BlogPostListener { }); } + private void showWriteButton() { + if (writeButton != null) + writeButton.setVisible(true); + } + private void showDeleteButton() { if (deleteButton != null) deleteButton.setVisible(true); diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java index 92f9430c4..37f7dd301 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java @@ -1,7 +1,6 @@ package org.briarproject.android.blogs; import android.app.Activity; -import android.content.Context; import android.content.Intent; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; @@ -9,7 +8,6 @@ import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; import android.support.v7.util.SortedList; import android.support.v7.widget.RecyclerView; -import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -23,12 +21,10 @@ import org.briarproject.api.sync.GroupId; import java.util.Collection; -import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.view.View.GONE; import static android.view.View.VISIBLE; 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; class BlogListAdapter extends RecyclerView.Adapter { @@ -136,7 +132,6 @@ class BlogListAdapter extends Blog b = item.getBlog(); i.putExtra(GROUP_ID, b.getId().getBytes()); i.putExtra(BLOG_NAME, b.getName()); - i.putExtra(IS_MY_BLOG, item.isOurs()); ActivityOptionsCompat options = ActivityOptionsCompat .makeCustomAnimation(ctx, android.R.anim.fade_in, android.R.anim.fade_out); diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java index e6b2ee82e..badf03450 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostAdapter.java @@ -6,21 +6,15 @@ import android.support.v7.widget.RecyclerView; 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.util.AuthorView; -import org.briarproject.util.StringUtils; import java.util.Collection; -class BlogPostAdapter extends - RecyclerView.Adapter { +class BlogPostAdapter extends RecyclerView.Adapter { private SortedList posts = new SortedList<>( BlogPostItem.class, new SortedList.Callback() { - @Override public int compare(BlogPostItem a, BlogPostItem b) { return a.compareTo(b); @@ -56,7 +50,6 @@ class BlogPostAdapter extends return a.getId().equals(b.getId()); } }); - private final Context ctx; private final OnBlogPostClickListener listener; @@ -66,30 +59,18 @@ class BlogPostAdapter extends } @Override - public BlogPostHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public BlogPostViewHolder onCreateViewHolder(ViewGroup parent, + int viewType) { View v = LayoutInflater.from(ctx).inflate( R.layout.list_item_blog_post, parent, false); - return new BlogPostHolder(v); + BlogPostViewHolder ui = new BlogPostViewHolder(v); + ui.setOnBlogPostClickListener(listener); + return ui; } @Override - public void onBindViewHolder(final BlogPostHolder ui, int position) { - final BlogPostItem post = getItem(position); - - // author and date - ui.author.setAuthor(post.getAuthor()); - ui.author.setAuthorStatus(post.getAuthorStatus()); - ui.author.setDate(post.getTimestamp()); - - // post body - ui.body.setText(StringUtils.fromUtf8(post.getBody())); - - ui.layout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - listener.onBlogPostClick(post); - } - }); + public void onBindViewHolder(BlogPostViewHolder ui, int position) { + ui.bindItem(getItem(position)); } @Override @@ -121,23 +102,6 @@ class BlogPostAdapter extends return posts.size() == 0; } - static class BlogPostHolder extends RecyclerView.ViewHolder { - - private final ViewGroup layout; - private final AuthorView author; - private final ImageView comment; - private final TextView body; - - BlogPostHolder(View v) { - super(v); - - layout = (ViewGroup) v; - author = (AuthorView) v.findViewById(R.id.authorView); - comment = (ImageView) v.findViewById(R.id.commentView); - body = (TextView) v.findViewById(R.id.bodyView); - } - } - interface OnBlogPostClickListener { void onBlogPostClick(BlogPostItem post); } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java index f96deda0b..7e0cb1c95 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java @@ -19,7 +19,6 @@ import org.briarproject.android.util.TrustIndicatorView; import org.briarproject.api.db.DbException; import org.briarproject.api.identity.Author; import org.briarproject.api.sync.MessageId; -import org.briarproject.util.StringUtils; import java.util.logging.Logger; @@ -27,7 +26,6 @@ import javax.inject.Inject; import im.delight.android.identicons.IdenticonDrawable; -import static android.view.View.GONE; import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION; public class BlogPostFragment extends BaseFragment { @@ -135,11 +133,7 @@ public class BlogPostFragment extends BaseFragment { if (ctx != null) { ui.date.setText(AndroidUtils.formatDate(ctx, post.getTimestamp())); } - - // TODO remove #598 - ui.title.setVisibility(GONE); - - ui.body.setText(StringUtils.fromUtf8(post.getBody())); + ui.body.setText(post.getBody()); } private static class BlogPostViewHolder { @@ -148,7 +142,6 @@ public class BlogPostFragment extends BaseFragment { private final TextView authorName; private final TrustIndicatorView trust; private final TextView date; - private final TextView title; private final TextView body; private BlogPostViewHolder(View v) { @@ -156,7 +149,6 @@ public class BlogPostFragment extends BaseFragment { authorName = (TextView) v.findViewById(R.id.authorName); trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator); date = (TextView) v.findViewById(R.id.date); - title = (TextView) v.findViewById(R.id.title); body = (TextView) v.findViewById(R.id.body); } } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java index 55a439e2e..5d83843f5 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java @@ -12,16 +12,14 @@ import org.briarproject.api.sync.MessageId; // This class is not thread-safe class BlogPostItem implements Comparable { - private final GroupId groupId; private final BlogPostHeader header; - private final byte[] body; + protected String body; private boolean read; - BlogPostItem(GroupId groupId, BlogPostHeader header, byte[] body) { - this.groupId = groupId; + BlogPostItem(BlogPostHeader header, @Nullable String body) { this.header = header; this.body = body; - read = header.isRead(); + this.read = header.isRead(); } public MessageId getId() { @@ -29,17 +27,13 @@ class BlogPostItem implements Comparable { } public GroupId getGroupId() { - return groupId; + return header.getGroupId(); } public long getTimestamp() { return header.getTimestamp(); } - public long getTimeReceived() { - return header.getTimeReceived(); - } - public Author getAuthor() { return header.getAuthor(); } @@ -48,7 +42,7 @@ class BlogPostItem implements Comparable { return header.getAuthorStatus(); } - public byte[] getBody() { + public String getBody() { return body; } @@ -56,15 +50,23 @@ class BlogPostItem implements Comparable { return read; } - public void setRead(boolean read) { - this.read = read; + public BlogPostHeader getHeader() { + return header; + } + + BlogPostHeader getPostHeader() { + return getHeader(); } @Override public int compareTo(@NonNull BlogPostItem other) { if (this == other) return 0; + return compare(getHeader(), other.getHeader()); + } + + protected static int compare(BlogPostHeader h1, BlogPostHeader h2) { // The newest post comes first - long aTime = getTimeReceived(), bTime = other.getTimeReceived(); + long aTime = h1.getTimeReceived(), bTime = h2.getTimeReceived(); if (aTime > bTime) return -1; if (aTime < bTime) return 1; return 0; diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java new file mode 100644 index 000000000..535c85e26 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java @@ -0,0 +1,142 @@ +package org.briarproject.android.blogs; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +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.blogs.BlogPostAdapter.OnBlogPostClickListener; +import org.briarproject.android.util.AuthorView; +import org.briarproject.api.blogs.BlogCommentHeader; +import org.briarproject.api.blogs.BlogPostHeader; +import org.briarproject.api.identity.Author; + +import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static org.briarproject.android.BriarActivity.GROUP_ID; +import static org.briarproject.android.blogs.BlogActivity.POST_ID; +import static org.briarproject.api.blogs.MessageType.POST; + +public class BlogPostViewHolder extends RecyclerView.ViewHolder { + + private final Context ctx; + private OnBlogPostClickListener listener; + + private final ViewGroup layout; + private final AuthorView reblogger; + private final AuthorView author; + private final ImageView reblogButton; + private final TextView body; + private final ViewGroup commentContainer; + + BlogPostViewHolder(View v) { + super(v); + + ctx = v.getContext(); + layout = (ViewGroup) v; + reblogger = (AuthorView) v.findViewById(R.id.rebloggerView); + author = (AuthorView) v.findViewById(R.id.authorView); + reblogButton = (ImageView) v.findViewById(R.id.commentView); + body = (TextView) v.findViewById(R.id.bodyView); + commentContainer = + (ViewGroup) v.findViewById(R.id.commentContainer); + } + + void setOnBlogPostClickListener(OnBlogPostClickListener listener) { + this.listener = listener; + } + + void setVisibility(int visibility) { + layout.setVisibility(visibility); + } + + void hideReblogButton() { + reblogButton.setVisibility(GONE); + } + + void bindItem(final BlogPostItem item) { + // author and date + BlogPostHeader post = item.getPostHeader(); + Author a = post.getAuthor(); + author.setAuthor(a); + author.setAuthorStatus(post.getAuthorStatus()); + author.setDate(post.getTimestamp()); + // TODO make author clickable more often #624 + if (item.getHeader().getType() == POST) { + author.setBlogLink(post.getGroupId()); + } else { + author.unsetBlogLink(); + } + + // post body + body.setText(item.getBody()); + + // reblog button + reblogButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(ctx, ReblogActivity.class); + i.putExtra(GROUP_ID, item.getGroupId().getBytes()); + i.putExtra(POST_ID, item.getId().getBytes()); + ActivityOptionsCompat options = + makeCustomAnimation(ctx, android.R.anim.slide_in_left, + android.R.anim.slide_out_right); + Intent[] intents = { i }; + ContextCompat.startActivities(ctx, intents, options.toBundle()); + } + }); + + // comments + commentContainer.removeAllViews(); + if (item instanceof BlogCommentItem) { + onBindComment((BlogCommentItem) item); + } else { + reblogger.setVisibility(GONE); + } + + layout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null) { + listener.onBlogPostClick(item); + } + } + }); + } + + private void onBindComment(final BlogCommentItem item) { + // reblogger + reblogger.setAuthor(item.getAuthor()); + reblogger.setAuthorStatus(item.getAuthorStatus()); + reblogger.setDate(item.getTimestamp()); + reblogger.setBlogLink(item.getGroupId()); + reblogger.setVisibility(VISIBLE); + + // comments + for (BlogCommentHeader c : item.getComments()) { + View v = LayoutInflater.from(ctx) + .inflate(R.layout.list_item_blog_comment, + commentContainer, false); + + AuthorView author = (AuthorView) v.findViewById(R.id.authorView); + TextView body = (TextView) v.findViewById(R.id.bodyView); + + author.setAuthor(c.getAuthor()); + author.setAuthorStatus(c.getAuthorStatus()); + author.setDate(c.getTimestamp()); + // TODO make author clickable #624 + + body.setText(c.getComment()); + + commentContainer.addView(v); + } + } +} diff --git a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java deleted file mode 100644 index 80f234010..000000000 --- a/briar-android/src/org/briarproject/android/blogs/BlogsFragment.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.briarproject.android.blogs; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.briarproject.R; -import org.briarproject.android.ActivityComponent; -import org.briarproject.android.fragment.BaseFragment; - -import static android.view.View.GONE; - -public class BlogsFragment extends BaseFragment { - - public final static String TAG = BlogsFragment.class.getName(); - - private static final String SELECTED_TAB = "selectedTab"; - private TabLayout tabLayout; - - public static BlogsFragment newInstance() { - - Bundle args = new Bundle(); - - BlogsFragment fragment = new BlogsFragment(); - fragment.setArguments(args); - return fragment; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View v = inflater.inflate(R.layout.fragment_blogs, container, - false); - - tabLayout = (TabLayout) v.findViewById(R.id.tabLayout); - ViewPager viewPager = (ViewPager) v.findViewById(R.id.pager); - - String[] titles = { - getString(R.string.blogs_feed), - getString(R.string.blogs_my_blogs), - getString(R.string.blogs_blog_list), - getString(R.string.blogs_available_blogs), - getString(R.string.blogs_drafts) - }; - TabAdapter tabAdapter = - new TabAdapter(getChildFragmentManager(), titles); - viewPager.setAdapter(tabAdapter); - tabLayout.setupWithViewPager(viewPager); - - tabLayout.setVisibility(GONE); - - if (savedInstanceState != null) { - int position = savedInstanceState.getInt(SELECTED_TAB, 0); - viewPager.setCurrentItem(position); - } - return v; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putInt(SELECTED_TAB, tabLayout.getSelectedTabPosition()); - } - - @Override - public String getUniqueTag() { - return TAG; - } - - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - - - private static class TabAdapter extends FragmentStatePagerAdapter { - private String[] titles; - - TabAdapter(FragmentManager fm, String[] titles) { - super(fm); - this.titles = titles; - } - - @Override - public int getCount() { - return 1; -// return titles.length; - } - - @Override - public Fragment getItem(int position) { - return FeedFragment.newInstance(); -// switch (position) { -// case 0: -// return FeedFragment.newInstance(); -// case 1: -// return new MyBlogsFragment(); -// default: -// return BlogListFragment.newInstance(position); -// } - } - - @Override - public CharSequence getPageTitle(int position) { - return titles[position]; - } - } - -} diff --git a/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java index 980d67d1d..5bac574b5 100644 --- a/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/CreateBlogActivity.java @@ -15,7 +15,6 @@ import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; import org.briarproject.R; import org.briarproject.android.ActivityComponent; @@ -35,11 +34,9 @@ import javax.inject.Inject; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.view.View.GONE; import static android.view.View.VISIBLE; -import static android.widget.Toast.LENGTH_LONG; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; 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.api.blogs.BlogConstants.MAX_BLOG_DESC_LENGTH; import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_TITLE_LENGTH; @@ -180,7 +177,6 @@ public class CreateBlogActivity extends BriarActivity new Intent(CreateBlogActivity.this, BlogActivity.class); i.putExtra(GROUP_ID, b.getId().getBytes()); i.putExtra(BLOG_NAME, b.getName()); - i.putExtra(IS_MY_BLOG, true); i.putExtra(IS_NEW_BLOG, true); ActivityOptionsCompat options = makeCustomAnimation(CreateBlogActivity.this, diff --git a/briar-android/src/org/briarproject/android/blogs/FeedController.java b/briar-android/src/org/briarproject/android/blogs/FeedController.java index d449eb60b..e2934f49f 100644 --- a/briar-android/src/org/briarproject/android/blogs/FeedController.java +++ b/briar-android/src/org/briarproject/android/blogs/FeedController.java @@ -1,24 +1,17 @@ package org.briarproject.android.blogs; +import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.controller.handler.ResultHandler; import org.briarproject.api.blogs.Blog; +import org.briarproject.api.db.DbException; import java.util.Collection; -public interface FeedController { +public interface FeedController extends BaseController { - void onResume(); - - void onPause(); - - void loadPosts(ResultHandler> resultHandler); + void loadBlogPosts( + ResultExceptionHandler, DbException> handler); void loadPersonalBlog(ResultHandler resultHandler); - void setOnBlogPostAddedListener(OnBlogPostAddedListener listener); - - interface OnBlogPostAddedListener { - void onBlogPostAdded(final BlogPostItem post); - } - } diff --git a/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java index 7c093598b..50074c0a7 100644 --- a/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java +++ b/briar-android/src/org/briarproject/android/blogs/FeedControllerImpl.java @@ -1,19 +1,12 @@ package org.briarproject.android.blogs; -import org.briarproject.android.api.AndroidNotificationManager; -import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.controller.handler.ResultHandler; import org.briarproject.api.blogs.Blog; -import org.briarproject.api.blogs.BlogManager; -import org.briarproject.api.blogs.BlogPostHeader; import org.briarproject.api.db.DbException; -import org.briarproject.api.event.BlogPostAddedEvent; -import org.briarproject.api.event.Event; -import org.briarproject.api.event.EventBus; -import org.briarproject.api.event.EventListener; +import org.briarproject.api.db.NoSuchGroupException; +import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.identity.Author; -import org.briarproject.api.identity.IdentityManager; -import org.briarproject.api.sync.GroupId; import java.util.ArrayList; import java.util.Collection; @@ -24,82 +17,56 @@ import javax.inject.Inject; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; -public class FeedControllerImpl extends DbControllerImpl - implements FeedController, EventListener { +public class FeedControllerImpl extends BaseControllerImpl + implements FeedController { private static final Logger LOG = Logger.getLogger(FeedControllerImpl.class.getName()); - @SuppressWarnings("WeakerAccess") - @Inject - AndroidNotificationManager notificationManager; - @Inject - protected EventBus eventBus; - - @Inject - protected volatile BlogManager blogManager; - @Inject - protected volatile IdentityManager identityManager; - - private volatile OnBlogPostAddedListener listener; - @Inject FeedControllerImpl() { } @Override - public void onResume() { + public void onStart() { + super.onStart(); notificationManager.blockAllBlogPostNotifications(); notificationManager.clearAllBlogPostNotifications(); - eventBus.addListener(this); } @Override - public void onPause() { + public void onStop() { + super.onStop(); notificationManager.unblockAllBlogPostNotifications(); - eventBus.removeListener(this); } @Override - public void eventOccurred(Event e) { - if (!(e instanceof BlogPostAddedEvent)) return; - - LOG.info("New blog post added"); - if (listener != null) { - BlogPostAddedEvent m = (BlogPostAddedEvent) e; - BlogPostHeader header = m.getHeader(); - addPost(m.getGroupId(), header); - } - } - - @Override - public void loadPosts( - final ResultHandler> resultHandler) { - - LOG.info("Loading blog posts..."); + public void loadBlogPosts( + final ResultExceptionHandler, DbException> handler) { + LOG.info("Loading all blog posts..."); runOnDbThread(new Runnable() { @Override public void run() { - Collection posts = new ArrayList<>(); try { // load blog posts long now = System.currentTimeMillis(); + Collection posts = new ArrayList<>(); for (Blog b : blogManager.getBlogs()) { - Collection header = - blogManager.getPostHeaders(b.getId()); - for (BlogPostHeader h : header) { - byte[] body = blogManager.getPostBody(h.getId()); - posts.add(new BlogPostItem(b.getId(), h, body)); + try { + posts.addAll(loadItems(b.getId())); + } catch (NoSuchGroupException | NoSuchMessageException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); } } long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) - LOG.info("Loading posts took " + duration + " ms"); - resultHandler.onResult(posts); + LOG.info("Loading all posts took " + duration + " ms"); + handler.onResult(posts); } catch (DbException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - resultHandler.onResult(null); + handler.onException(e); } } }); @@ -114,8 +81,7 @@ public class FeedControllerImpl extends DbControllerImpl try { // load blog posts long now = System.currentTimeMillis(); - Author a = - identityManager.getLocalAuthors().iterator().next(); + Author a = identityManager.getLocalAuthor(); Blog b = blogManager.getPersonalBlog(a); long duration = System.currentTimeMillis() - now; if (LOG.isLoggable(INFO)) @@ -130,25 +96,4 @@ public class FeedControllerImpl extends DbControllerImpl }); } - @Override - public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) { - this.listener = listener; - } - - private void addPost(final GroupId groupId, final BlogPostHeader header) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - byte[] body = blogManager.getPostBody(header.getId()); - BlogPostItem post = new BlogPostItem(groupId, header, body); - listener.onBlogPostAdded(post); - } catch (DbException ex) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, ex.toString(), ex); - } - } - }); - } - } diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java index a83f9beb0..9e6f57748 100644 --- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java @@ -17,11 +17,15 @@ import android.view.ViewGroup; import org.briarproject.R; import org.briarproject.android.ActivityComponent; +import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultHandler; import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.blogs.Blog; +import org.briarproject.api.blogs.BlogPostHeader; +import org.briarproject.api.db.DbException; import java.util.Collection; @@ -32,11 +36,10 @@ import static android.support.design.widget.Snackbar.LENGTH_LONG; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; 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.REQUEST_WRITE_POST; public class FeedFragment extends BaseFragment implements - OnBlogPostClickListener, FeedController.OnBlogPostAddedListener { + OnBlogPostClickListener, OnBlogPostAddedListener { public final static String TAG = FeedFragment.class.getName(); @@ -48,7 +51,7 @@ public class FeedFragment extends BaseFragment implements private BriarRecyclerView list; private Blog personalBlog = null; - static FeedFragment newInstance() { + public static FeedFragment newInstance() { FeedFragment f = new FeedFragment(); Bundle args = new Bundle(); @@ -95,6 +98,7 @@ public class FeedFragment extends BaseFragment implements @Override public void onStart() { super.onStart(); + feedController.onStart(); feedController.loadPersonalBlog( new UiResultHandler(getActivity()) { @Override @@ -102,33 +106,30 @@ public class FeedFragment extends BaseFragment implements personalBlog = b; } }); - } - - @Override - public void onResume() { - super.onResume(); - list.startPeriodicUpdate(); - feedController.onResume(); - feedController.loadPosts( - new UiResultHandler>(getActivity()) { + feedController.loadBlogPosts( + new UiResultExceptionHandler, DbException>( + getActivity()) { @Override public void onResultUi(Collection posts) { - if (posts == null) { - // TODO show error? - } else if (posts.isEmpty()) { + if (posts.isEmpty()) { list.showData(); } else { adapter.addAll(posts); } } + @Override + public void onExceptionUi(DbException exception) { + // TODO + } }); + list.startPeriodicUpdate(); } @Override - public void onPause() { - super.onPause(); + public void onStop() { + super.onStop(); + feedController.onStop(); list.stopPeriodicUpdate(); - feedController.onPause(); // TODO save list position in database/preferences? } @@ -171,33 +172,30 @@ public class FeedFragment extends BaseFragment implements } @Override - public void onBlogPostAdded(final BlogPostItem post) { - listener.runOnUiThread(new Runnable() { - @Override - public void run() { - adapter.add(post); - showSnackBar(R.string.blogs_blog_post_received); - } - }); + public void onBlogPostAdded(BlogPostHeader header, final boolean local) { + feedController.loadBlogPost(header, + new UiResultExceptionHandler( + getActivity()) { + @Override + public void onResultUi(BlogPostItem post) { + adapter.add(post); + if (local) { + showSnackBar(R.string.blogs_blog_post_created); + } else { + showSnackBar(R.string.blogs_blog_post_received); + } + } + @Override + public void onExceptionUi(DbException exception) { + // TODO: Decide how to handle errors in the UI + } + } + ); } @Override public void onBlogPostClick(BlogPostItem post) { - byte[] groupId = post.getGroupId().getBytes(); - String name = getString(R.string.blogs_personal_blog, - post.getAuthor().getName()); - boolean myBlog = personalBlog != null && - personalBlog.getId().equals(post.getGroupId()); - - Intent i = new Intent(getActivity(), BlogActivity.class); - i.putExtra(GROUP_ID, groupId); - i.putExtra(BLOG_NAME, name); - i.putExtra(IS_MY_BLOG, myBlog); - ActivityOptionsCompat options = - makeCustomAnimation(getActivity(), - android.R.anim.slide_in_left, - android.R.anim.slide_out_right); - startActivity(i, options.toBundle()); + // TODO Open detail view of post } @Override @@ -228,5 +226,4 @@ public class FeedFragment extends BaseFragment implements } s.show(); } - } diff --git a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java b/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java deleted file mode 100644 index 97f33ebf4..000000000 --- a/briar-android/src/org/briarproject/android/blogs/MyBlogsFragment.java +++ /dev/null @@ -1,169 +0,0 @@ -package org.briarproject.android.blogs; - -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import org.briarproject.R; -import org.briarproject.android.ActivityComponent; -import org.briarproject.android.fragment.BaseFragment; -import org.briarproject.android.util.BriarRecyclerView; -import org.briarproject.api.blogs.Blog; -import org.briarproject.api.blogs.BlogManager; -import org.briarproject.api.blogs.BlogPostHeader; -import org.briarproject.api.db.DbException; -import org.briarproject.api.db.NoSuchGroupException; -import org.briarproject.api.identity.IdentityManager; -import org.briarproject.api.identity.LocalAuthor; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; - -public class MyBlogsFragment extends BaseFragment { - - public final static String TAG = MyBlogsFragment.class.getName(); - - private static final Logger LOG = Logger.getLogger(TAG); - private BriarRecyclerView list; - private BlogListAdapter adapter; - - // Fields that are accessed from background threads must be volatile - @Inject - protected volatile IdentityManager identityManager; - @Inject - volatile BlogManager blogManager; - - @Inject - public MyBlogsFragment() { - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - setHasOptionsMenu(true); - - adapter = new BlogListAdapter(getActivity()); - - list = (BriarRecyclerView) inflater - .inflate(R.layout.fragment_blogs_my, container, false); - list.setLayoutManager(new LinearLayoutManager(getActivity())); - list.setAdapter(adapter); - list.setEmptyText(getString(R.string.blogs_my_blogs_empty_state)); - - return list; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - listener.getActivityComponent().inject(this); - // Starting from here, we can use injected objects - } - - @Override - public void onResume() { - super.onResume(); - adapter.clear(); - list.showProgressBar(); - loadBlogs(); - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.blogs_my_actions, menu); - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - // Handle presses on the action bar items - switch (item.getItemId()) { - case R.id.action_create_blog: - Intent intent = - new Intent(getContext(), CreateBlogActivity.class); - ActivityOptionsCompat options = - makeCustomAnimation(getActivity(), - android.R.anim.slide_in_left, - android.R.anim.slide_out_right); - ActivityCompat.startActivity(getActivity(), intent, - options.toBundle()); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public String getUniqueTag() { - return TAG; - } - - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - - private void loadBlogs() { - listener.runOnDbThread(new Runnable() { - @Override - public void run() { - try { - // load blogs - long now = System.currentTimeMillis(); - Collection blogs = new ArrayList<>(); - Collection authors = - identityManager.getLocalAuthors(); - LocalAuthor a = authors.iterator().next(); - for (Blog b : blogManager.getBlogs(a)) { - try { - Collection headers = - blogManager.getPostHeaders(b.getId()); - blogs.add(new BlogListItem(b, headers, true)); - } catch (NoSuchGroupException e) { - // Continue - } - } - displayBlogs(blogs); - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Full blog load took " + duration + " ms"); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void displayBlogs(final Collection items) { - listener.runOnUiThread(new Runnable() { - @Override - public void run() { - if (items.size() == 0) { - list.showData(); - } else { - adapter.addAll(items); - } - } - }); - } - -} diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java new file mode 100644 index 000000000..9aade027d --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java @@ -0,0 +1,73 @@ +package org.briarproject.android.blogs; + +import android.content.Intent; +import android.os.Bundle; +import android.view.MenuItem; + +import org.briarproject.R; +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.BriarActivity; +import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; + +import static org.briarproject.android.blogs.BlogActivity.POST_ID; + +public class ReblogActivity extends BriarActivity implements + BaseFragmentListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + byte[] groupId = intent.getByteArrayExtra(GROUP_ID); + if (groupId == null) + throw new IllegalArgumentException("No group ID in intent"); + byte[] postId = intent.getByteArrayExtra(POST_ID); + if (postId == null) + throw new IllegalArgumentException("No post message ID in intent"); + + setContentView(R.layout.activity_fragment_container); + + if (savedInstanceState == null) { + ReblogFragment f = ReblogFragment + .newInstance(new GroupId(groupId), new MessageId(postId)); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragmentContainer, f) + .commit(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void injectActivity(ActivityComponent component) { + component.inject(this); + } + + @Override + public void showLoadingScreen(boolean isBlocking, int stringId) { + // this is handled by the fragment + } + + @Override + public void hideLoadingScreen() { + // this is handled by the fragment + } + + @Override + public void onFragmentCreated(String tag) { + + } +} diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java new file mode 100644 index 000000000..367dd7041 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java @@ -0,0 +1,196 @@ +package org.briarproject.android.blogs; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.ScrollView; + +import org.briarproject.R; +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; +import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; + +import javax.inject.Inject; + +import static android.view.View.FOCUS_DOWN; +import static android.view.View.GONE; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static org.briarproject.android.BriarActivity.GROUP_ID; +import static org.briarproject.android.blogs.BlogActivity.POST_ID; + +public class ReblogFragment extends BaseFragment { + + public static final String TAG = ReblogFragment.class.getName(); + + + private BaseFragmentListener listener; + private ViewHolder ui; + private GroupId blogId; + private MessageId postId; + private BlogPostItem item; + + @Inject + FeedController feedController; + + static ReblogFragment newInstance(GroupId groupId, MessageId messageId) { + ReblogFragment f = new ReblogFragment(); + + Bundle args = new Bundle(); + args.putByteArray(GROUP_ID, groupId.getBytes()); + args.putByteArray(POST_ID, messageId.getBytes()); + f.setArguments(args); + + return f; + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + try { + listener = (BaseFragmentListener) context; + } catch (ClassCastException e) { + throw new ClassCastException( + "Using class must implement BaseFragmentListener"); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + setHasOptionsMenu(true); + + Bundle args = getArguments(); + blogId = new GroupId(args.getByteArray(GROUP_ID)); + postId = new MessageId(args.getByteArray(POST_ID)); + + View v = inflater.inflate(R.layout.fragment_reblog_dialog, container, + false); + ui = new ViewHolder(v); + showProgressBar(); + + return v; + } + + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + listener.getActivityComponent().inject(this); + } + + @Override + public void onStart() { + super.onStart(); + + feedController.loadBlogPost(blogId, postId, + new UiResultExceptionHandler( + getActivity()) { + @Override + public void onResultUi(BlogPostItem result) { + item = result; + bindViewHolder(); + } + @Override + public void onExceptionUi(DbException exception) { + // TODO + finish(); + } + }); + } + + private void bindViewHolder() { + if (item == null) return; + + ui.post.bindItem(item); + ui.post.hideReblogButton(); + + ui.publish.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + send(); + finish(); + } + }); + ui.publish.setEnabled(true); + hideProgressBar(); + ui.scrollView.post(new Runnable() { + @Override + public void run() { + //ui.scrollView.scrollTo(0, ui.scrollView.getBottom()); + ui.scrollView.fullScroll(FOCUS_DOWN); + } + }); + } + + private void send() { + String comment = getComment(); + feedController.repeatPost(item, comment, + new UiResultExceptionHandler(getActivity()) { + @Override + public void onResultUi(Void result) { + // do nothing, this fragment is gone already + } + @Override + public void onExceptionUi(DbException exception) { + // do nothing, this fragment is gone already + } + }); + } + + @Nullable + private String getComment() { + if (ui.input.getText().length() == 0) return null; + return ui.input.getText().toString(); + } + + private void showProgressBar() { + ui.progressBar.setVisibility(VISIBLE); + ui.post.setVisibility(GONE); + ui.input.setVisibility(GONE); + ui.publish.setVisibility(GONE); + } + + private void hideProgressBar() { + ui.progressBar.setVisibility(INVISIBLE); + ui.post.setVisibility(VISIBLE); + ui.input.setVisibility(VISIBLE); + ui.publish.setVisibility(VISIBLE); + } + + private static class ViewHolder { + private final ScrollView scrollView; + private final ProgressBar progressBar; + private final BlogPostViewHolder post; + private final EditText input; + private final Button publish; + + private ViewHolder(View v) { + scrollView = (ScrollView) v.findViewById(R.id.scrollView); + progressBar = (ProgressBar) v.findViewById(R.id.progressBar); + post = new BlogPostViewHolder(v.findViewById(R.id.postLayout)); + input = (EditText) v.findViewById(R.id.inputText); + publish = (Button) v.findViewById(R.id.publishButton); + } + } +} diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java index 75d480bfb..a2cf53646 100644 --- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java +++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java @@ -39,6 +39,10 @@ public abstract class BaseFragment extends Fragment { listener.onFragmentCreated(getUniqueTag()); } + protected void finish() { + getActivity().supportFinishAfterTransition(); + } + public interface BaseFragmentListener { void showLoadingScreen(boolean isBlocking, int stringId); diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java index f2227d826..3c14628eb 100644 --- a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java +++ b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java @@ -32,12 +32,12 @@ public class IntroductionActivity extends BriarActivity implements if (contactId == -1) throw new IllegalArgumentException("Wrong ContactId"); - setContentView(R.layout.activity_introduction); + setContentView(R.layout.activity_fragment_container); if (savedInstanceState == null) { getSupportFragmentManager() .beginTransaction() - .add(R.id.introductionContainer, + .add(R.id.fragmentContainer, ContactChooserFragment.newInstance()) .commit(); } @@ -109,7 +109,7 @@ public class IntroductionActivity extends BriarActivity implements android.R.anim.slide_in_left, android.R.anim.slide_out_right) .addSharedElement(view, "avatar") - .replace(R.id.introductionContainer, messageFragment, + .replace(R.id.fragmentContainer, messageFragment, ContactChooserFragment.TAG) .addToBackStack(null) .commit(); diff --git a/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java index 82fca3e36..268b16b85 100644 --- a/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java +++ b/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java @@ -343,12 +343,12 @@ public class ShowQrCodeFragment extends BaseEventFragment }); } - private void finish() { + @Override + protected void finish() { getActivity().getSupportFragmentManager().popBackStack(); } private class BluetoothStateReceiver extends BroadcastReceiver { - @Override public void onReceive(Context ctx, Intent intent) { int state = intent.getIntExtra(EXTRA_STATE, 0); diff --git a/briar-android/src/org/briarproject/android/util/AuthorView.java b/briar-android/src/org/briarproject/android/util/AuthorView.java index 51fb7debe..d84fb7f1b 100644 --- a/briar-android/src/org/briarproject/android/util/AuthorView.java +++ b/briar-android/src/org/briarproject/android/util/AuthorView.java @@ -1,29 +1,44 @@ package org.briarproject.android.util; import android.content.Context; +import android.content.Intent; +import android.content.res.TypedArray; import android.graphics.Typeface; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.content.ContextCompat; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import org.briarproject.R; +import org.briarproject.android.blogs.BlogActivity; import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author.Status; +import org.briarproject.api.sync.GroupId; import de.hdodenhof.circleimageview.CircleImageView; import im.delight.android.identicons.IdenticonDrawable; +import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; +import static android.util.TypedValue.COMPLEX_UNIT_PX; +import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.api.identity.Author.Status.OURSELVES; public class AuthorView extends RelativeLayout { private final CircleImageView avatar; + private final ImageView avatarIcon; private final TextView authorName; private final TextView date; private final TrustIndicatorView trustIndicator; - public AuthorView(Context context, AttributeSet attrs) { + public AuthorView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); LayoutInflater inflater = (LayoutInflater) context @@ -32,9 +47,16 @@ public class AuthorView extends RelativeLayout { .inflate(R.layout.author_view, this, true); avatar = (CircleImageView) findViewById(R.id.avatar); + avatarIcon = (ImageView) findViewById(R.id.avatarIcon); authorName = (TextView) findViewById(R.id.authorName); date = (TextView) findViewById(R.id.dateView); trustIndicator = (TrustIndicatorView) findViewById(R.id.trustIndicator); + + TypedArray attributes = + context.obtainStyledAttributes(attrs, R.styleable.AuthorView); + int persona = attributes.getInteger(R.styleable.AuthorView_persona, 0); + setPersona(persona); + attributes.recycle(); } public AuthorView(Context context) { @@ -45,6 +67,9 @@ public class AuthorView extends RelativeLayout { authorName.setText(author.getName()); IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes()); avatar.setImageDrawable(d); + + invalidate(); + requestLayout(); } public void setAuthorStatus(Status status) { @@ -59,6 +84,60 @@ public class AuthorView extends RelativeLayout { public void setDate(long date) { this.date.setText(AndroidUtils.formatDate(getContext(), date)); + + invalidate(); + requestLayout(); + } + + public void setBlogLink(final GroupId groupId) { + setClickable(true); + TypedValue outValue = new TypedValue(); + getContext().getTheme() + .resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + setBackgroundResource(outValue.resourceId); + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent i = new Intent(getContext(), BlogActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + ActivityOptionsCompat options = + makeCustomAnimation(getContext(), + android.R.anim.slide_in_left, + android.R.anim.slide_out_right); + Intent[] intents = {i}; + ContextCompat + .startActivities(getContext(), intents, + options.toBundle()); + } + }); + } + + public void unsetBlogLink() { + setClickable(false); + setBackgroundResource(android.R.color.transparent); + setOnClickListener(null); + } + + private void setPersona(int persona) { + switch (persona) { + // reblogger + case 1: + avatarIcon.setVisibility(VISIBLE); + break; + // commenter + case 2: + ViewGroup.LayoutParams params = avatar.getLayoutParams(); + int size = getResources().getDimensionPixelSize( + R.dimen.blogs_avatar_comment_size); + params.height = size; + params.width = size; + avatar.setLayoutParams(params); + float textSize = getResources() + .getDimensionPixelSize(R.dimen.text_size_tiny); + authorName.setTextSize(COMPLEX_UNIT_PX, textSize); + break; + } } } diff --git a/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java index d1a663e63..97d8e7763 100644 --- a/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java +++ b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java @@ -45,6 +45,9 @@ public class TrustIndicatorView extends ImageView { } setImageDrawable(ContextCompat.getDrawable(getContext(), res)); setVisibility(VISIBLE); + + invalidate(); + requestLayout(); } } diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java index ac8ea516b..2637779d4 100644 --- a/briar-api/src/org/briarproject/api/blogs/BlogManager.java +++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java @@ -56,7 +56,7 @@ public interface BlogManager { BlogPostHeader getPostHeader(GroupId g, MessageId m) throws DbException; /** Returns the body of the blog post with the given ID. */ - byte[] getPostBody(MessageId m) throws DbException; + String getPostBody(MessageId m) throws DbException; /** Returns the headers of all posts in the given blog. */ Collection getPostHeaders(GroupId g) throws DbException; diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java index 2720910e0..92f7e9c8a 100644 --- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java +++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java @@ -516,7 +516,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, } @Override - public byte[] getPostBody(MessageId m) throws DbException { + public String getPostBody(MessageId m) throws DbException { try { BdfList message = clientHelper.getMessageAsList(m); if (message == null) throw new DbException(); @@ -526,15 +526,14 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, } } - // TODO directly return String (#598) - private byte[] getPostBody(BdfList message) throws FormatException { + private String getPostBody(BdfList message) throws FormatException { MessageType type = MessageType.valueOf(message.getLong(0).intValue()); if (type == POST) { // type, body, signature - return StringUtils.toUtf8(message.getString(1)); + return message.getString(1); } else if (type == WRAPPED_POST) { // type, p_group descriptor, p_timestamp, p_content, p_signature - return StringUtils.toUtf8(message.getString(3)); + return message.getString(3); } else { throw new FormatException(); } From bb0a2a5b323a7ab6841777ccb255d4cc0a409f5f Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Thu, 1 Sep 2016 18:22:06 -0300 Subject: [PATCH 4/5] Add a scene transition animation when reblogging a blog post --- ..._reblog_dialog.xml => fragment_reblog.xml} | 12 +++--- .../android/blogs/BlogPostViewHolder.java | 43 ++++++++++++------- .../android/blogs/ReblogActivity.java | 19 ++++++++ .../android/blogs/ReblogFragment.java | 6 +-- 4 files changed, 55 insertions(+), 25 deletions(-) rename briar-android/res/layout/{fragment_reblog_dialog.xml => fragment_reblog.xml} (100%) diff --git a/briar-android/res/layout/fragment_reblog_dialog.xml b/briar-android/res/layout/fragment_reblog.xml similarity index 100% rename from briar-android/res/layout/fragment_reblog_dialog.xml rename to briar-android/res/layout/fragment_reblog.xml index 28b59e7b9..b17143b35 100644 --- a/briar-android/res/layout/fragment_reblog_dialog.xml +++ b/briar-android/res/layout/fragment_reblog.xml @@ -11,6 +11,12 @@ android:layout_height="wrap_content" android:padding="@dimen/margin_small"> + + - - = LOLLIPOP) { + setTransition(); + } + Intent intent = getIntent(); byte[] groupId = intent.getByteArrayExtra(GROUP_ID); if (groupId == null) @@ -70,4 +79,14 @@ public class ReblogActivity extends BriarActivity implements public void onFragmentCreated(String tag) { } + + @TargetApi(LOLLIPOP) + private void setTransition() { + Transition fade = new Fade(); + fade.excludeTarget(android.R.id.statusBarBackground, true); + fade.excludeTarget(R.id.action_bar_container, true); + fade.excludeTarget(android.R.id.navigationBarBackground, true); + getWindow().setExitTransition(fade); + getWindow().setEnterTransition(fade); + } } diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java index 367dd7041..119290bb2 100644 --- a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java @@ -85,9 +85,10 @@ public class ReblogFragment extends BaseFragment { blogId = new GroupId(args.getByteArray(GROUP_ID)); postId = new MessageId(args.getByteArray(POST_ID)); - View v = inflater.inflate(R.layout.fragment_reblog_dialog, container, + View v = inflater.inflate(R.layout.fragment_reblog, container, false); ui = new ViewHolder(v); + ui.post.setTransitionName(postId); showProgressBar(); return v; @@ -137,7 +138,6 @@ public class ReblogFragment extends BaseFragment { ui.scrollView.post(new Runnable() { @Override public void run() { - //ui.scrollView.scrollTo(0, ui.scrollView.getBottom()); ui.scrollView.fullScroll(FOCUS_DOWN); } }); @@ -166,14 +166,12 @@ public class ReblogFragment extends BaseFragment { private void showProgressBar() { ui.progressBar.setVisibility(VISIBLE); - ui.post.setVisibility(GONE); ui.input.setVisibility(GONE); ui.publish.setVisibility(GONE); } private void hideProgressBar() { ui.progressBar.setVisibility(INVISIBLE); - ui.post.setVisibility(VISIBLE); ui.input.setVisibility(VISIBLE); ui.publish.setVisibility(VISIBLE); } From bb00412187b87ea383d455f2acb0ecd3d84dad64 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 2 Sep 2016 11:29:26 +0100 Subject: [PATCH 5/5] Added @UiThread annotations, minor code cleanups. --- .idea/codeStyleSettings.xml | 28 +++++++++++++++++++ .../android/blogs/BaseController.java | 2 ++ .../android/blogs/BlogActivity.java | 4 +-- .../android/blogs/BlogCommentItem.java | 10 ++++--- .../android/blogs/BlogControllerImpl.java | 6 ++-- .../android/blogs/BlogPostItem.java | 3 +- .../android/blogs/BlogPostViewHolder.java | 8 ++++-- .../android/blogs/ReblogActivity.java | 7 ++--- .../android/blogs/ReblogFragment.java | 8 ++++-- .../android/fragment/BaseFragment.java | 6 ++++ .../briarproject/android/util/AuthorView.java | 18 ++++++------ 11 files changed, 71 insertions(+), 29 deletions(-) diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index 0677e45db..2feaa4e95 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -37,6 +37,34 @@ + + diff --git a/briar-android/src/org/briarproject/android/blogs/BaseController.java b/briar-android/src/org/briarproject/android/blogs/BaseController.java index e81c44d60..a7cf1e424 100644 --- a/briar-android/src/org/briarproject/android/blogs/BaseController.java +++ b/briar-android/src/org/briarproject/android/blogs/BaseController.java @@ -13,8 +13,10 @@ import java.util.Collection; public interface BaseController { + @UiThread void onStart(); + @UiThread void onStop(); void loadBlogPosts(GroupId g, diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java index 7e14c9d8e..249d0ff7c 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java @@ -67,11 +67,11 @@ public class BlogActivity extends BriarActivity implements groupId = new GroupId(b); blogController.setGroupId(groupId); - // Name of the Blog from Intent + // Name of the blog blogName = i.getStringExtra(BLOG_NAME); if (blogName != null) setTitle(blogName); - // Is this our blog and was it just created? + // Was this blog just created? isNew = i.getBooleanExtra(IS_NEW_BLOG, false); setContentView(R.layout.activity_blog); diff --git a/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java b/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java index f499d1246..b3dea0b8c 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogCommentItem.java @@ -13,13 +13,16 @@ import java.util.List; @UiThread class BlogCommentItem extends BlogPostItem { + private static final BlogCommentComparator COMPARATOR = + new BlogCommentComparator(); + private final BlogPostHeader postHeader; private final List comments = new ArrayList<>(); BlogCommentItem(BlogCommentHeader header) { super(header, null); postHeader = collectComments(header); - Collections.sort(comments, new BlogCommentComparator()); + Collections.sort(comments, COMPARATOR); } private BlogPostHeader collectComments(BlogPostHeader header) { @@ -54,10 +57,9 @@ class BlogCommentItem extends BlogPostItem { private static class BlogCommentComparator implements Comparator { @Override - public int compare(org.briarproject.api.blogs.BlogCommentHeader h1, - org.briarproject.api.blogs.BlogCommentHeader h2) { + public int compare(BlogCommentHeader h1, BlogCommentHeader h2) { // re-use same comparator used for blog posts, but reverse it - return BlogCommentItem.compare(h2, h1); + return BlogPostItem.compare(h2, h1); } } } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java index a85b48734..d70779e34 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java @@ -38,20 +38,20 @@ public class BlogControllerImpl extends BaseControllerImpl } else { throw new IllegalStateException( "An activity that injects the BlogController must " + - "implement the BlogPostListener"); + "implement the OnBlogPostAddedListener"); } } @Override public void onActivityResume() { - super.onStart(); + super.onStart(); // TODO: Should be called when activity starts. #609 notificationManager.blockNotification(groupId); notificationManager.clearBlogPostNotification(groupId); } @Override public void onActivityPause() { - super.onStop(); + super.onStop(); // TODO: Should be called when activity stops. #609 notificationManager.unblockNotification(groupId); } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java index 5d83843f5..747b66159 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostItem.java @@ -2,6 +2,7 @@ package org.briarproject.android.blogs; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.UiThread; import org.briarproject.api.blogs.BlogPostHeader; import org.briarproject.api.identity.Author; @@ -9,7 +10,7 @@ import org.briarproject.api.identity.Author.Status; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; -// This class is not thread-safe +@UiThread class BlogPostItem implements Comparable { private final BlogPostHeader header; diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java index 71fff2203..378b3993b 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java @@ -3,6 +3,7 @@ package org.briarproject.android.blogs; import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.support.annotation.UiThread; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.view.ViewCompat; @@ -28,11 +29,10 @@ import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.blogs.BlogActivity.POST_ID; import static org.briarproject.api.blogs.MessageType.POST; -public class BlogPostViewHolder extends RecyclerView.ViewHolder { +@UiThread +class BlogPostViewHolder extends RecyclerView.ViewHolder { private final Context ctx; - private OnBlogPostClickListener listener; - private final ViewGroup layout; private final AuthorView reblogger; private final AuthorView author; @@ -40,6 +40,8 @@ public class BlogPostViewHolder extends RecyclerView.ViewHolder { private final TextView body; private final ViewGroup commentContainer; + private OnBlogPostClickListener listener; + BlogPostViewHolder(View v) { super(v); diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java index 1df967c61..923d520a6 100644 --- a/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java @@ -2,6 +2,7 @@ package org.briarproject.android.blogs; import android.annotation.TargetApi; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.transition.Fade; import android.transition.Transition; @@ -14,8 +15,6 @@ import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.MessageId; -import static android.os.Build.VERSION.SDK_INT; -import static android.os.Build.VERSION_CODES.LOLLIPOP; import static org.briarproject.android.blogs.BlogActivity.POST_ID; public class ReblogActivity extends BriarActivity implements @@ -25,7 +24,7 @@ public class ReblogActivity extends BriarActivity implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (SDK_INT >= LOLLIPOP) { + if (Build.VERSION.SDK_INT >= 21) { setTransition(); } @@ -80,7 +79,7 @@ public class ReblogActivity extends BriarActivity implements } - @TargetApi(LOLLIPOP) + @TargetApi(21) private void setTransition() { Transition fade = new Fade(); fade.excludeTarget(android.R.id.statusBarBackground, true); diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java index 119290bb2..1fee6c647 100644 --- a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java @@ -33,7 +33,6 @@ public class ReblogFragment extends BaseFragment { public static final String TAG = ReblogFragment.class.getName(); - private BaseFragmentListener listener; private ViewHolder ui; private GroupId blogId; @@ -104,6 +103,7 @@ public class ReblogFragment extends BaseFragment { public void onStart() { super.onStart(); + // TODO: Load blog post when fragment is created. #631 feedController.loadBlogPost(blogId, postId, new UiResultExceptionHandler( getActivity()) { @@ -112,6 +112,7 @@ public class ReblogFragment extends BaseFragment { item = result; bindViewHolder(); } + @Override public void onExceptionUi(DbException exception) { // TODO @@ -123,6 +124,8 @@ public class ReblogFragment extends BaseFragment { private void bindViewHolder() { if (item == null) return; + hideProgressBar(); + ui.post.bindItem(item); ui.post.hideReblogButton(); @@ -134,7 +137,6 @@ public class ReblogFragment extends BaseFragment { } }); ui.publish.setEnabled(true); - hideProgressBar(); ui.scrollView.post(new Runnable() { @Override public void run() { @@ -151,6 +153,7 @@ public class ReblogFragment extends BaseFragment { public void onResultUi(Void result) { // do nothing, this fragment is gone already } + @Override public void onExceptionUi(DbException exception) { // do nothing, this fragment is gone already @@ -177,6 +180,7 @@ public class ReblogFragment extends BaseFragment { } private static class ViewHolder { + private final ScrollView scrollView; private final ProgressBar progressBar; private final BlogPostViewHolder post; diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java index a2cf53646..c19bcf1ee 100644 --- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java +++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java @@ -3,6 +3,7 @@ package org.briarproject.android.fragment; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.annotation.UiThread; import android.support.v4.app.Fragment; import org.briarproject.android.ActivityComponent; @@ -39,22 +40,27 @@ public abstract class BaseFragment extends Fragment { listener.onFragmentCreated(getUniqueTag()); } + @UiThread protected void finish() { getActivity().supportFinishAfterTransition(); } public interface BaseFragmentListener { + @UiThread void showLoadingScreen(boolean isBlocking, int stringId); + @UiThread void hideLoadingScreen(); void runOnUiThread(Runnable runnable); void runOnDbThread(Runnable runnable); + @UiThread ActivityComponent getActivityComponent(); + @UiThread void onFragmentCreated(String tag); } } diff --git a/briar-android/src/org/briarproject/android/util/AuthorView.java b/briar-android/src/org/briarproject/android/util/AuthorView.java index d84fb7f1b..c98c889a3 100644 --- a/briar-android/src/org/briarproject/android/util/AuthorView.java +++ b/briar-android/src/org/briarproject/android/util/AuthorView.java @@ -3,7 +3,6 @@ package org.briarproject.android.util; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; -import android.graphics.Typeface; import android.support.annotation.Nullable; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; @@ -25,6 +24,8 @@ import org.briarproject.api.sync.GroupId; import de.hdodenhof.circleimageview.CircleImageView; import im.delight.android.identicons.IdenticonDrawable; +import static android.content.Context.LAYOUT_INFLATER_SERVICE; +import static android.graphics.Typeface.BOLD; import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation; import static android.util.TypedValue.COMPLEX_UNIT_PX; import static org.briarproject.android.BriarActivity.GROUP_ID; @@ -42,9 +43,8 @@ public class AuthorView extends RelativeLayout { super(context, attrs); LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater - .inflate(R.layout.author_view, this, true); + .getSystemService(LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.author_view, this, true); avatar = (CircleImageView) findViewById(R.id.avatar); avatarIcon = (ImageView) findViewById(R.id.avatarIcon); @@ -75,7 +75,7 @@ public class AuthorView extends RelativeLayout { public void setAuthorStatus(Status status) { trustIndicator.setTrustLevel(status); if (status == OURSELVES) { - authorName.setTypeface(authorName.getTypeface(), Typeface.BOLD); + authorName.setTypeface(authorName.getTypeface(), BOLD); } invalidate(); @@ -92,9 +92,8 @@ public class AuthorView extends RelativeLayout { public void setBlogLink(final GroupId groupId) { setClickable(true); TypedValue outValue = new TypedValue(); - getContext().getTheme() - .resolveAttribute(android.R.attr.selectableItemBackground, - outValue, true); + getContext().getTheme().resolveAttribute( + android.R.attr.selectableItemBackground, outValue, true); setBackgroundResource(outValue.resourceId); setOnClickListener(new OnClickListener() { @Override @@ -106,8 +105,7 @@ public class AuthorView extends RelativeLayout { android.R.anim.slide_in_left, android.R.anim.slide_out_right); Intent[] intents = {i}; - ContextCompat - .startActivities(getContext(), intents, + ContextCompat.startActivities(getContext(), intents, options.toBundle()); } });