From 307e124ee84a3ec74c33d4c7ad7aef7ae4088e49 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Mon, 5 Sep 2016 20:21:11 -0300 Subject: [PATCH] Make the blog post pager usable for the feed and individual blogs --- .../layout/activity_fragment_container.xml | 12 +- .../res/layout/fragment_blog_post.xml | 7 +- .../res/layout/fragment_blog_post_pager.xml | 6 + .../android/ActivityComponent.java | 7 + .../android/NavDrawerActivity.java | 5 +- .../android/blogs/BasePostFragment.java | 99 +++++++ .../android/blogs/BasePostPagerFragment.java | 187 +++++++++++++ .../android/blogs/BlogActivity.java | 258 ++---------------- .../android/blogs/BlogFragment.java | 48 +++- .../android/blogs/BlogPostFragment.java | 90 +----- .../android/blogs/BlogPostPagerFragment.java | 66 +++++ .../android/blogs/FeedFragment.java | 7 +- .../android/blogs/FeedPostFragment.java | 85 ++++++ .../android/blogs/FeedPostPagerFragment.java | 65 +++++ 14 files changed, 614 insertions(+), 328 deletions(-) create mode 100644 briar-android/res/layout/fragment_blog_post_pager.xml create mode 100644 briar-android/src/org/briarproject/android/blogs/BasePostFragment.java create mode 100644 briar-android/src/org/briarproject/android/blogs/BasePostPagerFragment.java create mode 100644 briar-android/src/org/briarproject/android/blogs/BlogPostPagerFragment.java create mode 100644 briar-android/src/org/briarproject/android/blogs/FeedPostFragment.java create mode 100644 briar-android/src/org/briarproject/android/blogs/FeedPostPagerFragment.java diff --git a/briar-android/res/layout/activity_fragment_container.xml b/briar-android/res/layout/activity_fragment_container.xml index e6c20760fb..8bf59ee5ac 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"> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:visibility="invisible"/> + +</FrameLayout> \ 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 7a0df0d0b5..bc1b87c2ab 100644 --- a/briar-android/res/layout/fragment_blog_post.xml +++ b/briar-android/res/layout/fragment_blog_post.xml @@ -6,7 +6,12 @@ <FrameLayout android:layout_width="wrap_content" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:descendantFocusability="beforeDescendants" + android:focusable="true" + android:focusableInTouchMode="true"> + <!-- Above Focusability attributes prevent automatic scroll-down, + because body text is selectable --> <include android:id="@+id/postLayout" diff --git a/briar-android/res/layout/fragment_blog_post_pager.xml b/briar-android/res/layout/fragment_blog_post_pager.xml new file mode 100644 index 0000000000..fe1c2568a1 --- /dev/null +++ b/briar-android/res/layout/fragment_blog_post_pager.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.v4.view.ViewPager + android:id="@+id/pager" + 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 diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index 2a0d82c4b2..baee29d41e 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/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java index 6381bc9cd1..d4bacf55a9 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/BasePostFragment.java b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java new file mode 100644 index 0000000000..e340a3fcba --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java @@ -0,0 +1,99 @@ +package org.briarproject.android.blogs; + +import android.os.Bundle; +import android.support.annotation.CallSuper; +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.fragment.BaseFragment; +import org.briarproject.api.db.DbException; + +import java.util.logging.Logger; + +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 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); + ui = new BlogPostViewHolder(view); + return view; + } + + @CallSuper + @Override + public void onStart() { + super.onStart(); + startPeriodicUpdate(); + } + + @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); + } + } + + protected void onBlogPostLoaded(BlogPostItem post) { + listener.hideLoadingScreen(); + this.post = post; + ui.bindItem(post); + } + + 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 0000000000..b24f6be3a9 --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BasePostPagerFragment.java @@ -0,0 +1,187 @@ +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 org.briarproject.R; +import org.briarproject.android.blogs.BaseController.OnBlogPostAddedListener; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; +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 org.briarproject.android.blogs.BasePostPagerFragment.BlogPostPagerAdapter.INVALID_POSITION; +import static org.briarproject.android.blogs.BlogActivity.POST_ID; + +abstract class BasePostPagerFragment extends BaseFragment + implements OnBlogPostAddedListener { + + private ViewPager pager; + 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); + pager = (ViewPager) v.findViewById(R.id.pager); + postPagerAdapter = new BlogPostPagerAdapter(getChildFragmentManager()); + listener.showLoadingScreen(false, R.string.progress_title_please_wait); + + 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 BaseController getController(); + + protected void onBlogPostsLoaded(MessageId select, + Collection<BlogPostItem> posts) { + + postId = null; + postPagerAdapter.setPosts(posts); + selectPost(select); + } + + protected void onBlogPostsLoadedException(DbException exception) { + // TODO: Decide how to handle errors in the UI + finish(); + } + + private void loadBlogPost(BlogPostHeader header) { + getController().loadBlogPost(header, + new UiResultExceptionHandler<BlogPostItem, DbException>( + getActivity()) { + @Override + public void onResultUi(BlogPostItem post) { + addPost(post); + } + + @Override + public void onExceptionUi(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) { + pager.setAdapter(postPagerAdapter); + pager.setCurrentItem(pos); + } + } + + protected void addPost(BlogPostItem post) { + MessageId selected = getSelectedPost(); + postPagerAdapter.addPost(post); + if (selected != null) selectPost(selected); + } + + @UiThread + static class BlogPostPagerAdapter extends FragmentStatePagerAdapter { + + static final int INVALID_POSITION = -1; + private final List<BlogPostItem> posts = new ArrayList<>(); + + private BlogPostPagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public int getCount() { + return posts.size(); + } + + @Override + public Fragment getItem(int position) { + BlogPostItem post = posts.get(position); + return FeedPostFragment.newInstance(post.getGroupId(), post.getId()); + } + + private BlogPostItem getPost(int position) { + return posts.get(position); + } + + private void setPosts(Collection<BlogPostItem> posts) { + this.posts.clear(); + this.posts.addAll(posts); + Collections.sort(this.posts); + notifyDataSetChanged(); + } + + private void addPost(BlogPostItem post) { + posts.add(post); + Collections.sort(posts); + notifyDataSetChanged(); + } + + private int getPostPosition(MessageId m) { + int count = getCount(); + for (int i = 0; i < count; i++) { + if (getPost(i).getId().equals(m)) { + return i; + } + } + return INVALID_POSITION; + } + } + +} diff --git a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java index 249d0ff7cf..34ba66f982 100644 --- a/briar-android/src/org/briarproject/android/blogs/BlogActivity.java +++ b/briar-android/src/org/briarproject/android/blogs/BlogActivity.java @@ -2,13 +2,6 @@ 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; @@ -16,42 +9,25 @@ 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 { + 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"; + static final String POST_ID = "briar.POST_ID"; - 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,68 +40,50 @@ 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); - - setContentView(R.layout.activity_blog); + boolean isNew = i.getBooleanExtra(IS_NEW_BLOG, false); - 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)); + if (state == null) { + BlogFragment f = BlogFragment.newInstance(groupId, blogName, isNew); + getSupportFragmentManager().beginTransaction() + .replace(R.id.fragmentContainer, f, f.getUniqueTag()) + .commit(); } } @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()); + public void injectActivity(ActivityComponent component) { + component.inject(this); } @Override - public void onBackPressed() { - if (pager.getAdapter() == postPagerAdapter) { - pager.setAdapter(blogPagerAdapter); - savedPostId = null; - } else { - super.onBackPressed(); - } + public void onBlogPostAdded(BlogPostHeader header, boolean local) { + // all our fragments are implementing and registering that hook, + // so we don't need to do that ourselves } @Override - public void injectActivity(ActivityComponent component) { - component.inject(this); + 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 @@ -135,176 +93,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<Collection<BlogPostItem>, DbException>( - this) { - @Override - public void onResultUi(Collection<BlogPostItem> 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<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 - 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<BlogPostItem> 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<BlogPostItem> posts) { - this.posts.clear(); - this.posts.addAll(posts); - Collections.sort(this.posts); - notifyDataSetChanged(); - } - - private void addPost(BlogPostItem post) { - posts.add(post); - Collections.sort(posts); - notifyDataSetChanged(); - } - } - } diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java index b3b87a113c..0d9a0fe985 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; @@ -59,6 +59,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,7 +115,7 @@ public class BlogFragment extends BaseFragment implements @Override public void injectFragment(ActivityComponent component) { component.inject(this); - blogController.setGroupId(groupId); + blogController.setOnBlogPostAddedListener(this); } @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); } } @@ -211,9 +215,9 @@ public class BlogFragment extends BaseFragment implements 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); } } @@ -243,13 +247,13 @@ 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( + blogController.isMyBlog( new UiResultExceptionHandler<Boolean, DbException>( getActivity()) { @Override @@ -262,7 +266,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(); } }); } @@ -281,25 +285,39 @@ 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 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(); } @@ -335,7 +353,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/BlogPostFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogPostFragment.java index 926ce7f37d..b70a360625 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.BlogActivity.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); + + return super.onCreateView(inflater, container, savedInstanceState); + } - view = inflater.inflate(R.layout.fragment_blog_post, container, - false); - ui = new BlogPostViewHolder(view); - return view; + @Override + public String getUniqueTag() { + return TAG; } @Override @@ -75,62 +65,12 @@ public class BlogPostFragment extends BaseFragment { getActivity()) { @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 0000000000..54e646f9cf --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/BlogPostPagerFragment.java @@ -0,0 +1,66 @@ +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.db.DbException; +import org.briarproject.api.sync.MessageId; + +import java.util.Collection; + +import javax.inject.Inject; + +import static org.briarproject.android.blogs.BlogActivity.POST_ID; + + +public class BlogPostPagerFragment extends BasePostPagerFragment { + + public final static String TAG = BlogPostPagerFragment.class.getName(); + + @Inject + BlogController blogController; + + static BlogPostPagerFragment newInstance(MessageId postId) { + BlogPostPagerFragment f = new BlogPostPagerFragment(); + + Bundle args = new Bundle(); + args.putByteArray(POST_ID, postId.getBytes()); + f.setArguments(args); + + return f; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + blogController.setOnBlogPostAddedListener(this); + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + BaseController getController() { + return blogController; + } + + void loadBlogPosts(final MessageId select) { + blogController.loadBlogPosts( + new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>( + getActivity()) { + @Override + public void onResultUi(Collection<BlogPostItem> posts) { + onBlogPostsLoaded(select, posts); + } + + @Override + public void onExceptionUi(DbException exception) { + onBlogPostsLoadedException(exception); + } + }); + } + +} diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java index 9e6f577487..dbdd111271 100644 --- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java +++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java @@ -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 0000000000..5b4841cd41 --- /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.BlogActivity.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<BlogPostItem, DbException>( + getActivity()) { + @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 0000000000..db30ac1f3c --- /dev/null +++ b/briar-android/src/org/briarproject/android/blogs/FeedPostPagerFragment.java @@ -0,0 +1,65 @@ +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.db.DbException; +import org.briarproject.api.sync.MessageId; + +import java.util.Collection; + +import javax.inject.Inject; + +import static org.briarproject.android.blogs.BlogActivity.POST_ID; + +public class FeedPostPagerFragment extends BasePostPagerFragment { + + public final static String TAG = FeedPostPagerFragment.class.getName(); + + @Inject + FeedController feedController; + + static FeedPostPagerFragment newInstance(MessageId postId) { + FeedPostPagerFragment f = new FeedPostPagerFragment(); + + Bundle args = new Bundle(); + args.putByteArray(POST_ID, postId.getBytes()); + f.setArguments(args); + + return f; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + feedController.setOnBlogPostAddedListener(this); + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + BaseController getController() { + return feedController; + } + + void loadBlogPosts(final MessageId select) { + feedController.loadBlogPosts( + new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>( + getActivity()) { + @Override + public void onResultUi(Collection<BlogPostItem> posts) { + onBlogPostsLoaded(select, posts); + } + + @Override + public void onExceptionUi(DbException exception) { + onBlogPostsLoadedException(exception); + } + }); + } + +} -- GitLab