Merge branch '705-blog-pager-race-conditions' into 'master'

Remove blog pagers

I set out to fix potential race conditions in the blog pagers (the screens you reach by tapping the body of a blog post, that allow you to swipe left and right through the posts in the combined feed or a single blog). The race conditions here are similar to those addressed by !356, but the adapters don't inherit from BriarAdapter so they need to be fixed separately.

While I was looking into this I found a few minor problems with the pagers:
* The feed pager wasn't responding to events - this was fixed in !398
* The feed pager finishes NavDrawerActivity when any blog is removed
* The feed isn't updated when a blog is added (this applies to the list view as well as the pager)
* Posts aren't removed from the feed pager when a blog is removed

The last problem is quite serious - the feed pager's adapter contains posts that are no longer in the DB, so they'll fail to load. To fix that problem, the adapter needs to be cleared in onStop() and repopulated in onStart(). This is the same approach we use for other adapters where items can be removed from the underlying dataset. Unfortunately, FragmentStatePagerAdapter has some odd behaviour when you clear and repopulate it:

1. When reselecting the previously selected item after clearing and repopulating the adapter, the item slides into view instead of just appearing, which makes it look like you've accidentally swiped.
2. Items are sometimes duplicated when clearing and repopulating the adapter, so swiping left or right shows another copy of the same post.

These problems only seem to happen if the adapter is cleared - adding new posts works fine on master, although I think there might be some luck involved - FragmentStatePagerAdapter doesn't seem to be designed to support items changing positions.

I spent a lot of time trying to resolve these problems before concluding that maybe it wasn't worth it, and we should just remove the pagers. That's what's currently implemented in this branch. Tapping the body of a post will show the full-length post, but you won't be able to swipe left or right.

The swiping functionality was nice to have, so if you have ideas for fixing the bugs I'd love to hear them. But I'd rather remove this functionality than keep it in a buggy state with no plan for how to fix it.

Closes #705

See merge request !400
This commit is contained in:
akwizgran
2016-11-30 11:21:03 +00:00
18 changed files with 113 additions and 400 deletions

View File

