mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 05:39:53 +01:00
Make BlogController thread-safe. #555
This commit is contained in:
@@ -2,31 +2,36 @@ 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.UiThread;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||||
import android.support.v4.view.ViewPager;
|
import android.support.v4.view.ViewPager;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
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.BriarActivity;
|
import org.briarproject.android.BriarActivity;
|
||||||
import org.briarproject.android.blogs.BlogController.BlogPostListener;
|
import org.briarproject.android.blogs.BlogController.BlogPostListener;
|
||||||
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
|
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
|
||||||
import org.briarproject.android.controller.handler.UiResultHandler;
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
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 java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
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 android.widget.Toast.LENGTH_SHORT;
|
|
||||||
|
|
||||||
public class BlogActivity extends BriarActivity implements BlogPostListener,
|
public class BlogActivity extends BriarActivity implements BlogPostListener,
|
||||||
OnBlogPostClickListener, BaseFragmentListener {
|
OnBlogPostClickListener, BaseFragmentListener {
|
||||||
@@ -37,17 +42,17 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
|
static final String IS_MY_BLOG = "briar.IS_MY_BLOG";
|
||||||
static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
|
static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
|
||||||
|
|
||||||
private static final String BLOG_PAGER_ADAPTER = "briar.BLOG_PAGER_ADAPTER";
|
private static final String POST_ID = "briar.POST_ID";
|
||||||
|
|
||||||
|
private GroupId groupId;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private ViewPager pager;
|
private ViewPager pager;
|
||||||
private BlogPagerAdapter blogPagerAdapter;
|
private BlogPagerAdapter blogPagerAdapter;
|
||||||
private BlogPostPagerAdapter postPagerAdapter;
|
private BlogPostPagerAdapter postPagerAdapter;
|
||||||
private String blogName;
|
private String blogName;
|
||||||
private boolean myBlog, isNew;
|
private boolean myBlog, isNew;
|
||||||
|
private MessageId savedPostId;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
|
||||||
private volatile GroupId groupId = null;
|
|
||||||
@Inject
|
@Inject
|
||||||
BlogController blogController;
|
BlogController blogController;
|
||||||
|
|
||||||
@@ -58,8 +63,9 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
// GroupId from Intent
|
// GroupId from Intent
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||||
if (b == null) throw new IllegalStateException("No Group in intent.");
|
if (b == null) throw new IllegalStateException("No group ID in intent");
|
||||||
groupId = new GroupId(b);
|
groupId = new GroupId(b);
|
||||||
|
blogController.setGroupId(groupId);
|
||||||
|
|
||||||
// Name of the Blog from Intent
|
// Name of the Blog from Intent
|
||||||
blogName = i.getStringExtra(BLOG_NAME);
|
blogName = i.getStringExtra(BLOG_NAME);
|
||||||
@@ -76,27 +82,42 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
hideLoadingScreen();
|
hideLoadingScreen();
|
||||||
|
|
||||||
blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
|
blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
|
||||||
if (state == null || state.getBoolean(BLOG_PAGER_ADAPTER, true)) {
|
postPagerAdapter = new BlogPostPagerAdapter(
|
||||||
|
getSupportFragmentManager());
|
||||||
|
|
||||||
|
if (state == null || state.getByteArray(POST_ID) == null) {
|
||||||
pager.setAdapter(blogPagerAdapter);
|
pager.setAdapter(blogPagerAdapter);
|
||||||
|
savedPostId = null;
|
||||||
} else {
|
} else {
|
||||||
// this initializes and restores the postPagerAdapter
|
// Adapter will be set in selectPostInPostPager()
|
||||||
loadBlogPosts();
|
savedPostId = new MessageId(state.getByteArray(POST_ID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
if (savedPostId == null) {
|
||||||
|
MessageId selected = getSelectedPostInPostPager();
|
||||||
|
if (selected != null) loadBlogPosts(selected);
|
||||||
|
} else {
|
||||||
|
loadBlogPosts(savedPostId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
MessageId selected = getSelectedPostInPostPager();
|
||||||
// remember which adapter we had active
|
if (selected != null)
|
||||||
outState.putBoolean(BLOG_PAGER_ADAPTER,
|
outState.putByteArray(POST_ID, selected.getBytes());
|
||||||
pager.getAdapter() == blogPagerAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (pager.getAdapter() == postPagerAdapter) {
|
if (pager.getAdapter() == postPagerAdapter) {
|
||||||
pager.setAdapter(blogPagerAdapter);
|
pager.setAdapter(blogPagerAdapter);
|
||||||
|
savedPostId = null;
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
@@ -127,60 +148,80 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlogPostClick(int position, BlogPostItem post) {
|
public void onBlogPostClick(BlogPostItem post) {
|
||||||
loadBlogPosts(position, true);
|
loadBlogPosts(post.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadBlogPosts() {
|
private void loadBlogPosts(final MessageId select) {
|
||||||
loadBlogPosts(0, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadBlogPosts(final int position, final boolean setItem) {
|
|
||||||
showLoadingScreen();
|
showLoadingScreen();
|
||||||
blogController.loadBlog(groupId, false,
|
blogController.loadBlogPosts(
|
||||||
new UiResultHandler<Boolean>(this) {
|
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
||||||
|
this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Boolean result) {
|
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||||
if (result) {
|
hideLoadingScreen();
|
||||||
Collection<BlogPostItem> posts =
|
savedPostId = null;
|
||||||
blogController.getBlogPosts();
|
postPagerAdapter.setPosts(posts);
|
||||||
|
selectPostInPostPager(select);
|
||||||
|
}
|
||||||
|
|
||||||
if (postPagerAdapter == null) {
|
@Override
|
||||||
postPagerAdapter = new BlogPostPagerAdapter(
|
public void onExceptionUi(DbException exception) {
|
||||||
getSupportFragmentManager(),
|
// TODO: Decide how to handle errors in the UI
|
||||||
posts.size());
|
finish();
|
||||||
} else {
|
|
||||||
postPagerAdapter.setSize(posts.size());
|
|
||||||
}
|
|
||||||
pager.setAdapter(postPagerAdapter);
|
|
||||||
if (setItem) pager.setCurrentItem(position);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(BlogActivity.this,
|
|
||||||
R.string.blogs_blog_post_failed_to_load,
|
|
||||||
LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlogPostAdded(final BlogPostItem post, final boolean local) {
|
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
|
||||||
runOnUiThread(new Runnable() {
|
if (pager.getAdapter() == postPagerAdapter) {
|
||||||
@Override
|
loadBlogPost(header);
|
||||||
public void run() {
|
} else {
|
||||||
if (blogPagerAdapter != null) {
|
BlogFragment f = blogPagerAdapter.getFragment();
|
||||||
BlogFragment f = blogPagerAdapter.getFragment();
|
if (f != null && f.isVisible()) f.onBlogPostAdded(header, local);
|
||||||
if (f != null && f.isVisible()) {
|
}
|
||||||
f.onBlogPostAdded(post, local);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (postPagerAdapter != null) {
|
private void loadBlogPost(BlogPostHeader header) {
|
||||||
postPagerAdapter.onBlogPostAdded();
|
blogController.loadBlogPost(header,
|
||||||
postPagerAdapter.notifyDataSetChanged();
|
new UiResultExceptionHandler<BlogPostItem, DbException>(this) {
|
||||||
}
|
@Override
|
||||||
|
public void onResultUi(BlogPostItem post) {
|
||||||
|
addPostToPostPager(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO: Decide how to handle errors in the UI
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MessageId getSelectedPostInPostPager() {
|
||||||
|
if (pager.getAdapter() != postPagerAdapter) return null;
|
||||||
|
if (postPagerAdapter.getCount() == 0) return null;
|
||||||
|
int position = pager.getCurrentItem();
|
||||||
|
return postPagerAdapter.getPost(position).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectPostInPostPager(MessageId m) {
|
||||||
|
int count = postPagerAdapter.getCount();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (postPagerAdapter.getPost(i).getId().equals(m)) {
|
||||||
|
pager.setAdapter(postPagerAdapter);
|
||||||
|
pager.setCurrentItem(i);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPostToPostPager(BlogPostItem post) {
|
||||||
|
MessageId selected = getSelectedPostInPostPager();
|
||||||
|
postPagerAdapter.addPost(post);
|
||||||
|
if (selected != null) selectPostInPostPager(selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,18 +232,22 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
// The BlogPostAddedEvent arrives when the controller is not listening,
|
// The BlogPostAddedEvent arrives when the controller is not listening,
|
||||||
// so we need to manually reload the blog posts :(
|
// so we need to manually reload the blog posts :(
|
||||||
if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
|
if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
|
||||||
BlogFragment f = blogPagerAdapter.getFragment();
|
if (pager.getAdapter() == postPagerAdapter) {
|
||||||
if (f != null && f.isVisible()) {
|
MessageId selected = getSelectedPostInPostPager();
|
||||||
f.reload();
|
if (selected != null) loadBlogPosts(selected);
|
||||||
|
} else {
|
||||||
|
BlogFragment f = blogPagerAdapter.getFragment();
|
||||||
|
if (f != null && f.isVisible()) f.loadBlogPosts(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private class BlogPagerAdapter extends FragmentStatePagerAdapter {
|
private class BlogPagerAdapter extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
private BlogFragment fragment = null;
|
private BlogFragment fragment = null;
|
||||||
|
|
||||||
BlogPagerAdapter(FragmentManager fm) {
|
private BlogPagerAdapter(FragmentManager fm) {
|
||||||
super(fm);
|
super(fm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,36 +269,46 @@ public class BlogActivity extends BriarActivity implements BlogPostListener,
|
|||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlogFragment getFragment() {
|
private BlogFragment getFragment() {
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BlogPostPagerAdapter extends FragmentStatePagerAdapter {
|
@UiThread
|
||||||
private int size;
|
private static class BlogPostPagerAdapter
|
||||||
|
extends FragmentStatePagerAdapter {
|
||||||
|
|
||||||
BlogPostPagerAdapter(FragmentManager fm, int size) {
|
private final List<BlogPostItem> posts = new ArrayList<>();
|
||||||
|
|
||||||
|
private BlogPostPagerAdapter(FragmentManager fm) {
|
||||||
super(fm);
|
super(fm);
|
||||||
this.size = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return size;
|
return posts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
MessageId postIdOfPos = blogController.getBlogPostId(position);
|
return BlogPostFragment.newInstance(posts.get(position).getId());
|
||||||
return BlogPostFragment.newInstance(groupId, postIdOfPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBlogPostAdded() {
|
private BlogPostItem getPost(int position) {
|
||||||
size++;
|
return posts.get(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSize(int size) {
|
private void setPosts(Collection<BlogPostItem> posts) {
|
||||||
this.size = size;
|
this.posts.clear();
|
||||||
|
this.posts.addAll(posts);
|
||||||
|
Collections.sort(this.posts);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPost(BlogPostItem post) {
|
||||||
|
posts.add(post);
|
||||||
|
Collections.sort(posts);
|
||||||
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,36 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.android.controller.ActivityLifecycleController;
|
import org.briarproject.android.controller.ActivityLifecycleController;
|
||||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.android.controller.handler.ResultHandler;
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
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 java.util.SortedSet;
|
import java.util.Collection;
|
||||||
|
|
||||||
public interface BlogController extends ActivityLifecycleController {
|
public interface BlogController extends ActivityLifecycleController {
|
||||||
|
|
||||||
void loadBlog(GroupId groupId, boolean reload,
|
void setGroupId(GroupId g);
|
||||||
ResultHandler<Boolean> resultHandler);
|
|
||||||
|
|
||||||
SortedSet<BlogPostItem> getBlogPosts();
|
void loadBlogPosts(
|
||||||
|
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
|
||||||
|
|
||||||
@Nullable
|
void loadBlogPost(BlogPostHeader header,
|
||||||
BlogPostItem getBlogPost(MessageId postId);
|
ResultExceptionHandler<BlogPostItem, DbException> handler);
|
||||||
|
|
||||||
@Nullable
|
void loadBlogPost(MessageId m,
|
||||||
MessageId getBlogPostId(int position);
|
ResultExceptionHandler<BlogPostItem, DbException> handler);
|
||||||
|
|
||||||
void canDeleteBlog(GroupId groupId,
|
void canDeleteBlog(ResultExceptionHandler<Boolean, DbException> handler);
|
||||||
ResultExceptionHandler<Boolean, DbException> resultHandler);
|
|
||||||
|
|
||||||
void deleteBlog(ResultHandler<Boolean> resultHandler);
|
void deleteBlog(ResultExceptionHandler<Void, DbException> handler);
|
||||||
|
|
||||||
interface BlogPostListener {
|
interface BlogPostListener {
|
||||||
void onBlogPostAdded(BlogPostItem post, boolean local);
|
@UiThread
|
||||||
|
void onBlogPostAdded(BlogPostHeader header, boolean local);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.briarproject.android.controller.DbControllerImpl;
|
import org.briarproject.android.controller.DbControllerImpl;
|
||||||
import org.briarproject.android.controller.handler.ResultExceptionHandler;
|
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.Blog;
|
||||||
import org.briarproject.api.blogs.BlogManager;
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
import org.briarproject.api.blogs.BlogPostHeader;
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
@@ -20,8 +18,9 @@ import org.briarproject.api.sync.MessageId;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.SortedSet;
|
import java.util.List;
|
||||||
import java.util.TreeSet;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -38,19 +37,25 @@ public class BlogControllerImpl extends DbControllerImpl
|
|||||||
@Inject
|
@Inject
|
||||||
protected Activity activity;
|
protected Activity activity;
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile BlogManager blogManager;
|
protected EventBus eventBus;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile EventBus eventBus;
|
protected volatile BlogManager blogManager;
|
||||||
|
|
||||||
|
private final Map<MessageId, byte[]> bodyCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private volatile BlogPostListener listener;
|
private volatile BlogPostListener listener;
|
||||||
private volatile GroupId groupId = null;
|
private volatile GroupId groupId = null;
|
||||||
// FIXME: This collection isn't thread-safe, isn't updated atomically
|
|
||||||
private volatile TreeSet<BlogPostItem> posts = null;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BlogControllerImpl() {
|
BlogControllerImpl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setGroupId(GroupId g) {
|
||||||
|
groupId = g;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreate() {
|
public void onActivityCreate() {
|
||||||
if (activity instanceof BlogPostListener) {
|
if (activity instanceof BlogPostListener) {
|
||||||
@@ -78,26 +83,17 @@ public class BlogControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
if (e instanceof BlogPostAddedEvent) {
|
if (e instanceof BlogPostAddedEvent) {
|
||||||
BlogPostAddedEvent m = (BlogPostAddedEvent) e;
|
final BlogPostAddedEvent m = (BlogPostAddedEvent) e;
|
||||||
if (m.getGroupId().equals(groupId)) {
|
if (m.getGroupId().equals(groupId)) {
|
||||||
LOG.info("New blog post added");
|
LOG.info("New blog post added");
|
||||||
if (posts == null) {
|
activity.runOnUiThread(new Runnable() {
|
||||||
LOG.info("Posts have not loaded, yet");
|
@Override
|
||||||
// FIXME: Race condition, new post may not get loaded
|
public void run() {
|
||||||
return;
|
listener.onBlogPostAdded(m.getHeader(), m.isLocal());
|
||||||
}
|
}
|
||||||
final BlogPostHeader header = m.getHeader();
|
});
|
||||||
// FIXME: Don't make blocking calls in event handlers
|
|
||||||
try {
|
|
||||||
byte[] body = blogManager.getPostBody(header.getId());
|
|
||||||
BlogPostItem post = new BlogPostItem(groupId, header, body);
|
|
||||||
posts.add(post);
|
|
||||||
listener.onBlogPostAdded(post, m.isLocal());
|
|
||||||
} catch (DbException ex) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, ex.toString(), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (e instanceof GroupRemovedEvent) {
|
} else if (e instanceof GroupRemovedEvent) {
|
||||||
GroupRemovedEvent s = (GroupRemovedEvent) e;
|
GroupRemovedEvent s = (GroupRemovedEvent) e;
|
||||||
@@ -106,6 +102,7 @@ public class BlogControllerImpl extends DbControllerImpl
|
|||||||
activity.runOnUiThread(new Runnable() {
|
activity.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
// TODO: Not the controller's job, add a listener method
|
||||||
activity.finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -114,106 +111,127 @@ public class BlogControllerImpl extends DbControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadBlog(final GroupId g, final boolean reload,
|
public void loadBlogPosts(
|
||||||
final ResultHandler<Boolean> resultHandler) {
|
final ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
if (reload || posts == null) {
|
long now = System.currentTimeMillis();
|
||||||
groupId = g;
|
Collection<BlogPostHeader> headers =
|
||||||
posts = new TreeSet<>();
|
blogManager.getPostHeaders(groupId);
|
||||||
// load blog posts
|
long duration = System.currentTimeMillis() - now;
|
||||||
long now = System.currentTimeMillis();
|
if (LOG.isLoggable(INFO))
|
||||||
Collection<BlogPostItem> newPosts = new ArrayList<>();
|
LOG.info("Loading headers took " + duration + " ms");
|
||||||
Collection<BlogPostHeader> header =
|
List<BlogPostItem> items = new ArrayList<>(headers.size());
|
||||||
blogManager.getPostHeaders(g);
|
now = System.currentTimeMillis();
|
||||||
for (BlogPostHeader h : header) {
|
for (BlogPostHeader h : headers) {
|
||||||
byte[] body = blogManager.getPostBody(h.getId());
|
byte[] body = getPostBody(h.getId());
|
||||||
newPosts.add(new BlogPostItem(g, h, body));
|
items.add(new BlogPostItem(groupId, h, body));
|
||||||
}
|
|
||||||
posts.addAll(newPosts);
|
|
||||||
long duration = System.currentTimeMillis() - now;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Loading blog took " + duration + " ms");
|
|
||||||
}
|
}
|
||||||
resultHandler.onResult(true);
|
duration = System.currentTimeMillis() - now;
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loading bodies took " + duration + " ms");
|
||||||
|
handler.onResult(items);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
resultHandler.onResult(false);
|
handler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
public void loadBlogPost(final BlogPostHeader header,
|
||||||
public SortedSet<BlogPostItem> getBlogPosts() {
|
final ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||||
return posts;
|
if (groupId == null) throw new IllegalStateException();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public BlogPostItem getBlogPost(MessageId id) {
|
|
||||||
if (posts == null) return null;
|
|
||||||
for (BlogPostItem item : posts) {
|
|
||||||
if (item.getId().equals(id)) return item;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public MessageId getBlogPostId(int position) {
|
|
||||||
if (posts == null) return null;
|
|
||||||
int i = 0;
|
|
||||||
for (BlogPostItem post : posts) {
|
|
||||||
if (i == position) return post.getId();
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void canDeleteBlog(final GroupId g,
|
|
||||||
final ResultExceptionHandler<Boolean, DbException> resultHandler) {
|
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (groupId == null) {
|
|
||||||
resultHandler.onResult(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
resultHandler.onResult(blogManager.canBeRemoved(groupId));
|
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) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
resultHandler.onException(e);
|
handler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteBlog(final ResultHandler<Boolean> resultHandler) {
|
public void loadBlogPost(final MessageId m,
|
||||||
|
final ResultExceptionHandler<BlogPostItem, DbException> handler) {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (groupId == null) {
|
try {
|
||||||
resultHandler.onResult(false);
|
long now = System.currentTimeMillis();
|
||||||
return;
|
BlogPostHeader header = blogManager.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));
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
handler.onException(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
public void canDeleteBlog(
|
||||||
|
final ResultExceptionHandler<Boolean, DbException> handler) {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
handler.onResult(blogManager.canBeRemoved(groupId));
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
handler.onException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteBlog(
|
||||||
|
final ResultExceptionHandler<Void, DbException> handler) {
|
||||||
|
if (groupId == null) throw new IllegalStateException();
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Blog b = blogManager.getBlog(groupId);
|
Blog b = blogManager.getBlog(groupId);
|
||||||
blogManager.removeBlog(b);
|
blogManager.removeBlog(b);
|
||||||
resultHandler.onResult(true);
|
handler.onResult(null);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e.toString(), e);
|
LOG.log(WARNING, e.toString(), e);
|
||||||
resultHandler.onResult(false);
|
handler.onException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ import org.briarproject.android.ActivityComponent;
|
|||||||
import org.briarproject.android.blogs.BlogController.BlogPostListener;
|
import org.briarproject.android.blogs.BlogController.BlogPostListener;
|
||||||
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.controller.handler.UiResultHandler;
|
|
||||||
import org.briarproject.android.fragment.BaseFragment;
|
import org.briarproject.android.fragment.BaseFragment;
|
||||||
import org.briarproject.android.sharing.ShareBlogActivity;
|
import org.briarproject.android.sharing.ShareBlogActivity;
|
||||||
import org.briarproject.android.sharing.SharingStatusBlogActivity;
|
import org.briarproject.android.sharing.SharingStatusBlogActivity;
|
||||||
import org.briarproject.android.util.BriarRecyclerView;
|
import org.briarproject.android.util.BriarRecyclerView;
|
||||||
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
private boolean myBlog;
|
private boolean myBlog;
|
||||||
private BlogPostAdapter adapter;
|
private BlogPostAdapter adapter;
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private MenuItem deleteButton = null;
|
private MenuItem deleteButton;
|
||||||
|
|
||||||
static BlogFragment newInstance(GroupId groupId, String name,
|
static BlogFragment newInstance(GroupId groupId, String name,
|
||||||
boolean myBlog, boolean isNew) {
|
boolean myBlog, boolean isNew) {
|
||||||
@@ -85,7 +85,7 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
|
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
byte[] b = args.getByteArray(GROUP_ID);
|
byte[] b = args.getByteArray(GROUP_ID);
|
||||||
if (b == null) throw new IllegalStateException("No Group found.");
|
if (b == null) throw new IllegalStateException("No group ID in args");
|
||||||
groupId = new GroupId(b);
|
groupId = new GroupId(b);
|
||||||
blogName = args.getString(BLOG_NAME);
|
blogName = args.getString(BLOG_NAME);
|
||||||
myBlog = args.getBoolean(IS_MY_BLOG);
|
myBlog = args.getBoolean(IS_MY_BLOG);
|
||||||
@@ -126,19 +126,21 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
loadData(false);
|
|
||||||
if (!myBlog) checkIfBlogCanBeDeleted();
|
if (!myBlog) checkIfBlogCanBeDeleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
loadBlogPosts(false);
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
adapter.clear();
|
||||||
|
list.showProgressBar();
|
||||||
list.stopPeriodicUpdate();
|
list.stopPeriodicUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,41 +210,49 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlogPostAdded(BlogPostItem post, boolean local) {
|
public void onBlogPostAdded(BlogPostHeader header, final boolean local) {
|
||||||
adapter.add(post);
|
blogController.loadBlogPost(header,
|
||||||
if (local) list.scrollToPosition(0);
|
new UiResultExceptionHandler<BlogPostItem, DbException>(
|
||||||
|
getActivity()) {
|
||||||
|
@Override
|
||||||
|
public void onResultUi(BlogPostItem post) {
|
||||||
|
adapter.add(post);
|
||||||
|
if (local) list.scrollToPosition(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO: Decide how to handle errors in the UI
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadData(final boolean reload) {
|
void loadBlogPosts(final boolean reload) {
|
||||||
blogController.loadBlog(groupId, reload,
|
blogController.loadBlogPosts(
|
||||||
new UiResultHandler<Boolean>(getActivity()) {
|
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
||||||
|
getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Boolean result) {
|
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||||
if (result) {
|
if (posts.size() > 0) {
|
||||||
Collection<BlogPostItem> posts =
|
adapter.addAll(posts);
|
||||||
blogController.getBlogPosts();
|
if (reload) list.scrollToPosition(0);
|
||||||
if (posts.size() > 0) {
|
|
||||||
adapter.addAll(posts);
|
|
||||||
if (reload) list.scrollToPosition(0);
|
|
||||||
} else {
|
|
||||||
list.showData();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getActivity(),
|
list.showData();
|
||||||
R.string.blogs_blog_failed_to_load,
|
|
||||||
LENGTH_SHORT).show();
|
|
||||||
getActivity().supportFinishAfterTransition();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO: Decide how to handle errors in the UI
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload() {
|
|
||||||
loadData(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkIfBlogCanBeDeleted() {
|
private void checkIfBlogCanBeDeleted() {
|
||||||
blogController.canDeleteBlog(groupId,
|
blogController.canDeleteBlog(
|
||||||
new UiResultExceptionHandler<Boolean, DbException>(
|
new UiResultExceptionHandler<Boolean, DbException>(
|
||||||
getActivity()) {
|
getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
@@ -251,9 +261,11 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
showDeleteButton();
|
showDeleteButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExceptionUi(DbException exception) {
|
public void onExceptionUi(DbException exception) {
|
||||||
// nothing to do here, delete button is already hidden
|
// TODO: Decide how to handle errors in the UI
|
||||||
|
getActivity().finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -290,15 +302,20 @@ public class BlogFragment extends BaseFragment implements BlogPostListener {
|
|||||||
|
|
||||||
private void deleteBlog() {
|
private void deleteBlog() {
|
||||||
blogController.deleteBlog(
|
blogController.deleteBlog(
|
||||||
new UiResultHandler<Boolean>(getActivity()) {
|
new UiResultExceptionHandler<Void, DbException>(getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Boolean result) {
|
public void onResultUi(Void result) {
|
||||||
if (!result) return;
|
|
||||||
Toast.makeText(getActivity(),
|
Toast.makeText(getActivity(),
|
||||||
R.string.blogs_blog_removed, LENGTH_SHORT)
|
R.string.blogs_blog_removed, LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
getActivity().supportFinishAfterTransition();
|
getActivity().supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
// TODO: Decide how to handle errors in the UI
|
||||||
|
getActivity().finish();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package org.briarproject.android.blogs;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v7.util.SortedList;
|
import android.support.v7.util.SortedList;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
@@ -97,7 +95,7 @@ class BlogPostAdapter extends
|
|||||||
ui.layout.setOnClickListener(new View.OnClickListener() {
|
ui.layout.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.onBlogPostClick(ui.getAdapterPosition(), post);
|
listener.onBlogPostClick(post);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -132,15 +130,12 @@ class BlogPostAdapter extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class BlogPostHolder extends RecyclerView.ViewHolder {
|
static class BlogPostHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
private final ViewGroup layout;
|
private final ViewGroup layout;
|
||||||
private final CircleImageView avatar;
|
private final CircleImageView avatar;
|
||||||
private final TextView author;
|
private final TextView author;
|
||||||
private final TrustIndicatorView trust;
|
private final TrustIndicatorView trust;
|
||||||
private final TextView date;
|
private final TextView date;
|
||||||
private final TextView unread;
|
|
||||||
private final ImageView chat;
|
|
||||||
private final ImageView comment;
|
|
||||||
private final TextView title;
|
|
||||||
private final TextView body;
|
private final TextView body;
|
||||||
|
|
||||||
BlogPostHolder(View v) {
|
BlogPostHolder(View v) {
|
||||||
@@ -151,16 +146,12 @@ class BlogPostAdapter extends
|
|||||||
author = (TextView) v.findViewById(R.id.authorName);
|
author = (TextView) v.findViewById(R.id.authorName);
|
||||||
trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
|
trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
|
||||||
date = (TextView) v.findViewById(R.id.dateView);
|
date = (TextView) v.findViewById(R.id.dateView);
|
||||||
unread = (TextView) v.findViewById(R.id.newView);
|
|
||||||
chat = (ImageView) v.findViewById(R.id.chatView);
|
|
||||||
comment = (ImageView) v.findViewById(R.id.commentView);
|
|
||||||
title = (TextView) v.findViewById(R.id.titleView);
|
|
||||||
body = (TextView) v.findViewById(R.id.bodyView);
|
body = (TextView) v.findViewById(R.id.bodyView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnBlogPostClickListener {
|
interface OnBlogPostClickListener {
|
||||||
void onBlogPostClick(int position, BlogPostItem post);
|
void onBlogPostClick(BlogPostItem post);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@@ -10,16 +9,15 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
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.controller.handler.UiResultHandler;
|
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.android.fragment.BaseFragment;
|
import org.briarproject.android.fragment.BaseFragment;
|
||||||
import org.briarproject.android.util.AndroidUtils;
|
import org.briarproject.android.util.AndroidUtils;
|
||||||
import org.briarproject.android.util.TrustIndicatorView;
|
import org.briarproject.android.util.TrustIndicatorView;
|
||||||
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.sync.GroupId;
|
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
@@ -30,31 +28,27 @@ import javax.inject.Inject;
|
|||||||
import im.delight.android.identicons.IdenticonDrawable;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
|
||||||
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
|
||||||
import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
|
import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
|
||||||
|
|
||||||
public class BlogPostFragment extends BaseFragment {
|
public class BlogPostFragment extends BaseFragment {
|
||||||
|
|
||||||
public final static String TAG = BlogPostFragment.class.getName();
|
public final static String TAG = BlogPostFragment.class.getName();
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
private static final Logger LOG = Logger.getLogger(TAG);
|
||||||
|
private static final String BLOG_POST_ID = "briar.BLOG_POST_ID";
|
||||||
|
|
||||||
private final static String BLOG_POST_ID = "briar.BLOG_NAME";
|
|
||||||
|
|
||||||
private GroupId groupId;
|
|
||||||
private MessageId postId;
|
private MessageId postId;
|
||||||
private BlogPostViewHolder ui;
|
private BlogPostViewHolder ui;
|
||||||
private BlogPostItem post = null;
|
private BlogPostItem post;
|
||||||
private Runnable refresher = null;
|
private Runnable refresher;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BlogController blogController;
|
BlogController blogController;
|
||||||
|
|
||||||
static BlogPostFragment newInstance(GroupId groupId, MessageId postId) {
|
static BlogPostFragment newInstance(MessageId postId) {
|
||||||
BlogPostFragment f = new BlogPostFragment();
|
BlogPostFragment f = new BlogPostFragment();
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putByteArray(GROUP_ID, groupId.getBytes());
|
|
||||||
bundle.putByteArray(BLOG_POST_ID, postId.getBytes());
|
bundle.putByteArray(BLOG_POST_ID, postId.getBytes());
|
||||||
|
|
||||||
f.setArguments(bundle);
|
f.setArguments(bundle);
|
||||||
@@ -65,15 +59,11 @@ public class BlogPostFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
byte[] b = getArguments().getByteArray(GROUP_ID);
|
byte[] b = getArguments().getByteArray(BLOG_POST_ID);
|
||||||
if (b == null) throw new IllegalStateException("No Group found.");
|
if (b == null) throw new IllegalStateException("No post ID in args");
|
||||||
groupId = new GroupId(b);
|
postId = new MessageId(b);
|
||||||
byte[] p = getArguments().getByteArray(BLOG_POST_ID);
|
|
||||||
if (p == null) throw new IllegalStateException("No MessageId found.");
|
|
||||||
postId = new MessageId(p);
|
|
||||||
|
|
||||||
View v = inflater.inflate(R.layout.fragment_blog_post, container,
|
View v = inflater.inflate(R.layout.fragment_blog_post, container,
|
||||||
false);
|
false);
|
||||||
@@ -89,21 +79,20 @@ public class BlogPostFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
blogController.loadBlog(groupId, false,
|
blogController.loadBlogPost(postId,
|
||||||
new UiResultHandler<Boolean>((Activity) listener) {
|
new UiResultExceptionHandler<BlogPostItem, DbException>(
|
||||||
|
getActivity()) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Boolean result) {
|
public void onResultUi(BlogPostItem post) {
|
||||||
listener.hideLoadingScreen();
|
listener.hideLoadingScreen();
|
||||||
if (result) {
|
BlogPostFragment.this.post = post;
|
||||||
post = blogController.getBlogPost(postId);
|
bind();
|
||||||
if (post != null) {
|
}
|
||||||
bind();
|
|
||||||
}
|
@Override
|
||||||
} else {
|
public void onExceptionUi(DbException exception) {
|
||||||
Toast.makeText(getActivity(),
|
// TODO: Decide how to handle errors in the UI
|
||||||
R.string.blogs_blog_post_failed_to_load,
|
getActivity().finish();
|
||||||
LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -157,14 +146,15 @@ public class BlogPostFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class BlogPostViewHolder {
|
private static class BlogPostViewHolder {
|
||||||
private ImageView avatar;
|
|
||||||
private TextView authorName;
|
|
||||||
private TrustIndicatorView trust;
|
|
||||||
private TextView date;
|
|
||||||
private TextView title;
|
|
||||||
private TextView body;
|
|
||||||
|
|
||||||
BlogPostViewHolder(View v) {
|
private final ImageView avatar;
|
||||||
|
private final TextView authorName;
|
||||||
|
private final TrustIndicatorView trust;
|
||||||
|
private final TextView date;
|
||||||
|
private final TextView title;
|
||||||
|
private final TextView body;
|
||||||
|
|
||||||
|
private BlogPostViewHolder(View v) {
|
||||||
avatar = (ImageView) v.findViewById(R.id.avatar);
|
avatar = (ImageView) v.findViewById(R.id.avatar);
|
||||||
authorName = (TextView) v.findViewById(R.id.authorName);
|
authorName = (TextView) v.findViewById(R.id.authorName);
|
||||||
trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
|
trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.android.blogs;
|
package org.briarproject.android.blogs;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.api.blogs.BlogPostHeader;
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
@@ -31,14 +32,11 @@ class BlogPostItem implements Comparable<BlogPostItem> {
|
|||||||
return groupId;
|
return groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return header.getTitle();
|
return header.getTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getBody() {
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTimestamp() {
|
public long getTimestamp() {
|
||||||
return header.getTimestamp();
|
return header.getTimestamp();
|
||||||
}
|
}
|
||||||
@@ -55,18 +53,22 @@ class BlogPostItem implements Comparable<BlogPostItem> {
|
|||||||
return header.getAuthorStatus();
|
return header.getAuthorStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRead(boolean read) {
|
public byte[] getBody() {
|
||||||
this.read = read;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRead() {
|
public boolean isRead() {
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRead(boolean read) {
|
||||||
|
this.read = read;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(@NonNull BlogPostItem other) {
|
public int compareTo(@NonNull BlogPostItem other) {
|
||||||
if (this == other) return 0;
|
if (this == other) return 0;
|
||||||
// The blog with the newest message comes first
|
// The newest post comes first
|
||||||
long aTime = getTimeReceived(), bTime = other.getTimeReceived();
|
long aTime = getTimeReceived(), bTime = other.getTimeReceived();
|
||||||
if (aTime > bTime) return -1;
|
if (aTime > bTime) return -1;
|
||||||
if (aTime < bTime) return 1;
|
if (aTime < bTime) return 1;
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ public class FeedFragment extends BaseFragment implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBlogPostClick(int position, BlogPostItem post) {
|
public void onBlogPostClick(BlogPostItem post) {
|
||||||
byte[] groupId = post.getGroupId().getBytes();
|
byte[] groupId = post.getGroupId().getBytes();
|
||||||
String name = getString(R.string.blogs_personal_blog,
|
String name = getString(R.string.blogs_personal_blog,
|
||||||
post.getAuthor().getName());
|
post.getAuthor().getName());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.android.controller.handler;
|
package org.briarproject.android.controller.handler;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
public abstract class UiResultExceptionHandler<R, E extends Exception>
|
public abstract class UiResultExceptionHandler<R, E extends Exception>
|
||||||
implements ResultExceptionHandler<R, E> {
|
implements ResultExceptionHandler<R, E> {
|
||||||
@@ -31,7 +32,9 @@ public abstract class UiResultExceptionHandler<R, E extends Exception>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public abstract void onResultUi(R result);
|
public abstract void onResultUi(R result);
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public abstract void onExceptionUi(E exception);
|
public abstract void onExceptionUi(E exception);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.android.controller.handler;
|
package org.briarproject.android.controller.handler;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
public abstract class UiResultHandler<R> implements ResultHandler<R> {
|
public abstract class UiResultHandler<R> implements ResultHandler<R> {
|
||||||
|
|
||||||
@@ -20,5 +21,6 @@ public abstract class UiResultHandler<R> implements ResultHandler<R> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
public abstract void onResultUi(R result);
|
public abstract void onResultUi(R result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import org.briarproject.api.identity.LocalAuthor;
|
|||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
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 org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@@ -44,8 +43,10 @@ public interface BlogManager {
|
|||||||
/** Returns all blogs to which the user subscribes. */
|
/** Returns all blogs to which the user subscribes. */
|
||||||
Collection<Blog> getBlogs() throws DbException;
|
Collection<Blog> getBlogs() throws DbException;
|
||||||
|
|
||||||
|
/** Returns the header of the blog post with the given ID. */
|
||||||
|
BlogPostHeader getPostHeader(MessageId m) throws DbException;
|
||||||
|
|
||||||
/** Returns the body of the blog post with the given ID. */
|
/** Returns the body of the blog post with the given ID. */
|
||||||
@Nullable
|
|
||||||
byte[] getPostBody(MessageId m) throws DbException;
|
byte[] getPostBody(MessageId m) throws DbException;
|
||||||
|
|
||||||
/** Returns the headers of all posts in the given blog. */
|
/** Returns the headers of all posts in the given blog. */
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.briarproject.api.sync.Message;
|
|||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
import org.briarproject.clients.BdfIncomingMessageHook;
|
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -337,10 +336,26 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
public BlogPostHeader getPostHeader(MessageId m) throws DbException {
|
||||||
|
Transaction txn = db.startTransaction(true);
|
||||||
|
try {
|
||||||
|
BdfDictionary meta =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, m);
|
||||||
|
BlogPostHeader h = getPostHeaderFromMetadata(txn, m, meta);
|
||||||
|
txn.setComplete();
|
||||||
|
return h;
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public byte[] getPostBody(MessageId m) throws DbException {
|
public byte[] getPostBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
BdfList message = clientHelper.getMessageAsList(m);
|
BdfList message = clientHelper.getMessageAsList(m);
|
||||||
|
if (message == null) throw new DbException();
|
||||||
return getPostBody(message);
|
return getPostBody(message);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
@@ -358,24 +373,23 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
|||||||
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|
||||||
Map<MessageId, BdfDictionary> metadata;
|
Transaction txn = db.startTransaction(true);
|
||||||
try {
|
try {
|
||||||
metadata = clientHelper.getMessageMetadataAsDictionary(g);
|
Map<MessageId, BdfDictionary> metadata =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||||
|
List<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
|
||||||
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
|
BlogPostHeader h = getPostHeaderFromMetadata(txn,
|
||||||
|
entry.getKey(), entry.getValue());
|
||||||
|
headers.add(h);
|
||||||
|
}
|
||||||
|
txn.setComplete();
|
||||||
|
return headers;
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
Collection<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
|
|
||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
|
||||||
try {
|
|
||||||
BdfDictionary meta = entry.getValue();
|
|
||||||
BlogPostHeader h =
|
|
||||||
getPostHeaderFromMetadata(null, entry.getKey(), meta);
|
|
||||||
headers.add(h);
|
|
||||||
} catch (FormatException e) {
|
|
||||||
throw new DbException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return headers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -400,7 +414,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
|||||||
return d.getString(KEY_DESCRIPTION, "");
|
return d.getString(KEY_DESCRIPTION, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlogPostHeader getPostHeaderFromMetadata(@Nullable Transaction txn,
|
private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
|
||||||
MessageId id, BdfDictionary meta)
|
MessageId id, BdfDictionary meta)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
|
|
||||||
@@ -414,11 +428,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
|||||||
byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
|
byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
|
||||||
Author author = new Author(authorId, name, publicKey);
|
Author author = new Author(authorId, name, publicKey);
|
||||||
Status authorStatus;
|
Status authorStatus;
|
||||||
if (txn == null)
|
authorStatus = identityManager.getAuthorStatus(txn, authorId);
|
||||||
authorStatus = identityManager.getAuthorStatus(authorId);
|
|
||||||
else {
|
|
||||||
authorStatus = identityManager.getAuthorStatus(txn, authorId);
|
|
||||||
}
|
|
||||||
|
|
||||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||||
boolean read = meta.getBoolean(KEY_READ);
|
boolean read = meta.getBoolean(KEY_READ);
|
||||||
|
|||||||
Reference in New Issue
Block a user