Compare commits

...

1 Commits

Author SHA1 Message Date
Torsten Grote
b4f38e81ea Allow DbViewModel work on things other than lists. 2021-03-23 14:15:14 +00:00
9 changed files with 99 additions and 134 deletions

View File

@@ -53,13 +53,9 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
protected final AndroidNotificationManager notificationManager;
protected final BlogManager blogManager;
protected final MutableLiveData<LiveResult<List<BlogPostItem>>> blogPosts =
protected final MutableLiveData<LiveResult<ListUpdate>> blogPosts =
new MutableLiveData<>();
// UI thread
@Nullable
private Boolean postAddedWasLocal = null;
BaseViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
@@ -147,11 +143,10 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
@UiThread
private void onBlogPostItemAdded(BlogPostItem item, boolean local) {
List<BlogPostItem> items = addListItem(blogPosts, item);
List<BlogPostItem> items = addListItem(getBlogPostItems(), item);
if (items != null) {
Collections.sort(items);
postAddedWasLocal = local;
blogPosts.setValue(new LiveResult<>(items));
blogPosts.setValue(new LiveResult<>(new ListUpdate(local, items)));
}
}
@@ -168,17 +163,37 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
});
}
LiveData<LiveResult<List<BlogPostItem>>> getBlogPosts() {
LiveData<LiveResult<ListUpdate>> getBlogPosts() {
return blogPosts;
}
@UiThread
@Nullable
Boolean getPostAddedWasLocalAndReset() {
if (postAddedWasLocal == null) return null;
boolean wasLocal = postAddedWasLocal;
postAddedWasLocal = null;
return wasLocal;
protected List<BlogPostItem> getBlogPostItems() {
LiveResult<ListUpdate> value = blogPosts.getValue();
if (value == null) return null;
ListUpdate result = value.getResultOrNull();
return result == null ? null : result.getItems();
}
static class ListUpdate {
@Nullable
private final Boolean postAddedWasLocal;
private final List<BlogPostItem> items;
ListUpdate(@Nullable Boolean postAddedWasLocal,
List<BlogPostItem> items) {
this.postAddedWasLocal = postAddedWasLocal;
this.items = items;
}
@Nullable
public Boolean getPostAddedWasLocal() {
return postAddedWasLocal;
}
public List<BlogPostItem> getItems() {
return items;
}
}
}

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.BaseViewModel.ListUpdate;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
import org.briarproject.briar.android.sharing.ShareBlogActivity;
@@ -22,8 +23,6 @@ import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import java.util.List;
import javax.inject.Inject;
import androidx.annotation.Nullable;
@@ -174,9 +173,9 @@ public class BlogFragment extends BaseFragment
return TAG;
}
private void onBlogPostsLoaded(List<BlogPostItem> items) {
adapter.submitList(items, () -> {
Boolean wasLocal = viewModel.getPostAddedWasLocalAndReset();
private void onBlogPostsLoaded(ListUpdate update) {
adapter.submitList(update.getItems(), () -> {
Boolean wasLocal = update.getPostAddedWasLocal();
if (wasLocal != null && wasLocal) {
list.scrollToPosition(0);
displaySnackbar(R.string.blogs_blog_post_created,

View File

@@ -145,7 +145,8 @@ class BlogViewModel extends BaseViewModel {
}
private void loadBlogPosts(GroupId groupId) {
loadList(txn -> loadBlogPosts(txn, groupId), blogPosts::setValue);
loadList(txn -> new ListUpdate(null, loadBlogPosts(txn, groupId)),
blogPosts::setValue);
}
private void loadSharingContacts(GroupId groupId) {

View File

@@ -13,14 +13,13 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.BaseViewModel.ListUpdate;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.blog.Blog;
import java.util.List;
import javax.inject.Inject;
import androidx.annotation.Nullable;
@@ -101,10 +100,9 @@ public class FeedFragment extends BaseFragment
list.stopPeriodicUpdate();
}
private void onBlogPostsLoaded(List<BlogPostItem> items) {
if (items.isEmpty()) list.showData();
else adapter.submitList(items, () -> {
Boolean wasLocal = viewModel.getPostAddedWasLocalAndReset();
private void onBlogPostsLoaded(ListUpdate update) {
adapter.submitList(update.getItems(), () -> {
Boolean wasLocal = update.getPostAddedWasLocal();
if (wasLocal != null && wasLocal) {
showSnackBar(R.string.blogs_blog_post_created);
} else if (wasLocal != null) {

View File

@@ -13,7 +13,6 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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;
@@ -34,10 +33,8 @@ import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
@@ -70,15 +67,8 @@ class FeedViewModel extends BaseViewModel {
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
LOG.info("Blog post added");
onBlogPostAdded(b.getHeader(), b.isLocal());
} else if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog added");
// TODO how can this even happen?
// added RSS feeds should trigger BlogPostAddedEvent, no?
onBlogAdded(g.getGroup().getId());
}
} else if (e instanceof GroupRemovedEvent) {
}
if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog removed");
@@ -119,7 +109,7 @@ class FeedViewModel extends BaseViewModel {
}
@DatabaseExecutor
private List<BlogPostItem> loadAllBlogPosts(Transaction txn)
private ListUpdate loadAllBlogPosts(Transaction txn)
throws DbException {
long start = now();
List<BlogPostItem> posts = new ArrayList<>();
@@ -128,29 +118,17 @@ class FeedViewModel extends BaseViewModel {
}
Collections.sort(posts);
logDuration(LOG, "Loading all posts", start);
return posts;
}
private void onBlogAdded(GroupId g) {
runOnDbThread(true, txn -> {
List<BlogPostItem> posts = loadBlogPosts(txn, g);
txn.attach(() -> onBlogPostItemsAdded(posts));
}, e -> logException(LOG, WARNING, e));
return new ListUpdate(null, posts);
}
@UiThread
private void onBlogPostItemsAdded(List<BlogPostItem> posts) {
List<BlogPostItem> items = addListItems(blogPosts, posts);
private void onBlogRemoved(GroupId g) {
List<BlogPostItem> items = removeListItems(getBlogPostItems(),
item -> item.getGroupId().equals(g)
);
if (items != null) {
Collections.sort(items);
blogPosts.setValue(new LiveResult<>(items));
blogPosts.setValue(new LiveResult<>(new ListUpdate(null, items)));
}
}
private void onBlogRemoved(GroupId g) {
removeAndUpdateListItems(blogPosts, item ->
item.getGroupId().equals(g)
);
}
}

View File

@@ -151,7 +151,7 @@ public class ContactsViewModel extends DbViewModel implements EventListener {
@UiThread
private void updateItem(ContactId c,
Function<ContactListItem, ContactListItem> replacer, boolean sort) {
List<ContactListItem> list = updateListItems(contactListItems,
List<ContactListItem> list = updateListItems(getList(contactListItems),
itemToTest -> itemToTest.getContact().getId().equals(c),
replacer);
if (list == null) return;
@@ -161,10 +161,8 @@ public class ContactsViewModel extends DbViewModel implements EventListener {
@UiThread
private void removeItem(ContactId c) {
List<ContactListItem> list = removeListItems(contactListItems,
removeAndUpdateListItems(contactListItems,
itemToTest -> itemToTest.getContact().getId().equals(c));
if (list == null) return;
contactListItems.setValue(new LiveResult<>(list));
}
}

View File

@@ -145,7 +145,7 @@ class ForumListViewModel extends DbViewModel implements EventListener {
@UiThread
private void onForumPostReceived(GroupId g, ForumPostHeader header) {
List<ForumListItem> list = updateListItems(forumItems,
List<ForumListItem> list = updateListItems(getList(forumItems),
itemToTest -> itemToTest.getForum().getId().equals(g),
itemToUpdate -> new ForumListItem(itemToUpdate, header));
if (list == null) return;

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.privategroup.list;
import android.app.Application;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -173,7 +172,7 @@ class GroupListViewModel extends DbViewModel implements EventListener {
@UiThread
private void onGroupMessageAdded(GroupMessageHeader header) {
GroupId g = header.getGroupId();
List<GroupItem> list = updateListItems(groupItems,
List<GroupItem> list = updateListItems(getList(groupItems),
itemToTest -> itemToTest.getId().equals(g),
itemToUpdate -> new GroupItem(itemToUpdate, header));
if (list == null) return;
@@ -184,7 +183,7 @@ class GroupListViewModel extends DbViewModel implements EventListener {
@UiThread
private void onGroupDissolved(GroupId groupId) {
List<GroupItem> list = updateListItems(groupItems,
List<GroupItem> list = updateListItems(getList(groupItems),
itemToTest -> itemToTest.getId().equals(groupId),
itemToUpdate -> new GroupItem(itemToUpdate, true));
if (list == null) return;

View File

@@ -120,9 +120,11 @@ public abstract class DbViewModel extends AndroidViewModel {
* This method ensures that those operations can be processed on the
* UiThread in the correct order so that the removed message will not be
* re-added when the re-load completes.
*
* TODO: Rename this method and update javadoc, as it's not restricted to
* lists
*/
protected <T extends List<?>> void loadList(
DbCallable<T, DbException> task,
protected <T> void loadList(DbCallable<T, DbException> task,
UiConsumer<LiveResult<T>> uiConsumer) {
dbExecutor.execute(() -> {
try {
@@ -149,63 +151,46 @@ public abstract class DbViewModel extends AndroidViewModel {
}
/**
* Creates a copy of the list available in the given LiveData
* and adds the given item to the copy.
* Creates a copy of the given list and adds the given item to the copy.
*
* @return a copy of the list in the LiveData with item added or null when
* <ul>
* <li> LiveData does not have a value
* <li> LiveResult in the LiveData has an error
* </ul>
* @return an updated copy of the list, or null if the list is null
*/
@Nullable
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;
protected <T> List<T> addListItem(@Nullable List<T> list, T item) {
if (list == null) return null;
List<T> copy = new ArrayList<>(list);
copy.add(item);
return copy;
}
/**
* Creates a copy of the list available in the given LiveData
* and adds the given items to the copy.
* Creates a copy of the given list 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>
* @return an updated copy of the list, or null if the list is null
*/
@Nullable
protected <T> List<T> addListItems(LiveData<LiveResult<List<T>>> liveData,
protected <T> List<T> addListItems(@Nullable List<T> list,
Collection<T> items) {
List<T> copiedItems = getListCopy(liveData);
if (copiedItems == null) return null;
copiedItems.addAll(items);
return copiedItems;
if (list == null) return null;
List<T> copy = new ArrayList<>(list);
copy.addAll(items);
return copy;
}
/**
* Creates a copy of the list available in the given LiveData
* and replaces items where the given test function returns true.
* Creates a copy of the given list, replacing items where the given test
* function returns true.
*
* @return a copy of the list in the LiveData with item(s) replaced
* or null when 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>
* @return an updated copy of the list, or null if either the list is null
* or the test function returns false for all items
*/
@Nullable
protected <T> List<T> updateListItems(
LiveData<LiveResult<List<T>>> liveData, Function<T, Boolean> test,
Function<T, T> replacer) {
List<T> items = getListCopy(liveData);
if (items == null) return null;
protected <T> List<T> updateListItems(@Nullable List<T> list,
Function<T, Boolean> test, Function<T, T> replacer) {
if (list == null) return null;
List<T> copy = new ArrayList<>(list);
ListIterator<T> iterator = items.listIterator();
ListIterator<T> iterator = copy.listIterator();
boolean changed = false;
while (iterator.hasNext()) {
T item = iterator.next();
@@ -214,28 +199,23 @@ public abstract class DbViewModel extends AndroidViewModel {
iterator.set(replacer.apply(item));
}
}
return changed ? items : null;
return changed ? copy : null;
}
/**
* Creates a copy of the list available in the given LiveData
* and removes the items from it where the given test function returns true.
* Creates a copy of the given list, removing items from it where the given
* test function returns true.
*
* @return a copy of the list in the LiveData with item(s) removed
* or null when 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>
* @return an updated copy of the list, or null if either the list is null
* or the test function returns false for all items
*/
@Nullable
protected <T> List<T> removeListItems(
LiveData<LiveResult<List<T>>> liveData, Function<T, Boolean> test) {
List<T> items = getListCopy(liveData);
if (items == null) return null;
protected <T> List<T> removeListItems(@Nullable List<T> list,
Function<T, Boolean> test) {
if (list == null) return null;
List<T> copy = new ArrayList<>(list);
ListIterator<T> iterator = items.listIterator();
ListIterator<T> iterator = copy.listIterator();
boolean changed = false;
while (iterator.hasNext()) {
T item = iterator.next();
@@ -244,7 +224,7 @@ public abstract class DbViewModel extends AndroidViewModel {
iterator.remove();
}
}
return changed ? items : null;
return changed ? copy : null;
}
/**
@@ -255,29 +235,26 @@ public abstract class DbViewModel extends AndroidViewModel {
* <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
* <li> test function returned 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));
List<T> copy = removeListItems(getList(liveData), test);
if (copy != null) liveData.setValue(new LiveResult<>(copy));
}
/**
* Retrieves a copy of the list of items from the given LiveData
* or null if it is not available.
* The list copy can be safely mutated.
* Returns the list of items from the given LiveData, or null if no list is
* available.
*/
@Nullable
private <T> List<T> getListCopy(LiveData<LiveResult<List<T>>> liveData) {
protected <T> List<T> getList(LiveData<LiveResult<List<T>>> liveData) {
LiveResult<List<T>> value = liveData.getValue();
if (value == null) return null;
List<T> list = value.getResultOrNull();
if (list == null) return null;
return new ArrayList<>(list);
return value.getResultOrNull();
}
/**