@@ -6,10 +6,8 @@ import org.briarproject.android.blogs.BlogActivity;
import org.briarproject.android.blogs.BlogFragment; import org.briarproject.android.blogs.BlogFragment;
import org.briarproject.android.blogs.BlogModule; import org.briarproject.android.blogs.BlogModule;
import org.briarproject.android.blogs.BlogPostFragment; import org.briarproject.android.blogs.BlogPostFragment;
import org.briarproject.android.blogs.BlogPostPagerFragment;
import org.briarproject.android.blogs.FeedFragment; import org.briarproject.android.blogs.FeedFragment;
import org.briarproject.android.blogs.FeedPostFragment; import org.briarproject.android.blogs.FeedPostFragment;
import org.briarproject.android.blogs.FeedPostPagerFragment;
import org.briarproject.android.blogs.ReblogActivity; import org.briarproject.android.blogs.ReblogActivity;
import org.briarproject.android.blogs.ReblogFragment; import org.briarproject.android.blogs.ReblogFragment;
import org.briarproject.android.blogs.RssFeedImportActivity; import org.briarproject.android.blogs.RssFeedImportActivity;
@@ -132,10 +130,6 @@ public interface ActivityComponent {
void inject(FeedPostFragment fragment); void inject(FeedPostFragment fragment);
void inject(BlogPostPagerFragment fragment);
void inject(FeedPostPagerFragment fragment);
void inject(ReblogFragment fragment); void inject(ReblogFragment fragment);
void inject(ReblogActivity activity); void inject(ReblogActivity activity);

View File

@@ -8,6 +8,7 @@ import org.briarproject.android.controller.handler.ExceptionHandler;
import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.controller.handler.ResultExceptionHandler;
import org.briarproject.api.blogs.BlogPostHeader; import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
@@ -33,9 +34,10 @@ interface BaseController {
void repeatPost(BlogPostItem item, @Nullable String comment, void repeatPost(BlogPostItem item, @Nullable String comment,
ExceptionHandler<DbException> handler); ExceptionHandler<DbException> handler);
void setOnBlogPostAddedListener(OnBlogPostAddedListener listener); void setBlogListener(BlogListener listener);
interface OnBlogPostAddedListener extends DestroyableContext { @NotNullByDefault
interface BlogListener extends DestroyableContext {
@UiThread @UiThread
void onBlogPostAdded(BlogPostHeader header, boolean local); void onBlogPostAdded(BlogPostHeader header, boolean local);

View File

@@ -46,7 +46,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
private final Map<MessageId, BlogPostHeader> headerCache = private final Map<MessageId, BlogPostHeader> headerCache =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
private volatile OnBlogPostAddedListener listener; private volatile BlogListener listener;
BaseControllerImpl(@DatabaseExecutor Executor dbExecutor, BaseControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus, LifecycleManager lifecycleManager, EventBus eventBus,
@@ -73,7 +73,7 @@ abstract class BaseControllerImpl extends DbControllerImpl
} }
@Override @Override
public void setOnBlogPostAddedListener(OnBlogPostAddedListener listener) { public void setBlogListener(BlogListener listener) {
this.listener = listener; this.listener = listener;
} }

View File

@@ -11,7 +11,8 @@ import android.widget.ProgressBar;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.db.DbException; import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -19,8 +20,13 @@ import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION; import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class BasePostFragment extends BaseFragment { abstract class BasePostFragment extends BaseFragment {
static final String POST_ID = "briar.POST_ID";
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(BasePostFragment.class.getName()); Logger.getLogger(BasePostFragment.class.getName());
@@ -33,8 +39,9 @@ abstract class BasePostFragment extends BaseFragment {
@CallSuper @CallSuper
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_blog_post, container, view = inflater.inflate(R.layout.fragment_blog_post, container,
false); false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar); progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
@@ -64,12 +71,6 @@ abstract class BasePostFragment extends BaseFragment {
ui.bindItem(post); ui.bindItem(post);
} }
@UiThread
protected void onBlogPostLoadException(DbException exception) {
// TODO: Decide how to handle errors in the UI
finish();
}
private void startPeriodicUpdate() { private void startPeriodicUpdate() {
refresher = new Runnable() { refresher = new Runnable() {
@Override @Override

View File

@@ -1,183 +0,0 @@
package org.briarproject.android.blogs;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
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 android.widget.ProgressBar;
import org.briarproject.R;
import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.MessageId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static org.briarproject.android.blogs.BasePostPagerFragment.BlogPostPagerAdapter.INVALID_POSITION;
abstract class BasePostPagerFragment extends BaseFragment
implements OnBlogPostAddedListener {
static final String POST_ID = "briar.POST_ID";
private ViewPager pager;
private ProgressBar progressBar;
private BlogPostPagerAdapter postPagerAdapter;
private MessageId postId;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle state) {
Bundle args;
if (state == null) args = getArguments();
else args = state;
byte[] p = args.getByteArray(POST_ID);
if (p == null)
throw new IllegalStateException("No post ID in args");
postId = new MessageId(p);
View v = inflater.inflate(R.layout.fragment_blog_post_pager, container,
false);
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE);
pager = (ViewPager) v.findViewById(R.id.pager);
postPagerAdapter = new BlogPostPagerAdapter(getChildFragmentManager());
return v;
}
@Override
public void onStart() {
super.onStart();
if (postId == null) {
MessageId selected = getSelectedPost();
if (selected != null) loadBlogPosts(selected);
} else {
loadBlogPosts(postId);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
MessageId selected = getSelectedPost();
if (selected != null)
outState.putByteArray(POST_ID, selected.getBytes());
}
@Override
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
loadBlogPost(header);
}
abstract void loadBlogPosts(final MessageId select);
abstract void loadBlogPost(BlogPostHeader header);
@UiThread
protected void onBlogPostsLoaded(MessageId select,
Collection<BlogPostItem> posts) {
postId = null;
postPagerAdapter.setPosts(posts);
selectPost(select);
}
@UiThread
protected void onBlogPostsLoadedException(DbException exception) {
// TODO: Decide how to handle errors in the UI
finish();
}
@Nullable
private MessageId getSelectedPost() {
if (postPagerAdapter.getCount() == 0) return null;
int position = pager.getCurrentItem();
return postPagerAdapter.getPost(position).getId();
}
private void selectPost(MessageId m) {
int pos = postPagerAdapter.getPostPosition(m);
if (pos != INVALID_POSITION) {
progressBar.setVisibility(INVISIBLE);
pager.setAdapter(postPagerAdapter);
pager.setCurrentItem(pos);
}
}
protected void addPost(BlogPostItem post) {
MessageId selected = getSelectedPost();
postPagerAdapter.addPost(post);
if (selected != null) selectPost(selected);
}
@UiThread
static class BlogPostPagerAdapter extends FragmentStatePagerAdapter {
static final int INVALID_POSITION = -1;
private final List<BlogPostItem> posts = new ArrayList<>();
private BlogPostPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return posts.size();
}
@Override
public Fragment getItem(int position) {
BlogPostItem post = posts.get(position);
return FeedPostFragment.newInstance(post.getGroupId(), post.getId());
}
private BlogPostItem getPost(int position) {
return posts.get(position);
}
private void setPosts(Collection<BlogPostItem> posts) {
this.posts.clear();
this.posts.addAll(posts);
Collections.sort(this.posts);
notifyDataSetChanged();
}
private void addPost(BlogPostItem post) {
posts.add(post);
Collections.sort(posts);
notifyDataSetChanged();
}
private int getPostPosition(MessageId m) {
int count = getCount();
for (int i = 0; i < count; i++) {
if (getPost(i).getId().equals(m)) {
return i;
}
}
return INVALID_POSITION;
}
}
@Override
public void onBlogRemoved() {
finish();
}
}

View File

@@ -49,7 +49,7 @@ public class BlogActivity extends BriarActivity implements
@Override @Override
public void onBlogPostClick(BlogPostItem post) { public void onBlogPostClick(BlogPostItem post) {
BlogPostPagerFragment f = BlogPostPagerFragment.newInstance(post.getId()); BlogPostFragment f = BlogPostFragment.newInstance(post.getId());
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, f, f.getUniqueTag()) .replace(R.id.fragmentContainer, f, f.getUniqueTag())
.addToBackStack(f.getUniqueTag()) .addToBackStack(f.getUniqueTag())

View File

@@ -4,6 +4,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@@ -19,7 +20,7 @@ import android.widget.Toast;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener; import org.briarproject.android.blogs.BaseController.BlogListener;
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
@@ -46,10 +47,11 @@ import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_SHARE; import static org.briarproject.android.blogs.BlogActivity.REQUEST_SHARE;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST; import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
@UiThread
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class BlogFragment extends BaseFragment implements public class BlogFragment extends BaseFragment implements
OnBlogPostAddedListener { BlogListener {
private final static String TAG = BlogFragment.class.getName(); private final static String TAG = BlogFragment.class.getName();
@@ -99,7 +101,7 @@ public class BlogFragment extends BaseFragment implements
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
component.inject(this); component.inject(this);
blogController.setOnBlogPostAddedListener(this); blogController.setBlogListener(this);
} }
@Override @Override

View File

@@ -2,6 +2,7 @@ package org.briarproject.android.blogs;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -9,15 +10,18 @@ import android.view.ViewGroup;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID; @UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class BlogPostFragment extends BasePostFragment { public class BlogPostFragment extends BasePostFragment {
public final static String TAG = BlogPostFragment.class.getName(); private static final String TAG = BlogPostFragment.class.getName();
private MessageId postId; private MessageId postId;
@@ -36,8 +40,9 @@ public class BlogPostFragment extends BasePostFragment {
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
Bundle args = getArguments(); Bundle args = getArguments();
byte[] p = args.getByteArray(POST_ID); byte[] p = args.getByteArray(POST_ID);
@@ -67,9 +72,11 @@ public class BlogPostFragment extends BasePostFragment {
public void onResultUi(BlogPostItem post) { public void onResultUi(BlogPostItem post) {
onBlogPostLoaded(post); onBlogPostLoaded(post);
} }
@Override @Override
public void onExceptionUi(DbException exception) { public void onExceptionUi(DbException exception) {
onBlogPostLoadException(exception); // TODO: Decide how to handle errors in the UI
finish();
} }
}); });
} }

View File

@@ -9,7 +9,9 @@ import org.briarproject.api.identity.Author.Status;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
// This class is not thread-safe import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
public class BlogPostItem implements Comparable<BlogPostItem> { public class BlogPostItem implements Comparable<BlogPostItem> {
private final BlogPostHeader header; private final BlogPostHeader header;

View File

@@ -1,78 +0,0 @@
package org.briarproject.android.blogs;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.MessageId;
import java.util.Collection;
import javax.inject.Inject;
public class BlogPostPagerFragment extends BasePostPagerFragment {
public final static String TAG = BlogPostPagerFragment.class.getName();
@Inject
BlogController blogController;
static BlogPostPagerFragment newInstance(MessageId postId) {
BlogPostPagerFragment f = new BlogPostPagerFragment();
Bundle args = new Bundle();
args.putByteArray(POST_ID, postId.getBytes());
f.setArguments(args);
return f;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
blogController.setOnBlogPostAddedListener(this);
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
void loadBlogPosts(final MessageId select) {
blogController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
this) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
onBlogPostsLoaded(select, posts);
}
@Override
public void onExceptionUi(DbException exception) {
onBlogPostsLoadedException(exception);
}
});
}
@Override
void loadBlogPost(BlogPostHeader header) {
blogController.loadBlogPost(header,
new UiResultExceptionHandler<BlogPostItem, DbException>(
this) {
@Override
public void onResultUi(BlogPostItem post) {
addPost(post);
}
@Override
public void onExceptionUi(DbException exception) {
// TODO: Decide how to handle errors in the UI
finish();
}
});
}
}

View File

@@ -30,7 +30,7 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID; import static org.briarproject.android.blogs.BasePostFragment.POST_ID;
import static org.briarproject.android.util.AndroidUtils.TEASER_LENGTH; import static org.briarproject.android.util.AndroidUtils.TEASER_LENGTH;
import static org.briarproject.android.util.AndroidUtils.getSpanned; import static org.briarproject.android.util.AndroidUtils.getSpanned;
import static org.briarproject.android.util.AndroidUtils.getTeaser; import static org.briarproject.android.util.AndroidUtils.getTeaser;

View File

@@ -1,8 +1,11 @@
package org.briarproject.android.blogs; package org.briarproject.android.blogs;
import android.support.annotation.UiThread;
import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.android.controller.handler.ResultExceptionHandler;
import org.briarproject.api.blogs.Blog; import org.briarproject.api.blogs.Blog;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
@@ -13,4 +16,12 @@ public interface FeedController extends BaseController {
void loadPersonalBlog(ResultExceptionHandler<Blog, DbException> handler); void loadPersonalBlog(ResultExceptionHandler<Blog, DbException> handler);
void setFeedListener(FeedListener listener);
@NotNullByDefault
interface FeedListener extends BlogListener {
@UiThread
void onBlogAdded();
}
} }

View File

@@ -11,6 +11,7 @@ import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.event.BlogPostAddedEvent; import org.briarproject.api.event.BlogPostAddedEvent;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.GroupAddedEvent;
import org.briarproject.api.event.GroupRemovedEvent; import org.briarproject.api.event.GroupRemovedEvent;
import org.briarproject.api.identity.Author; import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.IdentityManager;
@@ -33,6 +34,8 @@ class FeedControllerImpl extends BaseControllerImpl
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(FeedControllerImpl.class.getName()); Logger.getLogger(FeedControllerImpl.class.getName());
private volatile FeedListener listener;
@Inject @Inject
FeedControllerImpl(@DatabaseExecutor Executor dbExecutor, FeedControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus, LifecycleManager lifecycleManager, EventBus eventBus,
@@ -45,6 +48,7 @@ class FeedControllerImpl extends BaseControllerImpl
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (listener == null) throw new IllegalStateException();
notificationManager.blockAllBlogPostNotifications(); notificationManager.blockAllBlogPostNotifications();
notificationManager.clearAllBlogPostNotifications(); notificationManager.clearAllBlogPostNotifications();
} }
@@ -55,12 +59,24 @@ class FeedControllerImpl extends BaseControllerImpl
notificationManager.unblockAllBlogPostNotifications(); notificationManager.unblockAllBlogPostNotifications();
} }
@Override
public void setFeedListener(FeedListener listener) {
super.setBlogListener(listener);
this.listener = listener;
}
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof BlogPostAddedEvent) { if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e; BlogPostAddedEvent b = (BlogPostAddedEvent) e;
LOG.info("Blog post added"); LOG.info("Blog post added");
onBlogPostAdded(b.getHeader(), b.isLocal()); onBlogPostAdded(b.getHeader(), b.isLocal());
} else if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog added");
onBlogAdded();
}
} else if (e instanceof GroupRemovedEvent) { } else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e; GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) { if (g.getGroup().getClientId().equals(CLIENT_ID)) {
@@ -70,6 +86,15 @@ class FeedControllerImpl extends BaseControllerImpl
} }
} }
private void onBlogAdded() {
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
listener.onBlogAdded();
}
});
}
@Override @Override
public void loadBlogPosts( public void loadBlogPosts(
final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) { final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {

View File

@@ -3,6 +3,7 @@ package org.briarproject.android.blogs;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@@ -17,14 +18,16 @@ import android.view.ViewGroup;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener;
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener; import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.android.blogs.FeedController.FeedListener;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.fragment.BaseFragment; import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.blogs.Blog; import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogPostHeader; import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -37,8 +40,11 @@ import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST; import static org.briarproject.android.blogs.BlogActivity.REQUEST_WRITE_POST;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class FeedFragment extends BaseFragment implements public class FeedFragment extends BaseFragment implements
OnBlogPostClickListener, OnBlogPostAddedListener { OnBlogPostClickListener, FeedListener {
public final static String TAG = FeedFragment.class.getName(); public final static String TAG = FeedFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG); private static final Logger LOG = Logger.getLogger(TAG);
@@ -62,8 +68,9 @@ public class FeedFragment extends BaseFragment implements
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_blog, container, false); View v = inflater.inflate(R.layout.fragment_blog, container, false);
@@ -81,7 +88,7 @@ public class FeedFragment extends BaseFragment implements
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
component.inject(this); component.inject(this);
feedController.setOnBlogPostAddedListener(this); feedController.setFeedListener(this);
} }
@Override @Override
@@ -216,8 +223,8 @@ public class FeedFragment extends BaseFragment implements
@Override @Override
public void onBlogPostClick(BlogPostItem post) { public void onBlogPostClick(BlogPostItem post) {
FeedPostPagerFragment f = FeedPostPagerFragment FeedPostFragment f =
.newInstance(post.getId()); FeedPostFragment.newInstance(post.getGroupId(), post.getId());
getActivity().getSupportFragmentManager().beginTransaction() getActivity().getSupportFragmentManager().beginTransaction()
.replace(R.id.content_fragment, f, f.getUniqueTag()) .replace(R.id.content_fragment, f, f.getUniqueTag())
.addToBackStack(f.getUniqueTag()) .addToBackStack(f.getUniqueTag())
@@ -253,6 +260,11 @@ public class FeedFragment extends BaseFragment implements
s.show(); s.show();
} }
@Override
public void onBlogAdded() {
loadBlogPosts(false);
}
@Override @Override
public void onBlogRemoved() { public void onBlogRemoved() {
loadBlogPosts(true); loadBlogPosts(true);

View File

@@ -2,6 +2,7 @@ package org.briarproject.android.blogs;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -9,17 +10,21 @@ import android.view.ViewGroup;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class FeedPostFragment extends BasePostFragment { public class FeedPostFragment extends BasePostFragment {
public final static String TAG = FeedPostFragment.class.getName(); private static final String TAG = FeedPostFragment.class.getName();
private MessageId postId; private MessageId postId;
private GroupId blogId; private GroupId blogId;
@@ -40,8 +45,9 @@ public class FeedPostFragment extends BasePostFragment {
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater,
Bundle savedInstanceState) { @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
Bundle args = getArguments(); Bundle args = getArguments();
byte[] b = args.getByteArray(GROUP_ID); byte[] b = args.getByteArray(GROUP_ID);
@@ -55,6 +61,11 @@ public class FeedPostFragment extends BasePostFragment {
return super.onCreateView(inflater, container, savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState);
} }
@Override
public String getUniqueTag() {
return TAG;
}
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
component.inject(this); component.inject(this);
@@ -70,16 +81,12 @@ public class FeedPostFragment extends BasePostFragment {
public void onResultUi(BlogPostItem post) { public void onResultUi(BlogPostItem post) {
onBlogPostLoaded(post); onBlogPostLoaded(post);
} }
@Override @Override
public void onExceptionUi(DbException exception) { public void onExceptionUi(DbException exception) {
onBlogPostLoadException(exception); // TODO: Decide how to handle errors in the UI
} }
}); });
} }
@Override
public String getUniqueTag() {
return TAG;
}
} }

