From b4f38e81ea72646de083ffcb4f6154ad6cae9fa9 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 22 Mar 2021 16:48:16 -0300 Subject: [PATCH] Allow DbViewModel work on things other than lists. --- .../briar/android/blog/BaseViewModel.java | 45 ++++--- .../briar/android/blog/BlogFragment.java | 9 +- .../briar/android/blog/BlogViewModel.java | 3 +- .../briar/android/blog/FeedFragment.java | 10 +- .../briar/android/blog/FeedViewModel.java | 40 ++----- .../android/contact/ContactsViewModel.java | 6 +- .../android/forum/ForumListViewModel.java | 2 +- .../privategroup/list/GroupListViewModel.java | 5 +- .../briar/android/viewmodel/DbViewModel.java | 113 +++++++----------- 9 files changed, 99 insertions(+), 134 deletions(-) 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 03c02af29..6ab092e0c 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 @@ -53,13 +53,9 @@ abstract class BaseViewModel extends DbViewModel implements EventListener { protected final AndroidNotificationManager notificationManager; protected final BlogManager blogManager; - protected final MutableLiveData>> blogPosts = + protected final MutableLiveData> 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 items = addListItem(blogPosts, item); + List 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>> getBlogPosts() { + LiveData> getBlogPosts() { return blogPosts; } @UiThread - @Nullable - Boolean getPostAddedWasLocalAndReset() { - if (postAddedWasLocal == null) return null; - boolean wasLocal = postAddedWasLocal; - postAddedWasLocal = null; - return wasLocal; + protected List getBlogPostItems() { + LiveResult 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 items; + + ListUpdate(@Nullable Boolean postAddedWasLocal, + List items) { + this.postAddedWasLocal = postAddedWasLocal; + this.items = items; + } + + @Nullable + public Boolean getPostAddedWasLocal() { + return postAddedWasLocal; + } + + public List getItems() { + return items; + } + } } 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 d9a7a0284..f43b209fd 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 @@ -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 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, 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 0ba0d185a..7e7d83c30 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 @@ -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) { 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 4ea4611a6..bf7ce988c 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 @@ -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 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) { 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 e4f6cbee5..0d9d4f5ce 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 @@ -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 loadAllBlogPosts(Transaction txn) + private ListUpdate loadAllBlogPosts(Transaction txn) throws DbException { long start = now(); List 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 posts = loadBlogPosts(txn, g); - txn.attach(() -> onBlogPostItemsAdded(posts)); - }, e -> logException(LOG, WARNING, e)); + return new ListUpdate(null, posts); } @UiThread - private void onBlogPostItemsAdded(List posts) { - List items = addListItems(blogPosts, posts); + private void onBlogRemoved(GroupId g) { + List 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) - ); - } - } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactsViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactsViewModel.java index e0b1ab910..56dc213e1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactsViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactsViewModel.java @@ -151,7 +151,7 @@ public class ContactsViewModel extends DbViewModel implements EventListener { @UiThread private void updateItem(ContactId c, Function replacer, boolean sort) { - List list = updateListItems(contactListItems, + List 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 list = removeListItems(contactListItems, + removeAndUpdateListItems(contactListItems, itemToTest -> itemToTest.getContact().getId().equals(c)); - if (list == null) return; - contactListItems.setValue(new LiveResult<>(list)); } } 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 52ad8c449..5cc5fc889 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 @@ -145,7 +145,7 @@ class ForumListViewModel extends DbViewModel implements EventListener { @UiThread private void onForumPostReceived(GroupId g, ForumPostHeader header) { - List list = updateListItems(forumItems, + List list = updateListItems(getList(forumItems), itemToTest -> itemToTest.getForum().getId().equals(g), itemToUpdate -> new ForumListItem(itemToUpdate, header)); if (list == null) return; 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 3eedd40fc..e4305ab52 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 @@ -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 list = updateListItems(groupItems, + List 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 list = updateListItems(groupItems, + List list = updateListItems(getList(groupItems), itemToTest -> itemToTest.getId().equals(groupId), itemToUpdate -> new GroupItem(itemToUpdate, true)); if (list == null) return; 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 c99b1acf4..99e1eecb5 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 @@ -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 > void loadList( - DbCallable task, + protected void loadList(DbCallable task, UiConsumer> 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 - *
    - *
  • LiveData does not have a value - *
  • LiveResult in the LiveData has an error - *
+ * @return an updated copy of the list, or null if the list is null */ @Nullable - protected List addListItem(LiveData>> liveData, - T item) { - List items = getListCopy(liveData); - if (items == null) return null; - items.add(item); - return items; + protected List addListItem(@Nullable List list, T item) { + if (list == null) return null; + List 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 - *
    - *
  • LiveData does not have a value - *
  • LiveResult in the LiveData has an error - *
+ * @return an updated copy of the list, or null if the list is null */ @Nullable - protected List addListItems(LiveData>> liveData, + protected List addListItems(@Nullable List list, Collection items) { - List copiedItems = getListCopy(liveData); - if (copiedItems == null) return null; - copiedItems.addAll(items); - return copiedItems; + if (list == null) return null; + List 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 - *
    - *
  • LiveData does not have a value - *
  • LiveResult in the LiveData has an error - *
  • test function did return false for all items in the list - *
+ * @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 List updateListItems( - LiveData>> liveData, Function test, - Function replacer) { - List items = getListCopy(liveData); - if (items == null) return null; + protected List updateListItems(@Nullable List list, + Function test, Function replacer) { + if (list == null) return null; + List copy = new ArrayList<>(list); - ListIterator iterator = items.listIterator(); + ListIterator 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 - *
    - *
  • LiveData does not have a value - *
  • LiveResult in the LiveData has an error - *
  • test function did return false for all items in the list - *
+ * @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 List removeListItems( - LiveData>> liveData, Function test) { - List items = getListCopy(liveData); - if (items == null) return null; + protected List removeListItems(@Nullable List list, + Function test) { + if (list == null) return null; + List copy = new ArrayList<>(list); - ListIterator iterator = items.listIterator(); + ListIterator 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 { *
    *
  • LiveData does not have a value *
  • LiveResult in the LiveData has an error - *
  • test function did return false for all items in the list + *
  • test function returned 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)); + List 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 List getListCopy(LiveData>> liveData) { + protected List getList(LiveData>> liveData) { LiveResult> value = liveData.getValue(); if (value == null) return null; - List list = value.getResultOrNull(); - if (list == null) return null; - return new ArrayList<>(list); + return value.getResultOrNull(); } /**