mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 05:39:53 +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.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
|
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.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
@@ -52,8 +53,9 @@ public class AddContactViewModel extends DbViewModel {
|
|||||||
AddContactViewModel(Application application,
|
AddContactViewModel(Application application,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager) {
|
LifecycleManager lifecycleManager,
|
||||||
super(application, dbExecutor, lifecycleManager);
|
TransactionManager db) {
|
||||||
|
super(application, dbExecutor, lifecycleManager, db);
|
||||||
this.contactManager = contactManager;
|
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.contact.event.PendingContactStateChangedEvent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
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.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -56,10 +57,11 @@ public class PendingContactListViewModel extends DbViewModel
|
|||||||
PendingContactListViewModel(Application application,
|
PendingContactListViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
|
TransactionManager db,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
RendezvousPoller rendezvousPoller,
|
RendezvousPoller rendezvousPoller,
|
||||||
EventBus eventBus) {
|
EventBus eventBus) {
|
||||||
super(application, dbExecutor, lifecycleManager);
|
super(application, dbExecutor, lifecycleManager, db);
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
this.rendezvousPoller = rendezvousPoller;
|
this.rendezvousPoller = rendezvousPoller;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public class ConversationViewModel extends DbViewModel
|
|||||||
PrivateMessageFactory privateMessageFactory,
|
PrivateMessageFactory privateMessageFactory,
|
||||||
AttachmentRetriever attachmentRetriever,
|
AttachmentRetriever attachmentRetriever,
|
||||||
AttachmentCreator attachmentCreator) {
|
AttachmentCreator attachmentCreator) {
|
||||||
super(application, dbExecutor, lifecycleManager);
|
super(application, dbExecutor, lifecycleManager, db);
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
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.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -78,8 +79,9 @@ public class ImageViewModel extends DbViewModel implements EventListener {
|
|||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
|
TransactionManager db,
|
||||||
@IoExecutor Executor ioExecutor) {
|
@IoExecutor Executor ioExecutor) {
|
||||||
super(application, dbExecutor, lifecycleManager);
|
super(application, dbExecutor, lifecycleManager, db);
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.app.Application;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
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.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
@@ -51,8 +52,9 @@ public class NavDrawerViewModel extends DbViewModel {
|
|||||||
NavDrawerViewModel(Application app,
|
NavDrawerViewModel(Application app,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
|
TransactionManager db,
|
||||||
SettingsManager settingsManager) {
|
SettingsManager settingsManager) {
|
||||||
super(app, dbExecutor, lifecycleManager);
|
super(app, dbExecutor, lifecycleManager, db);
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.content.IntentFilter;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
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.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -85,10 +86,10 @@ public class PluginViewModel extends DbViewModel implements EventListener {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PluginViewModel(Application app, @DatabaseExecutor Executor dbExecutor,
|
PluginViewModel(Application app, @DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager, TransactionManager db,
|
||||||
SettingsManager settingsManager, PluginManager pluginManager,
|
SettingsManager settingsManager, PluginManager pluginManager,
|
||||||
EventBus eventBus, NetworkManager networkManager) {
|
EventBus eventBus, NetworkManager networkManager) {
|
||||||
super(app, dbExecutor, lifecycleManager);
|
super(app, dbExecutor, lifecycleManager, db);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
|
|||||||
@@ -1,20 +1,36 @@
|
|||||||
package org.briarproject.briar.android.viewmodel;
|
package org.briarproject.briar.android.viewmodel;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
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.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
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.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
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.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 java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -25,17 +41,28 @@ public abstract class DbViewModel extends AndroidViewModel {
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final LifecycleManager lifecycleManager;
|
private final LifecycleManager lifecycleManager;
|
||||||
|
private final TransactionManager db;
|
||||||
|
|
||||||
public DbViewModel(
|
public DbViewModel(
|
||||||
@NonNull Application application,
|
@NonNull Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager) {
|
LifecycleManager lifecycleManager,
|
||||||
|
TransactionManager db) {
|
||||||
super(application);
|
super(application);
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.lifecycleManager = lifecycleManager;
|
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(() -> {
|
dbExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
lifecycleManager.waitForDatabase();
|
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> {
|
public class LiveResult<T> {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private T result;
|
private final T result;
|
||||||
@Nullable
|
@Nullable
|
||||||
private Exception exception;
|
private final Exception exception;
|
||||||
|
|
||||||
public LiveResult(T result) {
|
public LiveResult(T result) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
|
|||||||
Reference in New Issue
Block a user