From 95104d33833822b6481082772ab9ee16ddb2b18c Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 12 Jan 2021 14:54:17 -0300 Subject: [PATCH] Clean up after migrating blog controllers to view model --- .../android/activity/ActivityComponent.java | 3 - .../android/blog/BaseControllerImpl.java | 37 ----- .../briar/android/blog/BasePostFragment.java | 128 ------------------ .../briar/android/blog/BaseViewModel.java | 49 +------ .../briar/android/blog/BlogCommentItem.java | 4 +- .../briar/android/blog/BlogFragment.java | 4 +- .../briar/android/blog/BlogModule.java | 4 +- .../briar/android/blog/BlogPostFragment.java | 126 +++++++++++++++-- .../briar/android/blog/BlogPostItem.java | 2 +- .../android/blog/BlogPostViewHolder.java | 16 ++- .../briar/android/blog/BlogViewModel.java | 4 +- .../briar/android/blog/FeedFragment.java | 7 +- .../briar/android/blog/FeedPostFragment.java | 73 ---------- .../briar/android/blog/FeedViewModel.java | 41 +++++- .../briar/android/blog/ReblogActivity.java | 12 +- .../briar/android/blog/ReblogFragment.java | 43 +++--- .../android/forum/ForumListViewModel.java | 4 +- .../privategroup/list/GroupListViewModel.java | 5 +- .../briar/android/viewmodel/DbViewModel.java | 43 +++++- 19 files changed, 246 insertions(+), 359 deletions(-) delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/blog/BasePostFragment.java delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/blog/FeedPostFragment.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index e0f0ce09b..e5914e1cf 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -13,7 +13,6 @@ import org.briarproject.briar.android.blog.BlogActivity; import org.briarproject.briar.android.blog.BlogFragment; import org.briarproject.briar.android.blog.BlogPostFragment; import org.briarproject.briar.android.blog.FeedFragment; -import org.briarproject.briar.android.blog.FeedPostFragment; import org.briarproject.briar.android.blog.ReblogActivity; import org.briarproject.briar.android.blog.ReblogFragment; import org.briarproject.briar.android.blog.RssFeedImportActivity; @@ -150,8 +149,6 @@ public interface ActivityComponent { void inject(BlogPostFragment fragment); - void inject(FeedPostFragment fragment); - void inject(ReblogFragment fragment); void inject(ReblogActivity activity); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java deleted file mode 100644 index d95a5c395..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseControllerImpl.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.briarproject.briar.android.blog; - -import org.briarproject.bramble.api.db.DatabaseExecutor; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.event.EventListener; -import org.briarproject.bramble.api.identity.IdentityManager; -import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.briar.android.controller.DbControllerImpl; -import org.briarproject.briar.api.android.AndroidNotificationManager; -import org.briarproject.briar.api.blog.BlogManager; - -import java.util.concurrent.Executor; - -@MethodsNotNullByDefault -@ParametersNotNullByDefault -abstract class BaseControllerImpl extends DbControllerImpl - implements EventListener { - - protected final EventBus eventBus; - protected final AndroidNotificationManager notificationManager; - protected final IdentityManager identityManager; - protected final BlogManager blogManager; - - BaseControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, EventBus eventBus, - AndroidNotificationManager notificationManager, - IdentityManager identityManager, BlogManager blogManager) { - super(dbExecutor, lifecycleManager); - this.eventBus = eventBus; - this.notificationManager = notificationManager; - this.identityManager = identityManager; - this.blogManager = blogManager; - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BasePostFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BasePostFragment.java deleted file mode 100644 index d09f63a5f..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BasePostFragment.java +++ /dev/null @@ -1,128 +0,0 @@ -package org.briarproject.briar.android.blog; - -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ProgressBar; - -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.briar.R; -import org.briarproject.briar.android.fragment.BaseFragment; -import org.briarproject.briar.android.widget.LinkDialogFragment; - -import java.util.logging.Logger; - -import javax.annotation.Nullable; - -import androidx.annotation.CallSuper; -import androidx.annotation.UiThread; - -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; -import static android.view.View.INVISIBLE; -import static android.view.View.VISIBLE; -import static java.util.logging.Logger.getLogger; -import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; -import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION; - -@UiThread -@MethodsNotNullByDefault -@ParametersNotNullByDefault -abstract class BasePostFragment extends BaseFragment { - - static final String POST_ID = "briar.POST_ID"; - - private static final Logger LOG = - getLogger(BasePostFragment.class.getName()); - - private final Handler handler = new Handler(Looper.getMainLooper()); - - protected MessageId postId; - private ProgressBar progressBar; - private BlogPostViewHolder ui; - private BlogPostItem post; - private Runnable refresher; - - @CallSuper - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - // retrieve MessageId of blog post from arguments - byte[] p = requireArguments().getByteArray(POST_ID); - if (p == null) throw new IllegalStateException("No post ID in args"); - postId = new MessageId(p); - - View view = inflater.inflate(R.layout.fragment_blog_post, container, - false); - progressBar = view.findViewById(R.id.progressBar); - progressBar.setVisibility(VISIBLE); - ui = new BlogPostViewHolder(view, true, new OnBlogPostClickListener() { - @Override - public void onBlogPostClick(BlogPostItem post) { - // We're already there - } - - @Override - public void onAuthorClick(BlogPostItem post) { - if (getContext() == null) return; - Intent i = new Intent(getContext(), BlogActivity.class); - i.putExtra(GROUP_ID, post.getGroupId().getBytes()); - i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); - getContext().startActivity(i); - } - - @Override - public void onLinkClick(String url) { - LinkDialogFragment f = LinkDialogFragment.newInstance(url); - f.show(getParentFragmentManager(), f.getUniqueTag()); - } - }); - return view; - } - - @CallSuper - @Override - public void onStart() { - super.onStart(); - startPeriodicUpdate(); - } - - @CallSuper - @Override - public void onStop() { - super.onStop(); - stopPeriodicUpdate(); - } - - @UiThread - protected void onBlogPostLoaded(BlogPostItem post) { - progressBar.setVisibility(INVISIBLE); - this.post = post; - ui.bindItem(post); - } - - private void startPeriodicUpdate() { - refresher = () -> { - LOG.info("Updating Content..."); - ui.updateDate(post.getTimestamp()); - handler.postDelayed(refresher, MIN_DATE_RESOLUTION); - }; - LOG.info("Adding Handler Callback"); - handler.postDelayed(refresher, MIN_DATE_RESOLUTION); - } - - private void stopPeriodicUpdate() { - if (refresher != null) { - LOG.info("Removing Handler Callback"); - handler.removeCallbacks(refresher); - } - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseViewModel.java index d6690c4c2..e61ae3c38 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BaseViewModel.java @@ -6,7 +6,6 @@ import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.TransactionManager; -import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.identity.IdentityManager; @@ -28,13 +27,10 @@ import org.briarproject.briar.util.HtmlUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.logging.Logger; import javax.annotation.Nullable; -import javax.inject.Inject; import androidx.annotation.UiThread; import androidx.lifecycle.LiveData; @@ -48,27 +44,23 @@ import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.briar.util.HtmlUtils.ARTICLE; @NotNullByDefault -public class BaseViewModel extends DbViewModel implements EventListener { +abstract class BaseViewModel extends DbViewModel implements EventListener { private static final Logger LOG = getLogger(BaseViewModel.class.getName()); - protected final TransactionManager db; private final EventBus eventBus; + protected final TransactionManager db; protected final IdentityManager identityManager; protected final AndroidNotificationManager notificationManager; protected final BlogManager blogManager; - private final MutableLiveData>> blogPosts = + protected final MutableLiveData>> blogPosts = new MutableLiveData<>(); - // TODO do we still need those caches? - private final Map textCache = new ConcurrentHashMap<>(); - private final Map headerCache = - new ConcurrentHashMap<>(); + // UI thread @Nullable private Boolean postAddedWasLocal = null; - @Inject BaseViewModel(Application application, @DatabaseExecutor Executor dbExecutor, LifecycleManager lifecycleManager, @@ -84,7 +76,6 @@ public class BaseViewModel extends DbViewModel implements EventListener { this.identityManager = identityManager; this.notificationManager = notificationManager; this.blogManager = blogManager; - eventBus.addListener(this); } @@ -94,10 +85,6 @@ public class BaseViewModel extends DbViewModel implements EventListener { eventBus.removeListener(this); } - @Override - public void eventOccurred(Event e) { - } - @DatabaseExecutor protected List loadBlogPosts(Transaction txn, GroupId groupId) throws DbException { @@ -108,7 +95,6 @@ public class BaseViewModel extends DbViewModel implements EventListener { List items = new ArrayList<>(headers.size()); start = now(); for (BlogPostHeader h : headers) { - headerCache.put(h.getId(), h); BlogPostItem item = getItem(txn, h); items.add(item); } @@ -135,12 +121,7 @@ public class BaseViewModel extends DbViewModel implements EventListener { @DatabaseExecutor private String getPostText(Transaction txn, MessageId m) throws DbException { - String text = textCache.get(m); - if (text == null) { - text = HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE); - textCache.put(m, text); - } - return text; + return HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE); } LiveData> loadBlogPost(GroupId g, MessageId m) { @@ -149,7 +130,7 @@ public class BaseViewModel extends DbViewModel implements EventListener { runOnDbThread(() -> { try { long start = now(); - BlogPostHeader header = getPostHeader(g, m); + BlogPostHeader header = blogManager.getPostHeader(g, m); BlogPostItem item = db.transactionWithResult(true, txn -> getItem(txn, header) ); @@ -163,24 +144,7 @@ public class BaseViewModel extends DbViewModel implements EventListener { return result; } - @DatabaseExecutor - private BlogPostHeader getPostHeader(GroupId g, MessageId m) - throws DbException { - BlogPostHeader header = headerCache.get(m); - if (header == null) { - header = blogManager.getPostHeader(g, m); - headerCache.put(m, header); - } - return header; - } - - @UiThread - protected void updateBlogPosts(LiveResult> posts) { - blogPosts.setValue(posts); - } - protected void onBlogPostAdded(BlogPostHeader header, boolean local) { - postAddedWasLocal = local; runOnDbThread(() -> { try { db.transaction(true, txn -> { @@ -189,6 +153,7 @@ public class BaseViewModel extends DbViewModel implements EventListener { List items = addListItem(blogPosts, item); if (items != null) { Collections.sort(items); + postAddedWasLocal = local; blogPosts.setValue(new LiveResult<>(items)); } }); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogCommentItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogCommentItem.java index 89a6e7fa4..6894088d0 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogCommentItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogCommentItem.java @@ -8,7 +8,9 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -// This class is not thread-safe +import javax.annotation.concurrent.NotThreadSafe; + +@NotThreadSafe class BlogCommentItem extends BlogPostItem { private static final BlogCommentComparator COMPARATOR = diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogFragment.java index a3c836097..63ce4954d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogFragment.java @@ -166,7 +166,6 @@ public class BlogFragment extends BaseFragment if (request == REQUEST_WRITE_BLOG_POST && result == RESULT_OK) { displaySnackbar(R.string.blogs_blog_post_created, true); - viewModel.loadBlogPosts(groupId); } else if (request == REQUEST_SHARE_BLOG && result == RESULT_OK) { displaySnackbar(R.string.blogs_sharing_snackbar, false); } @@ -194,7 +193,8 @@ public class BlogFragment extends BaseFragment @Override public void onBlogPostClick(BlogPostItem post) { - BlogPostFragment f = BlogPostFragment.newInstance(post.getId()); + BlogPostFragment f = + BlogPostFragment.newInstance(groupId, post.getId()); showNextFragment(f); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogModule.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogModule.java index 5eafcb2eb..a12a5fc03 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogModule.java @@ -13,11 +13,11 @@ public interface BlogModule { @Binds @IntoMap @ViewModelKey(FeedViewModel.class) - abstract ViewModel bindFeedViewModel(FeedViewModel feedViewModel); + ViewModel bindFeedViewModel(FeedViewModel feedViewModel); @Binds @IntoMap @ViewModelKey(BlogViewModel.class) - abstract ViewModel bindBlogViewModel(BlogViewModel blogViewModel); + ViewModel bindBlogViewModel(BlogViewModel blogViewModel); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostFragment.java index 114e73fe8..75001f45b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostFragment.java @@ -1,39 +1,68 @@ package org.briarproject.briar.android.blog; +import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.ProgressBar; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; +import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.fragment.BaseFragment; +import org.briarproject.briar.android.widget.LinkDialogFragment; + +import java.util.logging.Logger; import javax.annotation.Nullable; import javax.inject.Inject; +import androidx.annotation.CallSuper; import androidx.annotation.UiThread; +import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ViewModelProvider; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; +import static android.view.View.INVISIBLE; +import static android.view.View.VISIBLE; +import static java.util.Objects.requireNonNull; +import static java.util.logging.Logger.getLogger; +import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; +import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION; + @UiThread @MethodsNotNullByDefault @ParametersNotNullByDefault -public class BlogPostFragment extends BasePostFragment { +public class BlogPostFragment extends BaseFragment + implements OnBlogPostClickListener { private static final String TAG = BlogPostFragment.class.getName(); + private static final Logger LOG = getLogger(TAG); + + static final String POST_ID = "briar.POST_ID"; + + protected BlogViewModel viewModel; + private final Handler handler = new Handler(Looper.getMainLooper()); + + private ProgressBar progressBar; + private BlogPostViewHolder ui; + private BlogPostItem post; + private Runnable refresher; @Inject ViewModelProvider.Factory viewModelFactory; - private BlogViewModel viewModel; - - static BlogPostFragment newInstance(MessageId postId) { + static BlogPostFragment newInstance(GroupId blogId, MessageId postId) { BlogPostFragment f = new BlogPostFragment(); - Bundle bundle = new Bundle(); + bundle.putByteArray(GROUP_ID, blogId.getBytes()); bundle.putByteArray(POST_ID, postId.getBytes()); - f.setArguments(bundle); return f; } @@ -45,22 +74,89 @@ public class BlogPostFragment extends BasePostFragment { .get(BlogViewModel.class); } - @Override - public String getUniqueTag() { - return TAG; - } - @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - View v = super.onCreateView(inflater, container, savedInstanceState); - viewModel.loadBlogPost(postId).observe(getViewLifecycleOwner(), res -> - res.onError(this::handleException) + Bundle args = requireArguments(); + GroupId groupId = + new GroupId(requireNonNull(args.getByteArray(GROUP_ID))); + MessageId postId = new MessageId(args.getByteArray(POST_ID)); + + View view = inflater.inflate(R.layout.fragment_blog_post, container, + false); + progressBar = view.findViewById(R.id.progressBar); + progressBar.setVisibility(VISIBLE); + ui = new BlogPostViewHolder(view, true, this); + LifecycleOwner owner = getViewLifecycleOwner(); + viewModel.loadBlogPost(groupId, postId).observe(owner, result -> + result.onError(this::handleException) .onSuccess(this::onBlogPostLoaded) ); - return v; + return view; + } + + @CallSuper + @Override + public void onStart() { + super.onStart(); + startPeriodicUpdate(); + } + + @CallSuper + @Override + public void onStop() { + super.onStop(); + stopPeriodicUpdate(); + } + + @UiThread + private void onBlogPostLoaded(BlogPostItem post) { + progressBar.setVisibility(INVISIBLE); + this.post = post; + ui.bindItem(post); + } + + @Override + public void onBlogPostClick(BlogPostItem post) { + // We're already there + } + + @Override + public void onAuthorClick(BlogPostItem post) { + Intent i = new Intent(requireContext(), BlogActivity.class); + i.putExtra(GROUP_ID, post.getGroupId().getBytes()); + i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); + getContext().startActivity(i); + } + + @Override + public void onLinkClick(String url) { + LinkDialogFragment f = LinkDialogFragment.newInstance(url); + f.show(getParentFragmentManager(), f.getUniqueTag()); + } + + private void startPeriodicUpdate() { + refresher = () -> { + LOG.info("Updating Content..."); + ui.updateDate(post.getTimestamp()); + handler.postDelayed(refresher, MIN_DATE_RESOLUTION); + }; + LOG.info("Adding Handler Callback"); + handler.postDelayed(refresher, MIN_DATE_RESOLUTION); + } + + private void stopPeriodicUpdate() { + if (refresher != null) { + LOG.info("Removing Handler Callback"); + handler.removeCallbacks(refresher); + } + } + + @Override + public String getUniqueTag() { + return TAG; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java index 3247a083c..a4e4cf449 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java @@ -17,7 +17,7 @@ public class BlogPostItem implements Comparable { private final BlogPostHeader header; @Nullable protected String text; - private boolean read; + private final boolean read; BlogPostItem(BlogPostHeader header, @Nullable String text) { this.header = header; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java index ad4debe92..1e948af57 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java @@ -25,11 +25,14 @@ import androidx.recyclerview.widget.RecyclerView; import static android.view.View.GONE; import static android.view.View.VISIBLE; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; -import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID; +import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID; import static org.briarproject.briar.android.util.UiUtils.TEASER_LENGTH; import static org.briarproject.briar.android.util.UiUtils.getSpanned; import static org.briarproject.briar.android.util.UiUtils.getTeaser; import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable; +import static org.briarproject.briar.android.view.AuthorView.COMMENTER; +import static org.briarproject.briar.android.view.AuthorView.REBLOGGER; +import static org.briarproject.briar.android.view.AuthorView.RSS_FEED_REBLOGGED; import static org.briarproject.briar.api.blog.MessageType.POST; @UiThread @@ -138,17 +141,16 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { reblogger.setAuthorClickable(v -> listener.onAuthorClick(item)); } reblogger.setVisibility(VISIBLE); - reblogger.setPersona(AuthorView.REBLOGGER); + reblogger.setPersona(REBLOGGER); author.setPersona(item.getHeader().getRootPost().isRssFeed() ? - AuthorView.RSS_FEED_REBLOGGED : - AuthorView.COMMENTER); + RSS_FEED_REBLOGGED : COMMENTER); // comments + // TODO use nested RecyclerView instead like we do for Image Attachments for (BlogCommentHeader c : item.getComments()) { - View v = LayoutInflater.from(ctx) - .inflate(R.layout.list_item_blog_comment, - commentContainer, false); + View v = LayoutInflater.from(ctx).inflate( + R.layout.list_item_blog_comment, commentContainer, false); AuthorView author = v.findViewById(R.id.authorView); TextView text = v.findViewById(R.id.textView); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogViewModel.java index 88e84e905..173a627cc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogViewModel.java @@ -148,8 +148,8 @@ class BlogViewModel extends BaseViewModel { notificationManager.unblockNotification(groupId); } - void loadBlogPosts(GroupId groupId) { - loadList(txn -> loadBlogPosts(txn, groupId), this::updateBlogPosts); + private void loadBlogPosts(GroupId groupId) { + loadList(txn -> loadBlogPosts(txn, groupId), blogPosts::setValue); } private void loadSharingContacts(GroupId groupId) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java index 66380a582..8ef8d1b96 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java @@ -62,9 +62,6 @@ public class FeedFragment extends BaseFragment component.inject(this); viewModel = new ViewModelProvider(this, viewModelFactory) .get(FeedViewModel.class); - // TODO ideally we only do this once when the ViewModel gets created - viewModel.loadPersonalBlog(); - viewModel.loadAllBlogPosts(); } @Nullable @@ -154,8 +151,8 @@ public class FeedFragment extends BaseFragment @Override public void onBlogPostClick(BlogPostItem post) { - FeedPostFragment f = - FeedPostFragment.newInstance(post.getGroupId(), post.getId()); + BaseFragment f = + BlogPostFragment.newInstance(post.getGroupId(), post.getId()); showNextFragment(f); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedPostFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedPostFragment.java deleted file mode 100644 index 6e90bb6d7..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedPostFragment.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.briarproject.briar.android.blog; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.sync.GroupId; -import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.briar.android.activity.ActivityComponent; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import androidx.annotation.UiThread; -import androidx.lifecycle.ViewModelProvider; - -import static java.util.Objects.requireNonNull; -import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; - -@UiThread -@MethodsNotNullByDefault -@ParametersNotNullByDefault -public class FeedPostFragment extends BasePostFragment { - - private static final String TAG = FeedPostFragment.class.getName(); - - @Inject - ViewModelProvider.Factory viewModelFactory; - - private FeedViewModel viewModel; - - 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; - } - - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) - .get(FeedViewModel.class); - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - Bundle args = requireArguments(); - GroupId groupId = - new GroupId(requireNonNull(args.getByteArray(GROUP_ID))); - MessageId postId = - new MessageId(requireNonNull(args.getByteArray(POST_ID))); - viewModel.loadBlogPost(groupId, postId).observe(getViewLifecycleOwner(), - result -> result.onError(this::handleException) - .onSuccess(this::onBlogPostLoaded) - ); - return super.onCreateView(inflater, container, savedInstanceState); - } - - @Override - public String getUniqueTag() { - return TAG; - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedViewModel.java index 3c18f5774..320afa7cd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedViewModel.java @@ -17,6 +17,7 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.event.GroupAddedEvent; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.bramble.api.system.AndroidExecutor; +import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.BlogManager; @@ -59,6 +60,8 @@ class FeedViewModel extends BaseViewModel { BlogManager blogManager) { super(application, dbExecutor, lifecycleManager, db, androidExecutor, eventBus, identityManager, notificationManager, blogManager); + loadPersonalBlog(); + loadAllBlogPosts(); } @Override @@ -71,13 +74,15 @@ class FeedViewModel extends BaseViewModel { GroupAddedEvent g = (GroupAddedEvent) e; if (g.getGroup().getClientId().equals(CLIENT_ID)) { LOG.info("Blog added"); - loadAllBlogPosts(); + // TODO how can this even happen? + // added RSS feeds should trigger BlogPostAddedEvent, no? + onBlogAdded(g.getGroup().getId()); } } else if (e instanceof GroupRemovedEvent) { GroupRemovedEvent g = (GroupRemovedEvent) e; if (g.getGroup().getClientId().equals(CLIENT_ID)) { LOG.info("Blog removed"); - loadAllBlogPosts(); + onBlogRemoved(g.getGroup().getId()); } } } @@ -94,7 +99,7 @@ class FeedViewModel extends BaseViewModel { notificationManager.unblockAllBlogPostNotifications(); } - void loadPersonalBlog() { + private void loadPersonalBlog() { runOnDbThread(() -> { try { long start = now(); @@ -112,8 +117,8 @@ class FeedViewModel extends BaseViewModel { return personalBlog; } - void loadAllBlogPosts() { - loadList(this::loadAllBlogPosts, this::updateBlogPosts); + private void loadAllBlogPosts() { + loadList(this::loadAllBlogPosts, blogPosts::setValue); } @DatabaseExecutor @@ -133,4 +138,30 @@ class FeedViewModel extends BaseViewModel { return posts; } + private void onBlogAdded(GroupId g) { + runOnDbThread(() -> { + try { + db.transaction(true, txn -> { + List posts = loadBlogPosts(txn, g); + txn.attach(() -> { + List items = + addListItems(blogPosts, posts); + if (items != null) { + Collections.sort(items); + blogPosts.setValue(new LiveResult<>(items)); + } + }); + }); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + + private void onBlogRemoved(GroupId g) { + removeAndUpdateListItems(blogPosts, item -> + item.getGroupId().equals(g) + ); + } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java index d45b10789..d73b1899b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogActivity.java @@ -11,7 +11,7 @@ import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; -import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID; +import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID; public class ReblogActivity extends BriarActivity implements BaseFragmentListener { @@ -39,13 +39,11 @@ public class ReblogActivity extends BriarActivity implements @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - onBackPressed(); - return true; - default: - return super.onOptionsItemSelected(item); + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + return true; } + return super.onOptionsItemSelected(item); } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java index f3746d7f7..a10a47bae 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/ReblogFragment.java @@ -33,7 +33,7 @@ import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static java.util.Objects.requireNonNull; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; -import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID; +import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_TEXT_LENGTH; @MethodsNotNullByDefault @@ -45,7 +45,7 @@ public class ReblogFragment extends BaseFragment implements SendListener { @Inject ViewModelProvider.Factory viewModelFactory; - private FeedViewModel viewModel; + private BlogViewModel viewModel; private ViewHolder ui; private BlogPostItem item; @@ -69,7 +69,7 @@ public class ReblogFragment extends BaseFragment implements SendListener { public void injectFragment(ActivityComponent component) { component.inject(this); viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) - .get(FeedViewModel.class); + .get(BlogViewModel.class); } @Override @@ -131,7 +131,7 @@ public class ReblogFragment extends BaseFragment implements SendListener { ui.input.setVisibility(VISIBLE); } - private class ViewHolder { + private class ViewHolder implements OnBlogPostClickListener { private final ScrollView scrollView; private final ProgressBar progressBar; @@ -142,24 +142,25 @@ public class ReblogFragment extends BaseFragment implements SendListener { scrollView = v.findViewById(R.id.scrollView); progressBar = v.findViewById(R.id.progressBar); post = new BlogPostViewHolder(v.findViewById(R.id.postLayout), - true, new OnBlogPostClickListener() { - @Override - public void onBlogPostClick(BlogPostItem post) { - // do nothing - } - - @Override - public void onAuthorClick(BlogPostItem post) { - // probably don't want to allow author clicks here - } - - @Override - public void onLinkClick(String url) { - LinkDialogFragment f = LinkDialogFragment.newInstance(url); - f.show(getParentFragmentManager(), f.getUniqueTag()); - } - }); + true, this); input = v.findViewById(R.id.inputText); } + + @Override + public void onBlogPostClick(BlogPostItem post) { + // do nothing + } + + @Override + public void onAuthorClick(BlogPostItem post) { + // probably don't want to allow author clicks here + } + + @Override + public void onLinkClick(String url) { + LinkDialogFragment f = LinkDialogFragment.newInstance(url); + f.show(getParentFragmentManager(), f.getUniqueTag()); + } } + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListViewModel.java index af86a9d95..52ad8c449 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumListViewModel.java @@ -156,11 +156,9 @@ class ForumListViewModel extends DbViewModel implements EventListener { @UiThread private void onGroupRemoved(GroupId groupId) { - List list = removeListItems(forumItems, i -> + removeAndUpdateListItems(forumItems, i -> i.getForum().getId().equals(groupId) ); - if (list == null) return; - forumItems.setValue(new LiveResult<>(list)); } void loadForumInvitations() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java index 041305a37..3eedd40fc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/list/GroupListViewModel.java @@ -193,10 +193,7 @@ class GroupListViewModel extends DbViewModel implements EventListener { @UiThread private void onGroupRemoved(GroupId groupId) { - List list = - removeListItems(groupItems, i -> i.getId().equals(groupId)); - if (list == null) return; - groupItems.setValue(new LiveResult<>(list)); + removeAndUpdateListItems(groupItems, i -> i.getId().equals(groupId)); } void removeGroup(GroupId g) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/viewmodel/DbViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/viewmodel/DbViewModel.java index a0aa00c8d..1c74d8f66 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/viewmodel/DbViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/viewmodel/DbViewModel.java @@ -13,6 +13,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.system.AndroidExecutor; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.ListIterator; import java.util.concurrent.Executor; @@ -27,6 +28,7 @@ import androidx.arch.core.util.Function; import androidx.core.util.Consumer; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import androidx.recyclerview.widget.RecyclerView; import static java.util.logging.Level.WARNING; @@ -153,13 +155,33 @@ public abstract class DbViewModel extends AndroidViewModel { * */ @Nullable - protected List addListItem(LiveData>> liveData, T item) { + protected List addListItem(LiveData>> liveData, + T item) { List items = getListCopy(liveData); if (items == null) return null; items.add(item); return items; } + /** + * Creates a copy of the list available in the given LiveData + * and adds the given items to the copy. + * + * @return a copy of the list in the LiveData with items added or null when + *
    + *
  • LiveData does not have a value + *
  • LiveResult in the LiveData has an error + *
+ */ + @Nullable + protected List addListItems(LiveData>> liveData, + Collection items) { + List copiedItems = getListCopy(liveData); + if (copiedItems == null) return null; + copiedItems.addAll(items); + return copiedItems; + } + /** * Creates a copy of the list available in the given LiveData * and replaces items where the given test function returns true. @@ -221,6 +243,25 @@ public abstract class DbViewModel extends AndroidViewModel { return changed ? items : null; } + /** + * Updates the given LiveData with a copy of its list + * with the items removed where the given test function returns true. + *

+ * Nothing is updated, if the + *

    + *
  • LiveData does not have a value + *
  • LiveResult in the LiveData has an error + *
  • test function did return false for all items in the list + *
+ */ + @UiThread + protected void removeAndUpdateListItems( + MutableLiveData>> liveData, + Function test) { + List list = removeListItems(liveData, test); + if (list != null) liveData.setValue(new LiveResult<>(list)); + } + /** * Retrieves a copy of the list of items from the given LiveData * or null if it is not available.