diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java index c79f7c0da..4fb5042c2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/remote/AddContactViewModel.java @@ -2,12 +2,9 @@ package org.briarproject.briar.android.contact.add.remote; import android.app.Application; -import org.briarproject.bramble.api.FormatException; -import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.contact.ContactManager; 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; @@ -18,7 +15,6 @@ import org.briarproject.briar.android.viewmodel.LiveEvent; import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.android.viewmodel.MutableLiveEvent; -import java.security.GeneralSecurityException; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -101,17 +97,11 @@ public class AddContactViewModel extends DbViewModel { void addContact(String nickname) { if (remoteHandshakeLink == null) throw new IllegalStateException(); runOnDbThread(() -> { - try { - contactManager.addPendingContact(remoteHandshakeLink, nickname); - addContactResult.postValue(new LiveResult<>(true)); - } catch (UnsupportedVersionException e) { - logException(LOG, WARNING, e); - addContactResult.postValue(new LiveResult<>(e)); - } catch (DbException | FormatException - | GeneralSecurityException e) { - logException(LOG, WARNING, e); - addContactResult.postValue(new LiveResult<>(e)); - } + contactManager.addPendingContact(remoteHandshakeLink, nickname); + addContactResult.postValue(new LiveResult<>(true)); + }, e -> { + logException(LOG, WARNING, e); + addContactResult.postValue(new LiveResult<>(e)); }); } @@ -121,13 +111,13 @@ public class AddContactViewModel extends DbViewModel { public void updatePendingContact(String name, PendingContact p) { runOnDbThread(() -> { - try { - contactManager.removePendingContact(p.getId()); - addContact(name); - } catch (NoSuchPendingContactException e) { + contactManager.removePendingContact(p.getId()); + addContact(name); + }, e -> { + if (e instanceof NoSuchPendingContactException) { logException(LOG, WARNING, e); // no error in UI as pending contact was converted into contact - } catch (DbException e) { + } else { logException(LOG, WARNING, e); addContactResult.postValue(new LiveResult<>(e)); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java index de17c5a82..cdccbd5f8 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java @@ -149,7 +149,7 @@ public class ConversationViewModel extends DbViewModel AttachmentReceivedEvent a = (AttachmentReceivedEvent) e; if (a.getContactId().equals(contactId)) { LOG.info("Attachment received"); - runOnDbThread(() -> attachmentRetriever + runOnDbThreadOrLogException(() -> attachmentRetriever .loadAttachmentItem(a.getMessageId())); } } else if (e instanceof AvatarUpdatedEvent) { @@ -194,18 +194,18 @@ public class ConversationViewModel extends DbViewModel private void loadContact(ContactId contactId) { runOnDbThread(() -> { - try { - long start = now(); - Contact c = contactManager.getContact(contactId); - AuthorInfo authorInfo = authorManager.getAuthorInfo(c); - contactItem.postValue(new ContactItem(c, authorInfo)); - logDuration(LOG, "Loading contact", start); - start = now(); - checkFeaturesAndOnboarding(contactId); - logDuration(LOG, "Checking for image support", start); - } catch (NoSuchContactException e) { + long start = now(); + Contact c = contactManager.getContact(contactId); + AuthorInfo authorInfo = authorManager.getAuthorInfo(c); + contactItem.postValue(new ContactItem(c, authorInfo)); + logDuration(LOG, "Loading contact", start); + start = now(); + checkFeaturesAndOnboarding(contactId); + logDuration(LOG, "Checking for image support", start); + }, e -> { + if (e instanceof NoSuchContactException) { contactDeleted.postValue(true); - } catch (DbException e) { + } else { logException(LOG, WARNING, e); } }); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java index ca0b75179..fc7804fce 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageViewModel.java @@ -6,7 +6,6 @@ import android.net.Uri; 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; @@ -198,14 +197,12 @@ public class ImageViewModel extends DbViewModel implements EventListener { private void saveImage(AttachmentItem attachment, OutputStreamProvider osp, @Nullable Runnable afterCopy) { runOnDbThread(() -> { - try { - Attachment a = - attachmentReader.getAttachment(attachment.getHeader()); - copyImageFromDb(a, osp, afterCopy); - } catch (DbException e) { - logException(LOG, WARNING, e); - saveState.postEvent(true); - } + Attachment a = + attachmentReader.getAttachment(attachment.getHeader()); + copyImageFromDb(a, osp, afterCopy); + }, e -> { + logException(LOG, WARNING, e); + saveState.postEvent(true); }); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumViewModel.java index b30c2db03..92789af8d 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumViewModel.java @@ -184,12 +184,12 @@ class ForumViewModel extends ThreadListViewModel { } public void loadSharingContacts() { - runOnDbThreadOrLogException(() -> { + runOnDbThreadOrLogException(true, txn -> { Collection contacts = - forumSharingManager.getSharedWith(groupId); + forumSharingManager.getSharedWith(txn, groupId); Collection contactIds = new ArrayList<>(contacts.size()); for (Contact c : contacts) contactIds.add(c.getId()); - sharingController.addAll(contactIds); + txn.attach(() -> sharingController.addAll(contactIds)); }); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java index fdc2bf45b..0e366a68c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerViewModel.java @@ -3,7 +3,6 @@ package org.briarproject.briar.android.navdrawer; 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; @@ -114,15 +113,12 @@ public class NavDrawerViewModel extends DbViewModel { return; } runOnDbThread(() -> { - try { - Settings settings = - settingsManager.getSettings(SETTINGS_NAMESPACE); - boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true); - shouldAskForDozeWhitelisting.postValue(ask); - } catch (DbException e) { - logException(LOG, WARNING, e); - shouldAskForDozeWhitelisting.postValue(true); - } + Settings settings = settingsManager.getSettings(SETTINGS_NAMESPACE); + boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true); + shouldAskForDozeWhitelisting.postValue(ask); + }, e -> { + logException(LOG, WARNING, e); + shouldAskForDozeWhitelisting.postValue(true); }); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupViewModel.java index 039e59346..544f09eff 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupViewModel.java @@ -223,14 +223,14 @@ class GroupViewModel extends ThreadListViewModel { } public void loadSharingContacts() { - runOnDbThreadOrLogException(() -> { + runOnDbThreadOrLogException(true, txn -> { Collection members = - privateGroupManager.getMembers(groupId); + privateGroupManager.getMembers(txn, groupId); Collection contactIds = new ArrayList<>(); for (GroupMember m : members) { if (m.getContactId() != null) contactIds.add(m.getContactId()); } - sharingController.addAll(contactIds); + txn.attach(() -> sharingController.addAll(contactIds)); }); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingController.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingController.java index 9a175534e..3fd3f4e96 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingController.java @@ -1,7 +1,6 @@ package org.briarproject.briar.android.sharing; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -28,7 +27,7 @@ public interface SharingController { /** * Adds a collection of contacts to be tracked. */ - @DatabaseExecutor + @UiThread void addAll(Collection contacts); /** diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingControllerImpl.java index 55a4149a8..b58ab1a08 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingControllerImpl.java @@ -2,14 +2,12 @@ package org.briarproject.briar.android.sharing; import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.db.DatabaseExecutor; 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.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; -import org.briarproject.bramble.api.system.AndroidExecutor; import java.util.Collection; import java.util.HashSet; @@ -26,7 +24,6 @@ public class SharingControllerImpl implements SharingController, EventListener { private final EventBus eventBus; private final ConnectionRegistry connectionRegistry; - private final AndroidExecutor executor; // UI thread private final Set contacts = new HashSet<>(); @@ -35,11 +32,9 @@ public class SharingControllerImpl implements SharingController, EventListener { @Inject SharingControllerImpl(EventBus eventBus, - ConnectionRegistry connectionRegistry, - AndroidExecutor executor) { + ConnectionRegistry connectionRegistry) { this.eventBus = eventBus; this.connectionRegistry = connectionRegistry; - this.executor = executor; eventBus.addListener(this); } @@ -78,13 +73,11 @@ public class SharingControllerImpl implements SharingController, EventListener { return online; } + @UiThread @Override - @DatabaseExecutor public void addAll(Collection c) { - executor.runOnUiThread(() -> { - contacts.addAll(c); - updateLiveData(); - }); + contacts.addAll(c); + updateLiveData(); } @UiThread 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 3507b1a9b..692344448 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 @@ -6,6 +6,7 @@ import org.briarproject.bramble.api.ThrowingRunnable; 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.DbRunnable; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager; @@ -24,6 +25,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.arch.core.util.Function; +import androidx.core.util.Consumer; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.RecyclerView; @@ -58,14 +60,15 @@ public abstract class DbViewModel extends AndroidViewModel { } /** - * Runs the given task on the {@link DatabaseExecutor} - * and waits for the DB to open. + * Waits for the DB to open and runs the given task on the + * {@link DatabaseExecutor}. *

* If you need a list of items to be displayed in a * {@link RecyclerView.Adapter}, * use {@link #loadList(DbCallable, UiConsumer)} instead. */ - protected void runOnDbThread(Runnable task) { + protected void runOnDbThread(ThrowingRunnable task, + Consumer err) { dbExecutor.execute(() -> { try { lifecycleManager.waitForDatabase(); @@ -73,13 +76,52 @@ public abstract class DbViewModel extends AndroidViewModel { } catch (InterruptedException e) { LOG.warning("Interrupted while waiting for database"); Thread.currentThread().interrupt(); + } catch (Exception e) { + err.accept(e); } }); } /** - * Runs the given task on the {@link DatabaseExecutor} - * and waits for the DB to open. If the task throws a {@link DbException} + * Waits for the DB to open and runs the given task on the + * {@link DatabaseExecutor}. + *

+ * If you need a list of items to be displayed in a + * {@link RecyclerView.Adapter}, + * use {@link #loadList(DbCallable, UiConsumer)} instead. + */ + protected void runOnDbThread(boolean readOnly, + DbRunnable task, Consumer err) { + dbExecutor.execute(() -> { + try { + lifecycleManager.waitForDatabase(); + db.transaction(readOnly, task); + } catch (InterruptedException e) { + LOG.warning("Interrupted while waiting for database"); + Thread.currentThread().interrupt(); + } catch (Exception e) { + err.accept(e); + } + }); + } + + /** + * Waits for the DB to open and runs the given task on the + * {@link DatabaseExecutor}. If the task throws a {@link DbException} + * it's caught and logged. + *

+ * If you need a list of items to be displayed in a + * {@link RecyclerView.Adapter}, + * use {@link #loadList(DbCallable, UiConsumer)} instead. + */ + protected void runOnDbThreadOrLogException(boolean readOnly, + DbRunnable task) { + runOnDbThread(readOnly, task, e -> logException(LOG, WARNING, e)); + } + + /** + * Waits for the DB to open and runs the given task on the + * {@link DatabaseExecutor}. If the task throws a {@link DbException} * it's caught and logged. *

* If you need a list of items to be displayed in a @@ -87,18 +129,8 @@ public abstract class DbViewModel extends AndroidViewModel { * use {@link #loadList(DbCallable, UiConsumer)} instead. */ protected void runOnDbThreadOrLogException( - ThrowingRunnable task) { - dbExecutor.execute(() -> { - try { - lifecycleManager.waitForDatabase(); - task.run(); - } catch (InterruptedException e) { - LOG.warning("Interrupted while waiting for database"); - Thread.currentThread().interrupt(); - } catch (DbException e) { - logException(LOG, WARNING, e); - } - }); + ThrowingRunnable task) { + runOnDbThread(task, e -> logException(LOG, WARNING, e)); } /** diff --git a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java index df10ccdf5..df8e0b1e7 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/privategroup/PrivateGroupManager.java @@ -129,6 +129,12 @@ public interface PrivateGroupManager { */ Collection getMembers(GroupId g) throws DbException; + /** + * Returns all members of the given private group. + */ + Collection getMembers(Transaction txn, GroupId g) + throws DbException; + /** * Returns true if the given author is a member of the given private group. */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/sharing/SharingManager.java b/briar-api/src/main/java/org/briarproject/briar/api/sharing/SharingManager.java index 3e6adc2dc..2170744ed 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/sharing/SharingManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/sharing/SharingManager.java @@ -3,6 +3,7 @@ package org.briarproject.briar.api.sharing; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.api.client.SessionId; @@ -45,6 +46,12 @@ public interface SharingManager */ Collection getSharedWith(GroupId g) throws DbException; + /** + * Returns all contacts with whom the given group is shared. + */ + Collection getSharedWith(Transaction txn, GroupId g) + throws DbException; + /** * Returns true if the group not already shared and no invitation is open */ diff --git a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java index ad776aa58..3ece7e3eb 100644 --- a/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/privategroup/PrivateGroupManagerImpl.java @@ -407,36 +407,36 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook } @Override - public Collection getMembers(GroupId g) throws DbException { - Transaction txn = db.startTransaction(true); - try { - Collection members = new ArrayList<>(); - Map authors = getMembers(txn, g); - LocalAuthor la = identityManager.getLocalAuthor(txn); - PrivateGroup privateGroup = getPrivateGroup(txn, g); - for (Entry m : authors.entrySet()) { - Author a = m.getKey(); - AuthorInfo authorInfo = - authorManager.getAuthorInfo(txn, a.getId()); - Status status = authorInfo.getStatus(); - Visibility v = m.getValue(); - ContactId c = null; - if (v != INVISIBLE && - (status == VERIFIED || status == UNVERIFIED)) { - c = contactManager.getContact(txn, a.getId(), la.getId()) - .getId(); - } - boolean isCreator = privateGroup.getCreator().equals(a); - members.add(new GroupMember(a, authorInfo, isCreator, c, v)); - } - db.commitTransaction(txn); - return members; - } finally { - db.endTransaction(txn); - } + public Collection getMembers(GroupId g) + throws DbException { + return db.transactionWithResult(true, txn -> getMembers(txn, g)); } - private Map getMembers(Transaction txn, GroupId g) + @Override + public Collection getMembers(Transaction txn, GroupId g) + throws DbException { + Collection members = new ArrayList<>(); + Map authors = getMemberAuthors(txn, g); + LocalAuthor la = identityManager.getLocalAuthor(txn); + PrivateGroup privateGroup = getPrivateGroup(txn, g); + for (Entry m : authors.entrySet()) { + Author a = m.getKey(); + AuthorInfo authorInfo = authorManager.getAuthorInfo(txn, a.getId()); + Status status = authorInfo.getStatus(); + Visibility v = m.getValue(); + ContactId c = null; + if (v != INVISIBLE && + (status == VERIFIED || status == UNVERIFIED)) { + c = contactManager.getContact(txn, a.getId(), la.getId()) + .getId(); + } + boolean isCreator = privateGroup.getCreator().equals(a); + members.add(new GroupMember(a, authorInfo, isCreator, c, v)); + } + return members; + } + + private Map getMemberAuthors(Transaction txn, GroupId g) throws DbException { try { BdfDictionary meta = @@ -458,7 +458,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook @Override public boolean isMember(Transaction txn, GroupId g, Author a) throws DbException { - for (Author member : getMembers(txn, g).keySet()) { + for (Author member : getMemberAuthors(txn, g).keySet()) { if (member.equals(a)) return true; } return false; diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/SharingManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/sharing/SharingManagerImpl.java index 2b3abbd64..4a24ece90 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/SharingManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/SharingManagerImpl.java @@ -426,17 +426,17 @@ abstract class SharingManagerImpl @Override public Collection getSharedWith(GroupId g) throws DbException { + return db.transactionWithResult(true, txn -> getSharedWith(txn, g)); + } + + @Override + public Collection getSharedWith(Transaction txn, GroupId g) + throws DbException { // TODO report also pending invitations Collection contacts = new ArrayList<>(); - Transaction txn = db.startTransaction(true); - try { - for (Contact c : db.getContacts(txn)) { - if (db.getGroupVisibility(txn, c.getId(), g) == SHARED) - contacts.add(c); - } - db.commitTransaction(txn); - } finally { - db.endTransaction(txn); + for (Contact c : db.getContacts(txn)) { + if (db.getGroupVisibility(txn, c.getId(), g) == SHARED) + contacts.add(c); } return contacts; } diff --git a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java index 9dae9d6b5..68e69043d 100644 --- a/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/privategroup/PrivateGroupManagerIntegrationTest.java @@ -20,10 +20,10 @@ import org.junit.Test; import java.util.Collection; -import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.briarproject.briar.api.identity.AuthorInfo.Status.VERIFIED; import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_CONTACT; import static org.briarproject.briar.api.privategroup.Visibility.REVEALED_BY_US;