mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Add methods to DbViewModel for loading and updating lists of items
This commit is contained in:
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||
@@ -52,8 +53,9 @@ public class AddContactViewModel extends DbViewModel {
|
||||
AddContactViewModel(Application application,
|
||||
ContactManager contactManager,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager) {
|
||||
super(application, dbExecutor, lifecycleManager);
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db) {
|
||||
super(application, dbExecutor, lifecycleManager, db);
|
||||
this.contactManager = contactManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
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;
|
||||
@@ -56,10 +57,11 @@ public class PendingContactListViewModel extends DbViewModel
|
||||
PendingContactListViewModel(Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
ContactManager contactManager,
|
||||
RendezvousPoller rendezvousPoller,
|
||||
EventBus eventBus) {
|
||||
super(application, dbExecutor, lifecycleManager);
|
||||
super(application, dbExecutor, lifecycleManager, db);
|
||||
this.contactManager = contactManager;
|
||||
this.rendezvousPoller = rendezvousPoller;
|
||||
this.eventBus = eventBus;
|
||||
|
||||
@@ -113,7 +113,7 @@ public class ConversationViewModel extends DbViewModel
|
||||
PrivateMessageFactory privateMessageFactory,
|
||||
AttachmentRetriever attachmentRetriever,
|
||||
AttachmentCreator attachmentCreator) {
|
||||
super(application, dbExecutor, lifecycleManager);
|
||||
super(application, dbExecutor, lifecycleManager, db);
|
||||
this.db = db;
|
||||
this.eventBus = eventBus;
|
||||
this.messagingManager = messagingManager;
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.view.View;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
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;
|
||||
@@ -78,8 +79,9 @@ public class ImageViewModel extends DbViewModel implements EventListener {
|
||||
EventBus eventBus,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
@IoExecutor Executor ioExecutor) {
|
||||
super(application, dbExecutor, lifecycleManager);
|
||||
super(application, dbExecutor, lifecycleManager, db);
|
||||
this.messagingManager = messagingManager;
|
||||
this.eventBus = eventBus;
|
||||
this.ioExecutor = ioExecutor;
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
@@ -51,8 +52,9 @@ public class NavDrawerViewModel extends DbViewModel {
|
||||
NavDrawerViewModel(Application app,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
SettingsManager settingsManager) {
|
||||
super(app, dbExecutor, lifecycleManager);
|
||||
super(app, dbExecutor, lifecycleManager, db);
|
||||
this.settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.content.IntentFilter;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
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;
|
||||
@@ -85,10 +86,10 @@ public class PluginViewModel extends DbViewModel implements EventListener {
|
||||
|
||||
@Inject
|
||||
PluginViewModel(Application app, @DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager,
|
||||
LifecycleManager lifecycleManager, TransactionManager db,
|
||||
SettingsManager settingsManager, PluginManager pluginManager,
|
||||
EventBus eventBus, NetworkManager networkManager) {
|
||||
super(app, dbExecutor, lifecycleManager);
|
||||
super(app, dbExecutor, lifecycleManager, db);
|
||||
this.app = app;
|
||||
this.settingsManager = settingsManager;
|
||||
this.pluginManager = pluginManager;
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
package org.briarproject.briar.android.viewmodel;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.Handler;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbCallable;
|
||||
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.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.arch.core.util.Function;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import static android.os.Looper.getMainLooper;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -25,17 +41,28 @@ public abstract class DbViewModel extends AndroidViewModel {
|
||||
@DatabaseExecutor
|
||||
private final Executor dbExecutor;
|
||||
private final LifecycleManager lifecycleManager;
|
||||
private final TransactionManager db;
|
||||
|
||||
public DbViewModel(
|
||||
@NonNull Application application,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager) {
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db) {
|
||||
super(application);
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.lifecycleManager = lifecycleManager;
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
public void runOnDbThread(Runnable task) {
|
||||
/**
|
||||
* Runs the given task on the {@link DatabaseExecutor}
|
||||
* and waits for the DB to open.
|
||||
* <p>
|
||||
* If you need a list of items to be displayed in a
|
||||
* {@link RecyclerView.Adapter},
|
||||
* use {@link #loadList(DbCallable, UiCallable)} instead.
|
||||
*/
|
||||
protected void runOnDbThread(Runnable task) {
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
@@ -47,4 +74,119 @@ public abstract class DbViewModel extends AndroidViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a list of items on the {@link DatabaseExecutor} within a single
|
||||
* {@link Transaction} and publishes it as a {@link LiveResult}
|
||||
* to the {@link UiThread}.
|
||||
* <p>
|
||||
* Use this to ensure that modifications to your local list do not get
|
||||
* overridden by database loads that were in progress while the modification
|
||||
* was made.
|
||||
* E.g. An event about the removal of a message causes the message item to
|
||||
* be removed from the local list while all messages are reloaded.
|
||||
* 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.
|
||||
*/
|
||||
protected <T extends List<?>> void loadList(
|
||||
DbCallable<T, DbException> task,
|
||||
UiCallable<LiveResult<T>> uiUpdate) {
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
db.transaction(true, txn -> {
|
||||
T t = task.call(txn);
|
||||
txn.attach(() -> uiUpdate.call(new LiveResult<>(t)));
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warning("Interrupted while waiting for database");
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
new Handler(getMainLooper())
|
||||
.post(() -> uiUpdate.call(new LiveResult<>(e)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNullByDefault
|
||||
public interface UiCallable<T> {
|
||||
@UiThread
|
||||
void call(T t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the list available in the given LiveData
|
||||
* and replaces 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>
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> List<T> updateListItem(
|
||||
LiveData<LiveResult<List<T>>> liveData, Function<T, Boolean> test,
|
||||
Function<T, T> replacer) {
|
||||
List<T> items = getList(liveData);
|
||||
if (items == null) return null;
|
||||
|
||||
ListIterator<T> iterator = items.listIterator();
|
||||
boolean changed = false;
|
||||
while (iterator.hasNext()) {
|
||||
T item = iterator.next();
|
||||
if (test.apply(item)) {
|
||||
changed = true;
|
||||
iterator.set(replacer.apply(item));
|
||||
}
|
||||
}
|
||||
return changed ? items : 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.
|
||||
*
|
||||
* @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>
|
||||
*/
|
||||
@Nullable
|
||||
protected <T> List<T> removeListItem(
|
||||
LiveData<LiveResult<List<T>>> liveData, Function<T, Boolean> test) {
|
||||
List<T> items = getList(liveData);
|
||||
if (items == null) return null;
|
||||
|
||||
ListIterator<T> iterator = items.listIterator();
|
||||
boolean changed = false;
|
||||
while (iterator.hasNext()) {
|
||||
T item = iterator.next();
|
||||
if (test.apply(item)) {
|
||||
changed = true;
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
return changed ? items : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of items from the given LiveData
|
||||
* or null if it is not available.
|
||||
*/
|
||||
@Nullable
|
||||
private <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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import androidx.annotation.Nullable;
|
||||
public class LiveResult<T> {
|
||||
|
||||
@Nullable
|
||||
private T result;
|
||||
private final T result;
|
||||
@Nullable
|
||||
private Exception exception;
|
||||
private final Exception exception;
|
||||
|
||||
public LiveResult(T result) {
|
||||
this.result = result;
|
||||
|
||||
Reference in New Issue
Block a user