diff --git a/briar-android/res/drawable/ic_chat.xml b/briar-android/res/drawable/ic_chat.xml
deleted file mode 100644
index 04f9fdcdf..000000000
--- a/briar-android/res/drawable/ic_chat.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
diff --git a/briar-android/res/drawable/ic_expand_more_black_24dp.xml b/briar-android/res/drawable/ic_expand_more_black_24dp.xml
deleted file mode 100644
index 8d57dbc10..000000000
--- a/briar-android/res/drawable/ic_expand_more_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/briar-android/res/layout/activity_fragment_container.xml b/briar-android/res/layout/activity_fragment_container.xml
index e6c20760f..8bf59ee5a 100644
--- a/briar-android/res/layout/activity_fragment_container.xml
+++ b/briar-android/res/layout/activity_fragment_container.xml
@@ -3,4 +3,14 @@
android:id="@+id/fragmentContainer"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"/>
\ No newline at end of file
+ android:layout_height="match_parent">
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/dropdown_author.xml b/briar-android/res/layout/dropdown_author.xml
deleted file mode 100644
index 1af9c5965..000000000
--- a/briar-android/res/layout/dropdown_author.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_blog_post.xml b/briar-android/res/layout/fragment_blog_post.xml
index 7a0df0d0b..18ec1f417 100644
--- a/briar-android/res/layout/fragment_blog_post.xml
+++ b/briar-android/res/layout/fragment_blog_post.xml
@@ -6,7 +6,12 @@
+ android:layout_height="wrap_content"
+ android:descendantFocusability="beforeDescendants"
+ android:focusable="true"
+ android:focusableInTouchMode="true">
+
+
+
diff --git a/briar-android/res/layout/activity_blog.xml b/briar-android/res/layout/fragment_blog_post_pager.xml
similarity index 79%
rename from briar-android/res/layout/activity_blog.xml
rename to briar-android/res/layout/fragment_blog_post_pager.xml
index 3ff8d409e..626dcaf26 100644
--- a/briar-android/res/layout/activity_blog.xml
+++ b/briar-android/res/layout/fragment_blog_post_pager.xml
@@ -1,15 +1,14 @@
+ android:layout_height="match_parent"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_blogs_my.xml b/briar-android/res/layout/fragment_blogs_my.xml
deleted file mode 100644
index 288adfaa3..000000000
--- a/briar-android/res/layout/fragment_blogs_my.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
diff --git a/briar-android/res/layout/list_item_blog_comment.xml b/briar-android/res/layout/list_item_blog_comment.xml
index bdbe42842..1be478ab2 100644
--- a/briar-android/res/layout/list_item_blog_comment.xml
+++ b/briar-android/res/layout/list_item_blog_comment.xml
@@ -29,7 +29,6 @@
android:paddingLeft="@dimen/listitem_vertical_margin"
android:paddingRight="@dimen/listitem_vertical_margin"
android:textColor="@color/briar_text_secondary"
- android:textIsSelectable="true"
android:textSize="@dimen/text_size_small"
tools:text="This is a comment that appears below a blog post. Usually, it is expected to be rather short. Not much longer than this one."/>
diff --git a/briar-android/res/layout/list_item_blog_post.xml b/briar-android/res/layout/list_item_blog_post.xml
index 8b136f2b7..d3cd14d48 100644
--- a/briar-android/res/layout/list_item_blog_post.xml
+++ b/briar-android/res/layout/list_item_blog_post.xml
@@ -6,7 +6,8 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:foreground="?android:attr/selectableItemBackground">
diff --git a/briar-android/res/menu/blogs_my_actions.xml b/briar-android/res/menu/blogs_my_actions.xml
deleted file mode 100644
index 9a3ce5b49..000000000
--- a/briar-android/res/menu/blogs_my_actions.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
\ No newline at end of file
diff --git a/briar-android/res/values/color.xml b/briar-android/res/values/color.xml
index e7cb49b15..0ac3140ee 100644
--- a/briar-android/res/values/color.xml
+++ b/briar-android/res/values/color.xml
@@ -35,7 +35,6 @@
#FFFFFF
#61000000
- @color/briar_blue_dark
#cfd2d4
#ffffff
\ No newline at end of file
diff --git a/briar-android/res/values/dimens.xml b/briar-android/res/values/dimens.xml
index 9f62ff094..c8e7eca7d 100644
--- a/briar-android/res/values/dimens.xml
+++ b/briar-android/res/values/dimens.xml
@@ -27,7 +27,6 @@
53dp
2dp
40dp
- 32dp
48dp
2dp
30sp
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index ba764c356..e86654509 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -73,6 +73,7 @@
Offline
Send
No data
+ …
It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.
@@ -199,18 +200,16 @@
Nobody
- Feed
- My Blogs
Create Blog
Add new Blog
Blog title (cannot be changed later)
A short description of your new blog
Potential readers may or may not subscribe to your blog based on the content of the description.
- You don\'t have any blogs.\n\nWhy don\'t you create one now by clicking the plus in the top right screen corner?
Blog created
This blog is empty
This blog is currently empty.\n\nEither the author hasn\'t written anything yet, or the person who shared this blog with you needs to come online, so posts can be synchronized.
NEW
+ read more
Write Blog Post
Add a title (optional)
Type your blog post here
@@ -231,7 +230,6 @@
Blog List
Available Blogs
- Drafts
Share Blog
@@ -312,7 +310,6 @@
Anonymous
- New identity\u2026
New Identity
Create Identity
Identity created
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 2a0d82c4b..baee29d41 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -6,8 +6,11 @@ import org.briarproject.android.blogs.BlogActivity;
import org.briarproject.android.blogs.BlogFragment;
import org.briarproject.android.blogs.BlogListFragment;
import org.briarproject.android.blogs.BlogPostFragment;
+import org.briarproject.android.blogs.BlogPostPagerFragment;
import org.briarproject.android.blogs.CreateBlogActivity;
+import org.briarproject.android.blogs.FeedPostFragment;
import org.briarproject.android.blogs.FeedFragment;
+import org.briarproject.android.blogs.FeedPostPagerFragment;
import org.briarproject.android.blogs.ReblogActivity;
import org.briarproject.android.blogs.ReblogFragment;
import org.briarproject.android.blogs.RssFeedImportActivity;
@@ -92,6 +95,10 @@ public interface ActivityComponent {
void inject(BlogFragment fragment);
void inject(BlogPostFragment fragment);
+ void inject(FeedPostFragment fragment);
+
+ void inject(BlogPostPagerFragment fragment);
+ void inject(FeedPostPagerFragment fragment);
void inject(ReblogFragment fragment);
diff --git a/briar-android/src/org/briarproject/android/Destroyable.java b/briar-android/src/org/briarproject/android/Destroyable.java
index a9a9ef8ce..17c7dc850 100644
--- a/briar-android/src/org/briarproject/android/Destroyable.java
+++ b/briar-android/src/org/briarproject/android/Destroyable.java
@@ -2,7 +2,7 @@ package org.briarproject.android;
import android.support.annotation.UiThread;
-interface Destroyable {
+public interface Destroyable {
@UiThread
boolean hasBeenDestroyed();
diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
index 6381bc9cd..d4bacf55a 100644
--- a/briar-android/src/org/briarproject/android/NavDrawerActivity.java
+++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java
@@ -74,7 +74,9 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
super.onNewIntent(intent);
exitIfStartupFailed(intent);
checkAuthorHandle(intent);
- clearBackStack();
+ // FIXME why was the stack cleared here?
+ // This prevents state from being restored properly
+// clearBackStack();
if (intent.getBooleanExtra(INTENT_FORUMS, false)) {
startFragment(ForumListFragment.newInstance());
}
@@ -248,7 +250,6 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
@Override
public void hideLoadingScreen() {
drawerLayout.setDrawerLockMode(LOCK_MODE_UNLOCKED);
- CustomAnimations.animateHeight(toolbar, true, 250);
progressViewGroup.setVisibility(INVISIBLE);
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java
index ab513217a..3c4f1bc21 100644
--- a/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/blogs/BaseControllerImpl.java
@@ -59,6 +59,9 @@ abstract class BaseControllerImpl extends DbControllerImpl
@Override
@CallSuper
public void onStart() {
+ if (listener == null)
+ throw new IllegalStateException(
+ "OnBlogPostAddedListener needs to be attached");
eventBus.addListener(this);
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
new file mode 100644
index 000000000..06153e422
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
@@ -0,0 +1,109 @@
+package org.briarproject.android.blogs;
+
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+
+import org.briarproject.R;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.api.db.DbException;
+
+import java.util.logging.Logger;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
+
+public abstract class BasePostFragment extends BaseFragment {
+
+ private final Logger LOG =
+ Logger.getLogger(BasePostFragment.class.getName());
+
+ private View view;
+ private ProgressBar progressBar;
+ private BlogPostViewHolder ui;
+ private BlogPostItem post;
+ private Runnable refresher;
+
+ @CallSuper
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ setHasOptionsMenu(true);
+
+ view = inflater.inflate(R.layout.fragment_blog_post, container,
+ false);
+ progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
+ progressBar.setVisibility(VISIBLE);
+ ui = new BlogPostViewHolder(view);
+ return view;
+ }
+
+ @CallSuper
+ @Override
+ public void onStart() {
+ super.onStart();
+ startPeriodicUpdate();
+ }
+
+ @CallSuper
+ @Override
+ public void onStop() {
+ super.onStop();
+ stopPeriodicUpdate();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getActivity().onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @UiThread
+ protected void onBlogPostLoaded(BlogPostItem post) {
+ progressBar.setVisibility(INVISIBLE);
+ this.post = post;
+ ui.bindItem(post);
+ }
+
+ @UiThread
+ protected void onBlogPostLoadException(DbException exception) {
+ // TODO: Decide how to handle errors in the UI
+ finish();
+ }
+
+ private void startPeriodicUpdate() {
+ refresher = new Runnable() {
+ @Override
+ public void run() {
+ if (ui == null) return;
+ LOG.info("Updating Content...");
+
+ ui.updateDate(post.getTimestamp());
+ view.postDelayed(refresher, MIN_RESOLUTION);
+ }
+ };
+ LOG.info("Adding Handler Callback");
+ view.postDelayed(refresher, MIN_RESOLUTION);
+ }
+
+ private void stopPeriodicUpdate() {
+ if (refresher != null && ui != null) {
+ LOG.info("Removing Handler Callback");
+ view.removeCallbacks(refresher);
+ }
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BasePostPagerFragment.java b/briar-android/src/org/briarproject/android/blogs/BasePostPagerFragment.java
new file mode 100644
index 000000000..e27c1470f
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BasePostPagerFragment.java
@@ -0,0 +1,177 @@
+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);
+
+ protected void onBlogPostsLoaded(MessageId select,
+ Collection posts) {
+
+ postId = null;
+ postPagerAdapter.setPosts(posts);
+ selectPost(select);
+ }
+
+ 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 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 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;
+ }
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
index 249d0ff7c..bb7a5cc8d 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java
@@ -2,56 +2,29 @@ package org.briarproject.android.blogs;
import android.content.Intent;
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.ViewGroup;
import android.widget.ProgressBar;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
-import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener;
import org.briarproject.android.blogs.BlogPostAdapter.OnBlogPostClickListener;
-import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
-import org.briarproject.api.blogs.BlogPostHeader;
-import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.MessageId;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
import javax.inject.Inject;
-import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
public class BlogActivity extends BriarActivity implements
- OnBlogPostAddedListener,
OnBlogPostClickListener, BaseFragmentListener {
static final int REQUEST_WRITE_POST = 1;
static final int REQUEST_SHARE = 2;
- public static final String BLOG_NAME = "briar.BLOG_NAME";
+ static final String BLOG_NAME = "briar.BLOG_NAME";
static final String IS_NEW_BLOG = "briar.IS_NEW_BLOG";
- public static final String POST_ID = "briar.POST_ID";
-
- private GroupId groupId;
private ProgressBar progressBar;
- private ViewPager pager;
- private BlogPagerAdapter blogPagerAdapter;
- private BlogPostPagerAdapter postPagerAdapter;
- private String blogName;
- private boolean isNew;
- private MessageId savedPostId;
@Inject
BlogController blogController;
@@ -64,62 +37,24 @@ public class BlogActivity extends BriarActivity implements
Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID);
if (b == null) throw new IllegalStateException("No group ID in intent");
- groupId = new GroupId(b);
+ GroupId groupId = new GroupId(b);
blogController.setGroupId(groupId);
// Name of the blog
- blogName = i.getStringExtra(BLOG_NAME);
+ String blogName = i.getStringExtra(BLOG_NAME);
if (blogName != null) setTitle(blogName);
// Was this blog just created?
- isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
+ boolean isNew = i.getBooleanExtra(IS_NEW_BLOG, false);
- setContentView(R.layout.activity_blog);
-
- pager = (ViewPager) findViewById(R.id.pager);
+ setContentView(R.layout.activity_fragment_container);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
- blogPagerAdapter = new BlogPagerAdapter(getSupportFragmentManager());
- postPagerAdapter = new BlogPostPagerAdapter(
- getSupportFragmentManager());
-
- if (state == null || state.getByteArray(POST_ID) == null) {
- // The blog fragment has its own progress bar
- hideLoadingScreen();
- pager.setAdapter(blogPagerAdapter);
- savedPostId = null;
- } else {
- // Adapter will be set in selectPostInPostPager()
- 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
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- MessageId selected = getSelectedPostInPostPager();
- if (selected != null)
- outState.putByteArray(POST_ID, selected.getBytes());
- }
-
- @Override
- public void onBackPressed() {
- if (pager.getAdapter() == postPagerAdapter) {
- pager.setAdapter(blogPagerAdapter);
- savedPostId = null;
- } else {
- super.onBackPressed();
+ if (state == null) {
+ BlogFragment f = BlogFragment.newInstance(groupId, blogName, isNew);
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.fragmentContainer, f, f.getUniqueTag())
+ .commit();
}
}
@@ -128,6 +63,15 @@ public class BlogActivity extends BriarActivity implements
component.inject(this);
}
+ @Override
+ public void onBlogPostClick(BlogPostItem post) {
+ BlogPostPagerFragment f = BlogPostPagerFragment.newInstance(post.getId());
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.fragmentContainer, f, f.getUniqueTag())
+ .addToBackStack(f.getUniqueTag())
+ .commit();
+ }
+
@Override
public void showLoadingScreen(boolean isBlocking, int stringId) {
progressBar.setVisibility(VISIBLE);
@@ -135,176 +79,10 @@ public class BlogActivity extends BriarActivity implements
@Override
public void hideLoadingScreen() {
- progressBar.setVisibility(GONE);
+ progressBar.setVisibility(INVISIBLE);
}
@Override
public void onFragmentCreated(String tag) {
-
}
-
- @Override
- public void onBlogPostClick(BlogPostItem post) {
- loadBlogPosts(post.getId());
- }
-
- private void loadBlogPosts(final MessageId select) {
- blogController.loadBlogPosts(
- new UiResultExceptionHandler, DbException>(
- this) {
- @Override
- public void onResultUi(Collection posts) {
- hideLoadingScreen();
- savedPostId = null;
- postPagerAdapter.setPosts(posts);
- selectPostInPostPager(select);
- }
-
- @Override
- public void onExceptionUi(DbException exception) {
- // TODO: Decide how to handle errors in the UI
- finish();
- }
- });
- }
-
- @Override
- public void onBlogPostAdded(BlogPostHeader header, boolean local) {
- if (pager.getAdapter() == postPagerAdapter) {
- loadBlogPost(header);
- } else {
- BlogFragment f = blogPagerAdapter.getFragment();
- if (f != null && f.isVisible()) f.onBlogPostAdded(header, local);
- }
- }
-
- private void loadBlogPost(BlogPostHeader header) {
- blogController.loadBlogPost(header,
- new UiResultExceptionHandler(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
- protected void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- // The BlogPostAddedEvent arrives when the controller is not listening,
- // so we need to manually reload the blog posts :(
- if (requestCode == REQUEST_WRITE_POST && resultCode == RESULT_OK) {
- if (pager.getAdapter() == postPagerAdapter) {
- MessageId selected = getSelectedPostInPostPager();
- 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 BlogFragment fragment = null;
-
- private BlogPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public int getCount() {
- return 1;
- }
-
- @Override
- public Fragment getItem(int position) {
- return BlogFragment.newInstance(groupId, blogName, isNew);
- }
-
- @Override
- public Object instantiateItem(ViewGroup container, int position) {
- // save a reference to the single fragment here for later
- fragment =
- (BlogFragment) super.instantiateItem(container, position);
- return fragment;
- }
-
- private BlogFragment getFragment() {
- return fragment;
- }
- }
-
- @UiThread
- private static class BlogPostPagerAdapter
- extends FragmentStatePagerAdapter {
-
- private final List posts = new ArrayList<>();
-
- private BlogPostPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
- @Override
- public int getCount() {
- return posts.size();
- }
-
- @Override
- public Fragment getItem(int position) {
- return BlogPostFragment.newInstance(posts.get(position).getId());
- }
-
- private BlogPostItem getPost(int position) {
- return posts.get(position);
- }
-
- private void setPosts(Collection 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();
- }
- }
-
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogController.java b/briar-android/src/org/briarproject/android/blogs/BlogController.java
index 768d29c14..4d5c35cbc 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogController.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogController.java
@@ -21,9 +21,7 @@ public interface BlogController extends BaseController {
void loadBlogPost(MessageId m,
ResultExceptionHandler handler);
- void isMyBlog(ResultExceptionHandler handler);
-
- void canDeleteBlog(ResultExceptionHandler handler);
+ void loadBlog(ResultExceptionHandler handler);
void deleteBlog(ResultExceptionHandler handler);
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
index d70779e34..ad7b7597d 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogControllerImpl.java
@@ -3,6 +3,7 @@ package org.briarproject.android.blogs;
import org.briarproject.android.controller.ActivityLifecycleController;
import org.briarproject.android.controller.handler.ResultExceptionHandler;
import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.BlogPostAddedEvent;
import org.briarproject.api.event.Event;
@@ -13,6 +14,7 @@ import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import java.util.Collection;
+import java.util.Collections;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -33,13 +35,6 @@ public class BlogControllerImpl extends BaseControllerImpl
@Override
public void onActivityCreate() {
- if (activity instanceof OnBlogPostAddedListener) {
- listener = (OnBlogPostAddedListener) activity;
- } else {
- throw new IllegalStateException(
- "An activity that injects the BlogController must " +
- "implement the OnBlogPostAddedListener");
- }
}
@Override
@@ -102,8 +97,8 @@ public class BlogControllerImpl extends BaseControllerImpl
}
@Override
- public void isMyBlog(
- final ResultExceptionHandler handler) {
+ public void loadBlog(
+ final ResultExceptionHandler handler) {
if (groupId == null) throw new IllegalStateException();
runOnDbThread(new Runnable() {
@Override
@@ -111,7 +106,12 @@ public class BlogControllerImpl extends BaseControllerImpl
try {
LocalAuthor a = identityManager.getLocalAuthor();
Blog b = blogManager.getBlog(groupId);
- handler.onResult(b.getAuthor().getId().equals(a.getId()));
+ boolean ours = a.getId().equals(b.getAuthor().getId());
+ boolean removable = blogManager.canBeRemoved(groupId);
+ BlogItem blog = new BlogItem(b,
+ Collections.emptyList(),
+ ours, removable);
+ handler.onResult(blog);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -122,24 +122,6 @@ public class BlogControllerImpl extends BaseControllerImpl
}
- @Override
- public void canDeleteBlog(
- final ResultExceptionHandler 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 handler) {
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
index b3b87a113..4ff2695e3 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -5,8 +5,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
@@ -28,6 +28,7 @@ import org.briarproject.android.sharing.SharingStatusBlogActivity;
import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.Author;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
@@ -59,6 +60,7 @@ public class BlogFragment extends BaseFragment implements
private BlogPostAdapter adapter;
private BriarRecyclerView list;
private MenuItem writeButton, deleteButton;
+ private boolean isMyBlog = false, canDeleteBlog = false;
static BlogFragment newInstance(GroupId groupId, String name,
boolean isNew) {
@@ -114,14 +116,13 @@ public class BlogFragment extends BaseFragment implements
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
- blogController.setGroupId(groupId);
+ blogController.setOnBlogPostAddedListener(this);
}
@Override
public void onStart() {
super.onStart();
- checkIfThisIsMyBlog();
- checkIfBlogCanBeDeleted();
+ loadBlog();
}
@Override
@@ -141,7 +142,9 @@ public class BlogFragment extends BaseFragment implements
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.blogs_blog_actions, menu);
writeButton = menu.findItem(R.id.action_write_blog_post);
+ if (isMyBlog) writeButton.setVisible(true);
deleteButton = menu.findItem(R.id.action_blog_delete);
+ if (canDeleteBlog) deleteButton.setVisible(true);
super.onCreateOptionsMenu(menu, inflater);
}
@@ -161,8 +164,8 @@ public class BlogFragment extends BaseFragment implements
new Intent(getActivity(), WriteBlogPostActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes());
i.putExtra(BLOG_NAME, blogName);
- ActivityCompat.startActivityForResult(getActivity(), i,
- REQUEST_WRITE_POST, options.toBundle());
+ startActivityForResult(i, REQUEST_WRITE_POST,
+ options.toBundle());
return true;
case R.id.action_blog_share:
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class);
@@ -190,9 +193,10 @@ public class BlogFragment extends BaseFragment implements
super.onActivityResult(request, result, data);
if (request == REQUEST_WRITE_POST && result == RESULT_OK) {
- displaySnackbar(R.string.blogs_blog_post_created);
+ displaySnackbar(R.string.blogs_blog_post_created, true);
+ loadBlogPosts(true);
} else if (request == REQUEST_SHARE && result == RESULT_OK) {
- displaySnackbar(R.string.blogs_sharing_snackbar);
+ displaySnackbar(R.string.blogs_sharing_snackbar, true);
}
}
@@ -205,15 +209,15 @@ public class BlogFragment extends BaseFragment implements
public void onBlogPostAdded(BlogPostHeader header, final boolean local) {
blogController.loadBlogPost(header,
new UiResultExceptionHandler(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(BlogPostItem post) {
adapter.add(post);
if (local) {
list.scrollToPosition(0);
- displaySnackbar(R.string.blogs_blog_post_created);
+ displaySnackbar(R.string.blogs_blog_post_created, false);
} else {
- displaySnackbar(R.string.blogs_blog_post_received);
+ displaySnackbar(R.string.blogs_blog_post_received, true);
}
}
@@ -229,7 +233,7 @@ public class BlogFragment extends BaseFragment implements
void loadBlogPosts(final boolean reload) {
blogController.loadBlogPosts(
new UiResultExceptionHandler, DbException>(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(Collection posts) {
if (posts.size() > 0) {
@@ -243,63 +247,68 @@ public class BlogFragment extends BaseFragment implements
@Override
public void onExceptionUi(DbException exception) {
// TODO: Decide how to handle errors in the UI
- getActivity().finish();
+ finish();
}
});
}
- private void checkIfThisIsMyBlog() {
- blogController.canDeleteBlog(
- new UiResultExceptionHandler(
- getActivity()) {
+ private void loadBlog() {
+ blogController.loadBlog(
+ new UiResultExceptionHandler(listener) {
@Override
- public void onResultUi(Boolean isMyBlog) {
- if (isMyBlog) {
+ public void onResultUi(BlogItem blog) {
+ setToolbarTitle(blog.getBlog().getAuthor());
+ if (blog.isOurs())
showWriteButton();
- }
+ if (blog.canBeRemoved())
+ showDeleteButton();
}
@Override
public void onExceptionUi(DbException exception) {
// TODO: Decide how to handle errors in the UI
- getActivity().finish();
+ finish();
}
});
}
- private void checkIfBlogCanBeDeleted() {
- blogController.canDeleteBlog(
- new UiResultExceptionHandler(
- getActivity()) {
- @Override
- public void onResultUi(Boolean canBeDeleted) {
- if (canBeDeleted) {
- showDeleteButton();
- }
- }
+ private void setToolbarTitle(Author a) {
+ String title = getString(R.string.blogs_personal_blog, a.getName());
+ getActivity().setTitle(title);
- @Override
- public void onExceptionUi(DbException exception) {
- // TODO: Decide how to handle errors in the UI
- getActivity().finish();
- }
- });
+ // safe title in intent, so it can be restored automatically
+ Intent intent = getActivity().getIntent();
+ intent.putExtra(BLOG_NAME, title);
}
private void showWriteButton() {
+ isMyBlog = true;
if (writeButton != null)
writeButton.setVisible(true);
}
private void showDeleteButton() {
+ canDeleteBlog = true;
if (deleteButton != null)
deleteButton.setVisible(true);
}
- private void displaySnackbar(int stringId) {
+ private void displaySnackbar(int stringId, boolean scroll) {
Snackbar snackbar =
- Snackbar.make(list, stringId, Snackbar.LENGTH_SHORT);
+ Snackbar.make(list, stringId, Snackbar.LENGTH_LONG);
snackbar.getView().setBackgroundResource(R.color.briar_primary);
+ if (scroll) {
+ View.OnClickListener onClick = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ list.smoothScrollToPosition(0);
+ }
+ };
+ snackbar.setActionTextColor(ContextCompat
+ .getColor(getContext(),
+ R.color.briar_button_positive));
+ snackbar.setAction(R.string.blogs_blog_post_scroll_to, onClick);
+ }
snackbar.show();
}
@@ -323,7 +332,7 @@ public class BlogFragment extends BaseFragment implements
private void deleteBlog() {
blogController.deleteBlog(
- new UiResultExceptionHandler(getActivity()) {
+ new UiResultExceptionHandler(listener) {
@Override
public void onResultUi(Void result) {
Toast.makeText(getActivity(),
@@ -335,7 +344,7 @@ public class BlogFragment extends BaseFragment implements
@Override
public void onExceptionUi(DbException exception) {
// TODO: Decide how to handle errors in the UI
- getActivity().finish();
+ finish();
}
});
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListItem.java b/briar-android/src/org/briarproject/android/blogs/BlogItem.java
similarity index 82%
rename from briar-android/src/org/briarproject/android/blogs/BlogListItem.java
rename to briar-android/src/org/briarproject/android/blogs/BlogItem.java
index 35c9f532b..536d05f24 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogListItem.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogItem.java
@@ -5,15 +5,16 @@ import org.briarproject.api.blogs.BlogPostHeader;
import java.util.Collection;
-class BlogListItem {
+class BlogItem {
private final Blog blog;
private final int postCount;
private final long timestamp;
private final int unread;
- private final boolean ours;
+ private final boolean ours, removable;
- BlogListItem(Blog blog, Collection headers, boolean ours) {
+ BlogItem(Blog blog, Collection headers, boolean ours,
+ boolean removable) {
this.blog = blog;
if (headers.isEmpty()) {
postCount = 0;
@@ -35,6 +36,7 @@ class BlogListItem {
this.unread = unread;
}
this.ours = ours;
+ this.removable = removable;
}
Blog getBlog() {
@@ -64,4 +66,8 @@ class BlogListItem {
boolean isOurs() {
return ours;
}
+
+ boolean canBeRemoved() {
+ return removable;
+ }
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java
index 37f7dd301..b3e55f848 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogListAdapter.java
@@ -29,11 +29,11 @@ import static org.briarproject.android.blogs.BlogActivity.BLOG_NAME;
class BlogListAdapter extends
RecyclerView.Adapter {
- private SortedList blogs = new SortedList<>(
- BlogListItem.class, new SortedList.Callback() {
+ private SortedList blogs = new SortedList<>(
+ BlogItem.class, new SortedList.Callback() {
@Override
- public int compare(BlogListItem a, BlogListItem b) {
+ public int compare(BlogItem a, BlogItem b) {
if (a == b) return 0;
// The blog with the newest message comes first
long aTime = a.getTimestamp(), bTime = b.getTimestamp();
@@ -66,14 +66,14 @@ class BlogListAdapter extends
}
@Override
- public boolean areContentsTheSame(BlogListItem a, BlogListItem b) {
+ public boolean areContentsTheSame(BlogItem a, BlogItem b) {
return a.getBlog().equals(b.getBlog()) &&
a.getTimestamp() == b.getTimestamp() &&
a.getUnreadCount() == b.getUnreadCount();
}
@Override
- public boolean areItemsTheSame(BlogListItem a, BlogListItem b) {
+ public boolean areItemsTheSame(BlogItem a, BlogItem b) {
return a.getBlog().equals(b.getBlog());
}
});
@@ -93,7 +93,7 @@ class BlogListAdapter extends
@Override
public void onBindViewHolder(BlogViewHolder ui, int position) {
- final BlogListItem item = getItem(position);
+ final BlogItem item = getItem(position);
// Avatar
ui.avatar.setText(item.getName().substring(0, 1));
@@ -145,14 +145,14 @@ class BlogListAdapter extends
return blogs.size();
}
- public BlogListItem getItem(int position) {
+ public BlogItem getItem(int position) {
return blogs.get(position);
}
@Nullable
- public BlogListItem getItem(GroupId g) {
+ public BlogItem getItem(GroupId g) {
for (int i = 0; i < blogs.size(); i++) {
- BlogListItem item = blogs.get(i);
+ BlogItem item = blogs.get(i);
if (item.getBlog().getGroup().getId().equals(g)) {
return item;
}
@@ -160,17 +160,17 @@ class BlogListAdapter extends
return null;
}
- public void addAll(Collection items) {
+ public void addAll(Collection items) {
blogs.addAll(items);
}
- void updateItem(BlogListItem item) {
- BlogListItem oldItem = getItem(item.getBlog().getGroup().getId());
+ void updateItem(BlogItem item) {
+ BlogItem oldItem = getItem(item.getBlog().getGroup().getId());
int position = blogs.indexOf(oldItem);
blogs.updateItemAt(position, item);
}
- public void remove(BlogListItem item) {
+ public void remove(BlogItem item) {
blogs.remove(item);
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
index 926ce7f37..ac7b4d504 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java
@@ -3,35 +3,23 @@ package org.briarproject.android.blogs;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
-import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.MessageId;
-import java.util.logging.Logger;
-
import javax.inject.Inject;
-import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
+import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
-public class BlogPostFragment extends BaseFragment {
+public class BlogPostFragment extends BasePostFragment {
public final static String TAG = BlogPostFragment.class.getName();
- private static final Logger LOG = Logger.getLogger(TAG);
- private static final String BLOG_POST_ID = "briar.BLOG_POST_ID";
-
- private View view;
private MessageId postId;
- private BlogPostViewHolder ui;
- private BlogPostItem post;
- private Runnable refresher;
@Inject
BlogController blogController;
@@ -40,7 +28,7 @@ public class BlogPostFragment extends BaseFragment {
BlogPostFragment f = new BlogPostFragment();
Bundle bundle = new Bundle();
- bundle.putByteArray(BLOG_POST_ID, postId.getBytes());
+ bundle.putByteArray(POST_ID, postId.getBytes());
f.setArguments(bundle);
return f;
@@ -50,16 +38,18 @@ public class BlogPostFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
- byte[] b = getArguments().getByteArray(BLOG_POST_ID);
- if (b == null) throw new IllegalStateException("No post ID in args");
- postId = new MessageId(b);
+ Bundle args = getArguments();
+ byte[] p = args.getByteArray(POST_ID);
+ if (p == null) throw new IllegalStateException("No post ID in args");
+ postId = new MessageId(p);
- view = inflater.inflate(R.layout.fragment_blog_post, container,
- false);
- ui = new BlogPostViewHolder(view);
- return view;
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
}
@Override
@@ -72,65 +62,15 @@ public class BlogPostFragment extends BaseFragment {
super.onStart();
blogController.loadBlogPost(postId,
new UiResultExceptionHandler(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(BlogPostItem post) {
- listener.hideLoadingScreen();
- BlogPostFragment.this.post = post;
- ui.bindItem(post);
- startPeriodicUpdate();
+ onBlogPostLoaded(post);
}
-
@Override
public void onExceptionUi(DbException exception) {
- // TODO: Decide how to handle errors in the UI
- finish();
+ onBlogPostLoadException(exception);
}
});
}
-
- @Override
- public void onStop() {
- super.onStop();
- stopPeriodicUpdate();
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- getActivity().onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public String getUniqueTag() {
- return TAG;
- }
-
- private void startPeriodicUpdate() {
- refresher = new Runnable() {
- @Override
- public void run() {
- if (ui == null) return;
- LOG.info("Updating Content...");
-
- ui.updateDate(post.getTimestamp());
- view.postDelayed(refresher, MIN_RESOLUTION);
- }
- };
- LOG.info("Adding Handler Callback");
- view.postDelayed(refresher, MIN_RESOLUTION);
- }
-
- private void stopPeriodicUpdate() {
- if (refresher != null && ui != null) {
- LOG.info("Removing Handler Callback");
- view.removeCallbacks(refresher);
- }
- }
-
}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostPagerFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostPagerFragment.java
new file mode 100644
index 000000000..f0a534b84
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostPagerFragment.java
@@ -0,0 +1,78 @@
+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;
+ }
+
+
+ void loadBlogPosts(final MessageId select) {
+ blogController.loadBlogPosts(
+ new UiResultExceptionHandler, DbException>(
+ listener) {
+ @Override
+ public void onResultUi(Collection posts) {
+ onBlogPostsLoaded(select, posts);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ onBlogPostsLoadedException(exception);
+ }
+ });
+ }
+
+ void loadBlogPost(BlogPostHeader header) {
+ blogController.loadBlogPost(header,
+ new UiResultExceptionHandler(
+ listener) {
+ @Override
+ public void onResultUi(BlogPostItem post) {
+ addPost(post);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO: Decide how to handle errors in the UI
+ finish();
+ }
+ });
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
index 9cc23d079..13a1b3f17 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
@@ -10,6 +10,7 @@ import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
@@ -26,7 +27,9 @@ import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAn
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.android.BriarActivity.GROUP_ID;
-import static org.briarproject.android.blogs.BlogActivity.POST_ID;
+import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
+import static org.briarproject.android.util.AndroidUtils.TEASER_LENGTH;
+import static org.briarproject.android.util.AndroidUtils.getTeaser;
import static org.briarproject.api.blogs.MessageType.POST;
@UiThread
@@ -81,14 +84,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
void bindItem(final BlogPostItem item) {
setTransitionName(item.getId());
- layout.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (listener != null) {
+ if (listener != null) {
+ layout.setClickable(true);
+ layout.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
listener.onBlogPostClick(item);
}
- }
- });
+ });
+ }
// author and date
BlogPostHeader post = item.getPostHeader();
@@ -104,10 +108,18 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
}
// post body
- body.setText(item.getBody());
+ CharSequence bodyText = item.getBody();
+ if (listener == null) {
+ body.setTextIsSelectable(true);
+ } else {
+ body.setTextIsSelectable(false);
+ if (item.getBody().length() > TEASER_LENGTH)
+ bodyText = getTeaser(ctx, item.getBody());
+ }
+ body.setText(bodyText);
// reblog button
- reblogButton.setOnClickListener(new View.OnClickListener() {
+ reblogButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(ctx, ReblogActivity.class);
@@ -154,6 +166,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// TODO make author clickable #624
body.setText(c.getComment());
+ if (listener == null) body.setTextIsSelectable(true);
commentContainer.addView(v);
}
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
index 9e6f57748..41791e962 100644
--- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
@@ -108,7 +108,7 @@ public class FeedFragment extends BaseFragment implements
});
feedController.loadBlogPosts(
new UiResultExceptionHandler, DbException>(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(Collection posts) {
if (posts.isEmpty()) {
@@ -175,7 +175,7 @@ public class FeedFragment extends BaseFragment implements
public void onBlogPostAdded(BlogPostHeader header, final boolean local) {
feedController.loadBlogPost(header,
new UiResultExceptionHandler(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(BlogPostItem post) {
adapter.add(post);
@@ -195,7 +195,12 @@ public class FeedFragment extends BaseFragment implements
@Override
public void onBlogPostClick(BlogPostItem post) {
- // TODO Open detail view of post
+ FeedPostPagerFragment f = FeedPostPagerFragment
+ .newInstance(post.getId());
+ getActivity().getSupportFragmentManager().beginTransaction()
+ .replace(R.id.content_fragment, f, f.getUniqueTag())
+ .addToBackStack(f.getUniqueTag())
+ .commit();
}
@Override
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedPostFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedPostFragment.java
new file mode 100644
index 000000000..442a85ef9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/FeedPostFragment.java
@@ -0,0 +1,85 @@
+package org.briarproject.android.blogs;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.controller.handler.UiResultExceptionHandler;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+
+import javax.inject.Inject;
+
+import static org.briarproject.android.BriarActivity.GROUP_ID;
+import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
+
+public class FeedPostFragment extends BasePostFragment {
+
+ public final static String TAG = FeedPostFragment.class.getName();
+
+ private MessageId postId;
+ private GroupId blogId;
+
+ @Inject
+ FeedController feedController;
+
+ static FeedPostFragment newInstance(GroupId blogId, MessageId postId) {
+ FeedPostFragment f = new FeedPostFragment();
+
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(GROUP_ID, blogId.getBytes());
+ bundle.putByteArray(POST_ID, postId.getBytes());
+
+ f.setArguments(bundle);
+ return f;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ Bundle args = getArguments();
+ byte[] b = args.getByteArray(GROUP_ID);
+ if (b == null) throw new IllegalStateException("No group ID in args");
+ blogId = new GroupId(b);
+
+ byte[] p = args.getByteArray(POST_ID);
+ if (p == null) throw new IllegalStateException("No post ID in args");
+ postId = new MessageId(p);
+
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
+ public void injectFragment(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ feedController.loadBlogPost(blogId, postId,
+ new UiResultExceptionHandler(
+ listener) {
+ @Override
+ public void onResultUi(BlogPostItem post) {
+ onBlogPostLoaded(post);
+ }
+ @Override
+ public void onExceptionUi(DbException exception) {
+ onBlogPostLoadException(exception);
+ }
+ });
+ }
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedPostPagerFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedPostPagerFragment.java
new file mode 100644
index 000000000..ab9444104
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/blogs/FeedPostPagerFragment.java
@@ -0,0 +1,77 @@
+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;
+ }
+
+
+ void loadBlogPosts(final MessageId select) {
+ feedController.loadBlogPosts(
+ new UiResultExceptionHandler, DbException>(
+ listener) {
+ @Override
+ public void onResultUi(Collection posts) {
+ onBlogPostsLoaded(select, posts);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ onBlogPostsLoadedException(exception);
+ }
+ });
+ }
+
+ void loadBlogPost(BlogPostHeader header) {
+ feedController.loadBlogPost(header,
+ new UiResultExceptionHandler(
+ listener) {
+ @Override
+ public void onResultUi(BlogPostItem post) {
+ addPost(post);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO: Decide how to handle errors in the UI
+ finish();
+ }
+ });
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java
index 923d520a6..6b4afb89b 100644
--- a/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java
+++ b/briar-android/src/org/briarproject/android/blogs/ReblogActivity.java
@@ -15,7 +15,7 @@ import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
-import static org.briarproject.android.blogs.BlogActivity.POST_ID;
+import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
public class ReblogActivity extends BriarActivity implements
BaseFragmentListener {
diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
index 1fee6c647..305273ba8 100644
--- a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
@@ -27,7 +27,7 @@ import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static org.briarproject.android.BriarActivity.GROUP_ID;
-import static org.briarproject.android.blogs.BlogActivity.POST_ID;
+import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
public class ReblogFragment extends BaseFragment {
@@ -106,7 +106,7 @@ public class ReblogFragment extends BaseFragment {
// TODO: Load blog post when fragment is created. #631
feedController.loadBlogPost(blogId, postId,
new UiResultExceptionHandler(
- getActivity()) {
+ listener) {
@Override
public void onResultUi(BlogPostItem result) {
item = result;
@@ -148,7 +148,7 @@ public class ReblogFragment extends BaseFragment {
private void send() {
String comment = getComment();
feedController.repeatPost(item, comment,
- new UiResultExceptionHandler(getActivity()) {
+ new UiResultExceptionHandler(listener) {
@Override
public void onResultUi(Void result) {
// do nothing, this fragment is gone already
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
index ba2eab195..1f9cab093 100644
--- a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java
@@ -1,33 +1,36 @@
package org.briarproject.android.controller.handler;
-import android.app.Activity;
import android.support.annotation.UiThread;
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+
public abstract class UiResultExceptionHandler
implements ResultExceptionHandler {
- private final Activity activity;
+ private final BaseFragmentListener listener;
- public UiResultExceptionHandler(Activity activity) {
- this.activity = activity;
+ protected UiResultExceptionHandler(BaseFragmentListener listener) {
+ this.listener = listener;
}
@Override
public void onResult(final R result) {
- activity.runOnUiThread(new Runnable() {
+ listener.runOnUiThread(new Runnable() {
@Override
public void run() {
- onResultUi(result);
+ if (!listener.hasBeenDestroyed())
+ onResultUi(result);
}
});
}
@Override
public void onException(final E exception) {
- activity.runOnUiThread(new Runnable() {
+ listener.runOnUiThread(new Runnable() {
@Override
public void run() {
- onExceptionUi(exception);
+ if (!listener.hasBeenDestroyed())
+ onExceptionUi(exception);
}
});
}
diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
index 0616b82a2..03c68d7ac 100644
--- a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
+++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java
@@ -1,22 +1,24 @@
package org.briarproject.android.controller.handler;
-import android.app.Activity;
import android.support.annotation.UiThread;
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+
public abstract class UiResultHandler implements ResultHandler {
- private final Activity activity;
+ private final BaseFragmentListener listener;
- public UiResultHandler(Activity activity) {
- this.activity = activity;
+ protected UiResultHandler(BaseFragmentListener listener) {
+ this.listener = listener;
}
@Override
public void onResult(final R result) {
- activity.runOnUiThread(new Runnable() {
+ listener.runOnUiThread(new Runnable() {
@Override
public void run() {
- onResultUi(result);
+ if (!listener.hasBeenDestroyed())
+ onResultUi(result);
}
});
}
diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
index c19bcf1ee..d6f4ed363 100644
--- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
+++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
@@ -7,6 +7,7 @@ import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.Destroyable;
public abstract class BaseFragment extends Fragment {
@@ -45,7 +46,7 @@ public abstract class BaseFragment extends Fragment {
getActivity().supportFinishAfterTransition();
}
- public interface BaseFragmentListener {
+ public interface BaseFragmentListener extends Destroyable {
@UiThread
void showLoadingScreen(boolean isBlocking, int stringId);
diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
index 610e480e5..66593d0f8 100644
--- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java
+++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
@@ -6,7 +6,12 @@ import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.support.design.widget.TextInputLayout;
+import android.support.v4.content.ContextCompat;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
import android.text.format.DateUtils;
+import android.text.style.ForegroundColorSpan;
import org.briarproject.R;
import org.briarproject.util.IoUtils;
@@ -31,6 +36,7 @@ import static android.text.format.DateUtils.WEEK_IN_MILLIS;
public class AndroidUtils {
public static final long MIN_RESOLUTION = MINUTE_IN_MILLIS;
+ public static final int TEASER_LENGTH = 240;
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
@@ -115,4 +121,25 @@ public class AndroidUtils {
MIN_RESOLUTION, flags).toString();
}
+ public static SpannableStringBuilder getTeaser(Context ctx, String body) {
+ if (body.length() < TEASER_LENGTH)
+ throw new IllegalArgumentException(
+ "String is shorter than TEASER_LENGTH");
+
+ SpannableStringBuilder builder =
+ new SpannableStringBuilder(body.substring(0, TEASER_LENGTH));
+ String ellipsis = ctx.getString(R.string.ellipsis);
+ builder.append(ellipsis).append(" ");
+
+ Spannable readMore = new SpannableString(
+ ctx.getString(R.string.read_more) + ellipsis);
+ ForegroundColorSpan fg = new ForegroundColorSpan(
+ ContextCompat.getColor(ctx, R.color.briar_text_link));
+ readMore.setSpan(fg, 0, readMore.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ builder.append(readMore);
+
+ return builder;
+ }
+
}
diff --git a/briar-android/src/org/briarproject/android/util/AuthorView.java b/briar-android/src/org/briarproject/android/util/AuthorView.java
index 6920ebbf1..45ab5d347 100644
--- a/briar-android/src/org/briarproject/android/util/AuthorView.java
+++ b/briar-android/src/org/briarproject/android/util/AuthorView.java
@@ -26,6 +26,7 @@ import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.graphics.Typeface.BOLD;
import static android.graphics.Typeface.NORMAL;
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
@@ -106,6 +107,7 @@ public class AuthorView extends RelativeLayout {
public void onClick(View v) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes());
+ i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
ActivityOptionsCompat options =
makeCustomAnimation(getContext(),
android.R.anim.slide_in_left,