mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Clean up after migrating blog controllers to view model
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<LiveResult<List<BlogPostItem>>> blogPosts =
|
||||
protected final MutableLiveData<LiveResult<List<BlogPostItem>>> blogPosts =
|
||||
new MutableLiveData<>();
|
||||
|
||||
// TODO do we still need those caches?
|
||||
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||
private final Map<MessageId, BlogPostHeader> 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<BlogPostItem> loadBlogPosts(Transaction txn, GroupId groupId)
|
||||
throws DbException {
|
||||
@@ -108,7 +95,6 @@ public class BaseViewModel extends DbViewModel implements EventListener {
|
||||
List<BlogPostItem> 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<LiveResult<BlogPostItem>> 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<List<BlogPostItem>> 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<BlogPostItem> items = addListItem(blogPosts, item);
|
||||
if (items != null) {
|
||||
Collections.sort(items);
|
||||
postAddedWasLocal = local;
|
||||
blogPosts.setValue(new LiveResult<>(items));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
|
||||
private final BlogPostHeader header;
|
||||
@Nullable
|
||||
protected String text;
|
||||
private boolean read;
|
||||
private final boolean read;
|
||||
|
||||
BlogPostItem(BlogPostHeader header, @Nullable String text) {
|
||||
this.header = header;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<BlogPostItem> posts = loadBlogPosts(txn, g);
|
||||
txn.attach(() -> {
|
||||
List<BlogPostItem> 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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -156,11 +156,9 @@ class ForumListViewModel extends DbViewModel implements EventListener {
|
||||
|
||||
@UiThread
|
||||
private void onGroupRemoved(GroupId groupId) {
|
||||
List<ForumListItem> list = removeListItems(forumItems, i ->
|
||||
removeAndUpdateListItems(forumItems, i ->
|
||||
i.getForum().getId().equals(groupId)
|
||||
);
|
||||
if (list == null) return;
|
||||
forumItems.setValue(new LiveResult<>(list));
|
||||
}
|
||||
|
||||
void loadForumInvitations() {
|
||||
|
||||
@@ -193,10 +193,7 @@ class GroupListViewModel extends DbViewModel implements EventListener {
|
||||
|
||||
@UiThread
|
||||
private void onGroupRemoved(GroupId groupId) {
|
||||
List<GroupItem> 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) {
|
||||
|
||||
@@ -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 {
|
||||
* </ul>
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> List<T> addListItem(LiveData<LiveResult<List<T>>> liveData, T item) {
|
||||
protected <T> List<T> addListItem(LiveData<LiveResult<List<T>>> liveData,
|
||||
T item) {
|
||||
List<T> 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
|
||||
* <ul>
|
||||
* <li> LiveData does not have a value
|
||||
* <li> LiveResult in the LiveData has an error
|
||||
* </ul>
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> List<T> addListItems(LiveData<LiveResult<List<T>>> liveData,
|
||||
Collection<T> items) {
|
||||
List<T> 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.
|
||||
* <p>
|
||||
* Nothing is updated, if the
|
||||
* <ul>
|
||||
* <li> LiveData does not have a value
|
||||
* <li> LiveResult in the LiveData has an error
|
||||
* <li> test function did return false for all items in the list
|
||||
* </ul>
|
||||
*/
|
||||
@UiThread
|
||||
protected <T> void removeAndUpdateListItems(
|
||||
MutableLiveData<LiveResult<List<T>>> liveData,
|
||||
Function<T, Boolean> test) {
|
||||
List<T> 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.
|
||||
|
||||
Reference in New Issue
Block a user