View File

@@ -1,89 +0,0 @@
package org.briarproject.android.blogs;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.MessageId;
import java.util.Collection;
import javax.inject.Inject;
public class FeedPostPagerFragment extends BasePostPagerFragment {
public final static String TAG = FeedPostPagerFragment.class.getName();
@Inject
FeedController feedController;
static FeedPostPagerFragment newInstance(MessageId postId) {
FeedPostPagerFragment f = new FeedPostPagerFragment();
Bundle args = new Bundle();
args.putByteArray(POST_ID, postId.getBytes());
f.setArguments(args);
return f;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
feedController.setOnBlogPostAddedListener(this);
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void onStart() {
super.onStart();
feedController.onStart();
}
@Override
public void onStop() {
super.onStop();
feedController.onStop();
}
@Override
void loadBlogPosts(final MessageId select) {
feedController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
this) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
onBlogPostsLoaded(select, posts);
}
@Override
public void onExceptionUi(DbException exception) {
onBlogPostsLoadedException(exception);
}
});
}
@Override
void loadBlogPost(BlogPostHeader header) {
feedController.loadBlogPost(header,
new UiResultExceptionHandler<BlogPostItem, DbException>(
this) {
@Override
public void onResultUi(BlogPostItem post) {
addPost(post);
}
@Override
public void onExceptionUi(DbException exception) {
// TODO: Decide how to handle errors in the UI
finish();
}
});
}
}

View File

@@ -15,7 +15,7 @@ import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID; import static org.briarproject.android.blogs.BasePostFragment.POST_ID;
public class ReblogActivity extends BriarActivity implements public class ReblogActivity extends BriarActivity implements
BaseFragmentListener { BaseFragmentListener {

View File

@@ -28,7 +28,7 @@ import static android.view.View.GONE;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.android.BriarActivity.GROUP_ID; import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID; import static org.briarproject.android.blogs.BasePostFragment.POST_ID;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault