diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index 0163f415a..d2c4bac64 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -39,6 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio import org.briarproject.briar.android.settings.SettingsModule; import org.briarproject.briar.android.privategroup.list.GroupListModule; import org.briarproject.briar.android.reporting.DevReportModule; +import org.briarproject.briar.android.sharing.SharingModule; import org.briarproject.briar.android.test.TestAvatarCreatorImpl; import org.briarproject.briar.android.viewmodel.ViewModelModule; import org.briarproject.briar.api.android.AndroidNotificationManager; @@ -80,9 +81,10 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; DevReportModule.class, ContactListModule.class, // below need to be within same scope as ViewModelProvider.Factory - ForumModule.BindsModule.class, + ForumModule.class, GroupListModule.class, - GroupConversationModule.BindsModule.class, + GroupConversationModule.class, + SharingModule.class, }) public class AppModule { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index 3835a7aaa..3b1e91ef9 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -32,7 +32,6 @@ import org.briarproject.briar.android.conversation.ImageFragment; import org.briarproject.briar.android.forum.CreateForumActivity; import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumListFragment; -import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment; import org.briarproject.briar.android.introduction.ContactChooserFragment; import org.briarproject.briar.android.introduction.IntroductionActivity; @@ -50,7 +49,6 @@ import org.briarproject.briar.android.navdrawer.TransportsActivity; import org.briarproject.briar.android.panic.PanicPreferencesActivity; import org.briarproject.briar.android.panic.PanicResponderActivity; import org.briarproject.briar.android.privategroup.conversation.GroupActivity; -import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule; import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity; import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment; import org.briarproject.briar.android.privategroup.creation.CreateGroupModule; @@ -89,12 +87,10 @@ import dagger.Component; ActivityModule.class, BlogModule.class, CreateGroupModule.class, - ForumModule.class, GroupInvitationModule.class, - GroupConversationModule.class, GroupMemberModule.class, GroupRevealModule.class, - SharingModule.class + SharingModule.SharingLegacyModule.class }, dependencies = AndroidComponent.class) public interface ActivityComponent { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java index 8957706f7..baf49a711 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java @@ -160,7 +160,6 @@ public abstract class BriarActivity extends BaseActivity { * @param ownLayout true if the custom toolbar brings its own layout * @return the Toolbar object or null if content view did not contain one */ - @Nullable protected Toolbar setUpCustomToolbar(boolean ownLayout) { // Custom Toolbar Toolbar toolbar = findViewById(R.id.toolbar); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingController.java index 01f885578..3ebe7024f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingController.java @@ -7,6 +7,7 @@ import java.util.Collection; import androidx.annotation.UiThread; +@Deprecated @NotNullByDefault public interface SharingController { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java index 858c94c53..b49eb6aa8 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java @@ -18,6 +18,7 @@ import javax.inject.Inject; import androidx.annotation.UiThread; +@Deprecated @NotNullByDefault public class SharingControllerImpl implements SharingController, EventListener { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumActivity.java index f3e7ecb87..6a204dc23 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumActivity.java @@ -7,17 +7,14 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import org.briarproject.bramble.api.contact.ContactId; 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.forum.ForumController.ForumListener; import org.briarproject.briar.android.sharing.ForumSharingStatusActivity; import org.briarproject.briar.android.sharing.ShareForumActivity; import org.briarproject.briar.android.threaded.ThreadItemAdapter; import org.briarproject.briar.android.threaded.ThreadListActivity; -import org.briarproject.briar.android.threaded.ThreadListController; import org.briarproject.briar.android.threaded.ThreadListViewModel; import javax.annotation.Nullable; @@ -35,13 +32,10 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_TEX @MethodsNotNullByDefault @ParametersNotNullByDefault public class ForumActivity extends - ThreadListActivity> - implements ForumListener { + ThreadListActivity> { @Inject ViewModelProvider.Factory viewModelFactory; - @Inject - ForumController forumController; private ForumViewModel viewModel; @@ -52,11 +46,6 @@ public class ForumActivity extends .get(ForumViewModel.class); } - @Override - protected ThreadListController getController() { - return forumController; - } - @Override protected ThreadListViewModel getViewModel() { return viewModel; @@ -72,9 +61,15 @@ public class ForumActivity extends super.onCreate(state); Toolbar toolbar = setUpCustomToolbar(false); + // Open member list on Toolbar click + toolbar.setOnClickListener(v -> { + Intent i = new Intent(ForumActivity.this, + ForumSharingStatusActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + startActivity(i); + }); - Intent i = getIntent(); - String groupName = i.getStringExtra(GROUP_NAME); + String groupName = getIntent().getStringExtra(GROUP_NAME); if (groupName != null) { setTitle(groupName); } else { @@ -82,16 +77,6 @@ public class ForumActivity extends setTitle(forum.getName()) ); } - - // Open member list on Toolbar click - if (toolbar != null) { - toolbar.setOnClickListener(v -> { - Intent i1 = new Intent(ForumActivity.this, - ForumSharingStatusActivity.class); - i1.putExtra(GROUP_ID, groupId.getBytes()); - startActivity(i1); - }); - } } @Override @@ -115,8 +100,8 @@ public class ForumActivity extends // Inflate the menu items for use in the action bar MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.forum_actions, menu); - - return super.onCreateOptionsMenu(menu); + super.onCreateOptionsMenu(menu); + return true; } @Override @@ -158,11 +143,4 @@ public class ForumActivity extends builder.show(); } - @Override - public void onForumLeft(ContactId c) { - sharingController.remove(c); - setToolbarSubTitle(sharingController.getTotalCount(), - sharingController.getOnlineCount()); - } - } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumController.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumController.java deleted file mode 100644 index 83ec874a3..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumController.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.briarproject.briar.android.forum; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.briar.android.threaded.ThreadListController; - -import androidx.annotation.UiThread; - -@NotNullByDefault -interface ForumController extends ThreadListController { - - interface ForumListener extends ThreadListListener { - @UiThread - void onForumLeft(ContactId c); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java deleted file mode 100644 index 4bfab3d74..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumControllerImpl.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.briarproject.briar.android.forum; - -import org.briarproject.bramble.api.contact.Contact; -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.crypto.CryptoExecutor; -import org.briarproject.bramble.api.db.DatabaseExecutor; -import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.event.Event; -import org.briarproject.bramble.api.event.EventBus; -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.system.Clock; -import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; -import org.briarproject.briar.android.threaded.ThreadListControllerImpl; -import org.briarproject.briar.api.android.AndroidNotificationManager; -import org.briarproject.briar.api.forum.ForumInvitationResponse; -import org.briarproject.briar.api.forum.ForumManager; -import org.briarproject.briar.api.forum.ForumSharingManager; -import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent; -import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import static java.util.logging.Level.WARNING; -import static org.briarproject.bramble.util.LogUtils.logException; - -@NotNullByDefault -class ForumControllerImpl extends ThreadListControllerImpl - implements ForumController { - - private static final Logger LOG = - Logger.getLogger(ForumControllerImpl.class.getName()); - - private final ForumManager forumManager; - private final ForumSharingManager forumSharingManager; - - @Inject - ForumControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, IdentityManager identityManager, - @CryptoExecutor Executor cryptoExecutor, - ForumManager forumManager, ForumSharingManager forumSharingManager, - EventBus eventBus, Clock clock, - AndroidNotificationManager notificationManager) { - super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, - eventBus, clock, notificationManager); - this.forumManager = forumManager; - this.forumSharingManager = forumSharingManager; - } - - @Override - public void onActivityStart() { - super.onActivityStart(); - } - - @Override - public void eventOccurred(Event e) { - super.eventOccurred(e); - - ForumListener listener = (ForumListener) this.listener; - - if (e instanceof ForumInvitationResponseReceivedEvent) { - ForumInvitationResponseReceivedEvent f = - (ForumInvitationResponseReceivedEvent) e; - ForumInvitationResponse r = f.getMessageHeader(); - if (r.getShareableId().equals(getGroupId()) && r.wasAccepted()) { - LOG.info("Forum invitation was accepted"); - listener.onInvitationAccepted(f.getContactId()); - } - } else if (e instanceof ContactLeftShareableEvent) { - ContactLeftShareableEvent c = (ContactLeftShareableEvent) e; - if (c.getGroupId().equals(getGroupId())) { - LOG.info("Forum left by contact"); - listener.onForumLeft(c.getContactId()); - } - } - } - - @Override - public void loadSharingContacts( - ResultExceptionHandler, DbException> handler) { - runOnDbThread(() -> { - try { - Collection contacts = - forumSharingManager.getSharedWith(getGroupId()); - Collection contactIds = - new ArrayList<>(contacts.size()); - for (Contact c : contacts) contactIds.add(c.getId()); - handler.onResult(contactIds); - } catch (DbException e) { - logException(LOG, WARNING, e); - handler.onException(e); - } - }); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumModule.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumModule.java index e2efac2d9..2f2aba2cd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumModule.java @@ -1,37 +1,23 @@ package org.briarproject.briar.android.forum; -import org.briarproject.briar.android.activity.ActivityScope; -import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.viewmodel.ViewModelKey; import androidx.lifecycle.ViewModel; import dagger.Binds; import dagger.Module; -import dagger.Provides; import dagger.multibindings.IntoMap; @Module -public class ForumModule { +public interface ForumModule { - @Module - public interface BindsModule { - @Binds - @IntoMap - @ViewModelKey(ForumListViewModel.class) - ViewModel bindForumListViewModel(ForumListViewModel forumListViewModel); + @Binds + @IntoMap + @ViewModelKey(ForumListViewModel.class) + ViewModel bindForumListViewModel(ForumListViewModel forumListViewModel); - @Binds - @IntoMap - @ViewModelKey(ForumViewModel.class) - ViewModel bindForumViewModel(ForumViewModel forumViewModel); - } - - @ActivityScope - @Provides - ForumController provideForumController(BaseActivity activity, - ForumControllerImpl forumController) { - activity.addLifecycleController(forumController); - return forumController; - } + @Binds + @IntoMap + @ViewModelKey(ForumViewModel.class) + ViewModel bindForumViewModel(ForumViewModel forumViewModel); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java index 7366725de..4973a5d0b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/forum/ForumPostItem.java @@ -6,7 +6,6 @@ import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.briar.android.threaded.ThreadItem; import org.briarproject.briar.api.forum.ForumPostHeader; -import javax.annotation.Nullable; import javax.annotation.concurrent.NotThreadSafe; @NotThreadSafe @@ -17,9 +16,4 @@ class ForumPostItem extends ThreadItem { h.getAuthorInfo(), h.isRead()); } - ForumPostItem(MessageId messageId, @Nullable MessageId parentId, - String text, long timestamp, Author author, AuthorInfo authorInfo) { - super(messageId, parentId, text, timestamp, author, authorInfo, 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 0c1f21aff..48dfb7640 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 @@ -3,6 +3,8 @@ package org.briarproject.briar.android.forum; import android.app.Application; import android.widget.Toast; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; @@ -19,18 +21,24 @@ import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.Clock; import org.briarproject.briar.R; +import org.briarproject.briar.android.sharing.SharingController; import org.briarproject.briar.android.threaded.ThreadListViewModel; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.api.forum.Forum; +import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumPost; import org.briarproject.briar.api.forum.ForumPostHeader; import org.briarproject.briar.api.forum.ForumSharingManager; +import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent; import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent; +import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -66,6 +74,7 @@ class ForumViewModel extends ThreadListViewModel { AndroidExecutor androidExecutor, IdentityManager identityManager, AndroidNotificationManager notificationManager, + SharingController sharingController, @CryptoExecutor Executor cryptoExecutor, Clock clock, MessageTracker messageTracker, @@ -73,8 +82,8 @@ class ForumViewModel extends ThreadListViewModel { ForumManager forumManager, ForumSharingManager forumSharingManager) { super(application, dbExecutor, lifecycleManager, db, androidExecutor, - identityManager, notificationManager, cryptoExecutor, clock, - messageTracker, eventBus); + identityManager, notificationManager, sharingController, + cryptoExecutor, clock, messageTracker, eventBus); this.forumManager = forumManager; this.forumSharingManager = forumSharingManager; } @@ -88,6 +97,20 @@ class ForumViewModel extends ThreadListViewModel { ForumPostItem item = buildItem(f.getHeader(), f.getText()); addItem(item); } + } else if (e instanceof ForumInvitationResponseReceivedEvent) { + ForumInvitationResponseReceivedEvent f = + (ForumInvitationResponseReceivedEvent) e; + ForumInvitationResponse r = f.getMessageHeader(); + if (r.getShareableId().equals(groupId) && r.wasAccepted()) { + LOG.info("Forum invitation was accepted"); + sharingController.add(f.getContactId()); + } + } else if (e instanceof ContactLeftShareableEvent) { + ContactLeftShareableEvent c = (ContactLeftShareableEvent) e; + if (c.getGroupId().equals(groupId)) { + LOG.info("Forum left by contact"); + sharingController.remove(c.getContactId()); + } } else { super.eventOccurred(e); } @@ -123,15 +146,13 @@ class ForumViewModel extends ThreadListViewModel { @Override public void createAndStoreMessage(String text, - @Nullable ForumPostItem parentItem) { + @Nullable MessageId parentId) { runOnDbThread(() -> { try { LocalAuthor author = identityManager.getLocalAuthor(); GroupCount count = forumManager.getGroupCount(groupId); long timestamp = max(count.getLatestMsgTime() + 1, clock.currentTimeMillis()); - MessageId parentId = - parentItem != null ? parentItem.getId() : null; createMessage(text, timestamp, parentId, author); } catch (DbException e) { logException(LOG, WARNING, e); @@ -183,6 +204,21 @@ class ForumViewModel extends ThreadListViewModel { }); } + public void loadSharingContacts() { + runOnDbThread(() -> { + try { + Collection contacts = + forumSharingManager.getSharedWith(groupId); + Collection contactIds = + new ArrayList<>(contacts.size()); + for (Contact c : contacts) contactIds.add(c.getId()); + sharingController.addAll(contactIds); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + void deleteForum() { runOnDbThread(() -> { try { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java index 799a0c5b1..a5dac9f34 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupActivity.java @@ -6,20 +6,15 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.identity.AuthorId; 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.privategroup.conversation.GroupController.GroupListener; import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity; import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity; import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity; import org.briarproject.briar.android.threaded.ThreadListActivity; -import org.briarproject.briar.android.threaded.ThreadListController; import org.briarproject.briar.android.threaded.ThreadListViewModel; -import org.briarproject.briar.api.privategroup.Visibility; import javax.annotation.Nullable; import javax.inject.Inject; @@ -37,19 +32,13 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_ @MethodsNotNullByDefault @ParametersNotNullByDefault public class GroupActivity extends - ThreadListActivity - implements GroupListener { + ThreadListActivity { @Inject ViewModelProvider.Factory viewModelFactory; - @Inject - GroupController controller; private GroupViewModel viewModel; - private MenuItem revealMenuItem, inviteMenuItem, leaveMenuItem, - dissolveMenuItem; - @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -57,11 +46,6 @@ public class GroupActivity extends .get(GroupViewModel.class); } - @Override - protected ThreadListController getController() { - return controller; - } - @Override protected ThreadListViewModel getViewModel() { return viewModel; @@ -77,25 +61,21 @@ public class GroupActivity extends super.onCreate(state); Toolbar toolbar = setUpCustomToolbar(false); + // Open member list on Toolbar click + toolbar.setOnClickListener(v -> { + Intent i = new Intent(GroupActivity.this, + GroupMemberListActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + startActivity(i); + }); - Intent i = getIntent(); - String groupName = i.getStringExtra(GROUP_NAME); + String groupName = getIntent().getStringExtra(GROUP_NAME); if (groupName != null) setTitle(groupName); observeOnce(viewModel.getPrivateGroup(), this, privateGroup -> setTitle(privateGroup.getName()) ); observeOnce(viewModel.isCreator(), this, adapter::setIsCreator); - // Open member list on Toolbar click - if (toolbar != null) { - toolbar.setOnClickListener(v -> { - Intent i1 = new Intent(GroupActivity.this, - GroupMemberListActivity.class); - i1.putExtra(GROUP_ID, groupId.getBytes()); - startActivity(i1); - }); - } - // start with group disabled and enable when not dissolved setGroupEnabled(false); viewModel.isDissolved().observe(this, dissolved -> { @@ -116,49 +96,38 @@ public class GroupActivity extends MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.group_actions, menu); - revealMenuItem = menu.findItem(R.id.action_group_reveal); - inviteMenuItem = menu.findItem(R.id.action_group_invite); - leaveMenuItem = menu.findItem(R.id.action_group_leave); - dissolveMenuItem = menu.findItem(R.id.action_group_dissolve); - - // all role-dependent items are invisible until we know our role - revealMenuItem.setVisible(false); - inviteMenuItem.setVisible(false); - leaveMenuItem.setVisible(false); - dissolveMenuItem.setVisible(false); - // show items based on role (which will not change, so observe once) observeOnce(viewModel.isCreator(), this, isCreator -> { - revealMenuItem.setVisible(!isCreator); - inviteMenuItem.setVisible(isCreator); - leaveMenuItem.setVisible(!isCreator); - dissolveMenuItem.setVisible(isCreator); + menu.findItem(R.id.action_group_reveal).setVisible(!isCreator); + menu.findItem(R.id.action_group_invite).setVisible(isCreator); + menu.findItem(R.id.action_group_leave).setVisible(!isCreator); + menu.findItem(R.id.action_group_dissolve).setVisible(isCreator); }); - - return super.onCreateOptionsMenu(menu); + super.onCreateOptionsMenu(menu); + return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.action_group_member_list) { - Intent i1 = new Intent(this, GroupMemberListActivity.class); - i1.putExtra(GROUP_ID, groupId.getBytes()); - startActivity(i1); + Intent i = new Intent(this, GroupMemberListActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + startActivity(i); return true; } else if (itemId == R.id.action_group_reveal) { if (viewModel.isCreator().getValue()) throw new IllegalStateException(); - Intent i2 = new Intent(this, RevealContactsActivity.class); - i2.putExtra(GROUP_ID, groupId.getBytes()); - startActivity(i2); + Intent i = new Intent(this, RevealContactsActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + startActivity(i); return true; } else if (itemId == R.id.action_group_invite) { if (!viewModel.isCreator().getValue()) throw new IllegalStateException(); - Intent i3 = new Intent(this, GroupInviteActivity.class); - i3.putExtra(GROUP_ID, groupId.getBytes()); - startActivityForResult(i3, REQUEST_GROUP_INVITE); + Intent i = new Intent(this, GroupInviteActivity.class); + i.putExtra(GROUP_ID, groupId.getBytes()); + startActivityForResult(i, REQUEST_GROUP_INVITE); return true; } else if (itemId == R.id.action_group_leave) { if (viewModel.isCreator().getValue()) @@ -169,8 +138,7 @@ public class GroupActivity extends if (!viewModel.isCreator().getValue()) throw new IllegalStateException(); showDissolveGroupDialog(); - - return super.onOptionsItemSelected(item); + return true; } return super.onOptionsItemSelected(item); } @@ -233,18 +201,7 @@ public class GroupActivity extends viewModel.deletePrivateGroup(); } - @Override - public void onContactRelationshipRevealed(AuthorId memberId, ContactId c, - Visibility v) { - adapter.updateVisibility(memberId, v); - - sharingController.add(c); - setToolbarSubTitle(sharingController.getTotalCount(), - sharingController.getOnlineCount()); - } - public void onGroupDissolved() { - setGroupEnabled(false); AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.BriarDialogTheme); builder.setTitle(getString(R.string.groups_dissolved_dialog_title)); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupController.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupController.java deleted file mode 100644 index 210360ade..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupController.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.briarproject.briar.android.privategroup.conversation; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.identity.AuthorId; -import org.briarproject.briar.android.threaded.ThreadListController; -import org.briarproject.briar.api.privategroup.Visibility; - -import androidx.annotation.UiThread; - -public interface GroupController - extends ThreadListController { - - interface GroupListener extends ThreadListListener { - @UiThread - void onContactRelationshipRevealed(AuthorId memberId, - ContactId contactId, Visibility v); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java deleted file mode 100644 index 275ee3586..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupControllerImpl.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.briarproject.briar.android.privategroup.conversation; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.crypto.CryptoExecutor; -import org.briarproject.bramble.api.db.DatabaseExecutor; -import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.event.Event; -import org.briarproject.bramble.api.event.EventBus; -import org.briarproject.bramble.api.identity.IdentityManager; -import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.system.Clock; -import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; -import org.briarproject.briar.android.threaded.ThreadListControllerImpl; -import org.briarproject.briar.api.android.AndroidNotificationManager; -import org.briarproject.briar.api.privategroup.GroupMember; -import org.briarproject.briar.api.privategroup.PrivateGroupManager; -import org.briarproject.briar.api.privategroup.event.ContactRelationshipRevealedEvent; -import org.briarproject.briar.api.privategroup.event.GroupInvitationResponseReceivedEvent; -import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.Executor; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import static java.util.logging.Level.WARNING; -import static java.util.logging.Logger.getLogger; -import static org.briarproject.bramble.util.LogUtils.logException; - -@MethodsNotNullByDefault -@ParametersNotNullByDefault -class GroupControllerImpl extends ThreadListControllerImpl - implements GroupController { - - private static final Logger LOG = - getLogger(GroupControllerImpl.class.getName()); - - private final PrivateGroupManager privateGroupManager; - - @Inject - GroupControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, IdentityManager identityManager, - @CryptoExecutor Executor cryptoExecutor, - PrivateGroupManager privateGroupManager, - EventBus eventBus, Clock clock, - AndroidNotificationManager notificationManager) { - super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, - eventBus, clock, notificationManager); - this.privateGroupManager = privateGroupManager; - } - - @Override - public void onActivityStart() { - super.onActivityStart(); - } - - @Override - public void eventOccurred(Event e) { - super.eventOccurred(e); - - GroupListener listener = (GroupListener) this.listener; - - if (e instanceof ContactRelationshipRevealedEvent) { - ContactRelationshipRevealedEvent c = - (ContactRelationshipRevealedEvent) e; - if (getGroupId().equals(c.getGroupId())) { - listener.onContactRelationshipRevealed(c.getMemberId(), - c.getContactId(), c.getVisibility()); - } - } else if (e instanceof GroupInvitationResponseReceivedEvent) { - GroupInvitationResponseReceivedEvent g = - (GroupInvitationResponseReceivedEvent) e; - GroupInvitationResponse r = g.getMessageHeader(); - if (getGroupId().equals(r.getShareableId()) && r.wasAccepted()) { - listener.onInvitationAccepted(g.getContactId()); - } - } - } - - @Override - public void loadSharingContacts( - ResultExceptionHandler, DbException> handler) { - runOnDbThread(() -> { - try { - Collection members = - privateGroupManager.getMembers(getGroupId()); - Collection contactIds = new ArrayList<>(); - for (GroupMember m : members) { - if (m.getContactId() != null) - contactIds.add(m.getContactId()); - } - handler.onResult(contactIds); - } catch (DbException e) { - logException(LOG, WARNING, e); - handler.onException(e); - } - }); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupConversationModule.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupConversationModule.java index 6dd2c65ab..88e43cf59 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupConversationModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupConversationModule.java @@ -1,31 +1,18 @@ package org.briarproject.briar.android.privategroup.conversation; -import org.briarproject.briar.android.activity.ActivityScope; -import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.viewmodel.ViewModelKey; import androidx.lifecycle.ViewModel; import dagger.Binds; import dagger.Module; -import dagger.Provides; import dagger.multibindings.IntoMap; @Module -public class GroupConversationModule { +public interface GroupConversationModule { - @Module - public interface BindsModule { - @Binds - @IntoMap - @ViewModelKey(GroupViewModel.class) - ViewModel bindGroupViewModel(GroupViewModel groupViewModel); - } + @Binds + @IntoMap + @ViewModelKey(GroupViewModel.class) + ViewModel bindGroupViewModel(GroupViewModel groupViewModel); - @ActivityScope - @Provides - GroupController provideGroupController(BaseActivity activity, - GroupControllerImpl groupController) { - activity.addLifecycleController(groupController); - return groupController; - } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageAdapter.java index 4d88a22ea..4eedbd580 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/conversation/GroupMessageAdapter.java @@ -4,19 +4,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder; import org.briarproject.briar.android.threaded.ThreadItemAdapter; import org.briarproject.briar.android.threaded.ThreadPostViewHolder; -import org.briarproject.briar.api.privategroup.Visibility; import androidx.annotation.LayoutRes; import androidx.annotation.UiThread; -import static androidx.recyclerview.widget.RecyclerView.NO_POSITION; - @UiThread @NotNullByDefault class GroupMessageAdapter extends ThreadItemAdapter { @@ -50,25 +46,4 @@ class GroupMessageAdapter extends ThreadItemAdapter { notifyDataSetChanged(); } - void updateVisibility(AuthorId memberId, Visibility v) { - int position = findItemPosition(memberId); - if (position != NO_POSITION) { - GroupMessageItem item = getItem(position); - if (item instanceof JoinMessageItem) { - ((JoinMessageItem) item).setVisibility(v); - notifyItemChanged(findItemPosition(item.getId()), item); - } - } - } - - @Deprecated - private int findItemPosition(AuthorId a) { - for (int i = 0; i < getItemCount(); i++) { - GroupMessageItem item = getItem(i); - if (item.getAuthor().getId().equals(a)) - return i; - } - return NO_POSITION; // Not found - } - } 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 e79e862d7..bf7b574b6 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 @@ -2,6 +2,7 @@ package org.briarproject.briar.android.privategroup.conversation; import android.app.Application; +import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; @@ -19,20 +20,27 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.android.sharing.SharingController; import org.briarproject.briar.android.threaded.ThreadListViewModel; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.PostHeader; +import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageHeader; import org.briarproject.briar.api.privategroup.JoinMessageHeader; import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroupManager; +import org.briarproject.briar.api.privategroup.event.ContactRelationshipRevealedEvent; import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent; +import org.briarproject.briar.api.privategroup.event.GroupInvitationResponseReceivedEvent; import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent; +import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -74,14 +82,15 @@ class GroupViewModel extends ThreadListViewModel { EventBus eventBus, IdentityManager identityManager, AndroidNotificationManager notificationManager, + SharingController sharingController, @CryptoExecutor Executor cryptoExecutor, Clock clock, MessageTracker messageTracker, PrivateGroupManager privateGroupManager, GroupMessageFactory groupMessageFactory) { super(application, dbExecutor, lifecycleManager, db, androidExecutor, - identityManager, notificationManager, cryptoExecutor, clock, - messageTracker, eventBus); + identityManager, notificationManager, sharingController, + cryptoExecutor, clock, messageTracker, eventBus); this.privateGroupManager = privateGroupManager; this.groupMessageFactory = groupMessageFactory; } @@ -95,11 +104,24 @@ class GroupViewModel extends ThreadListViewModel { LOG.info("Group message received, adding..."); GroupMessageItem item = buildItem(g.getHeader(), g.getText()); addItem(item); - if (item instanceof JoinMessageItem) { - // TODO -// if (((JoinMessageItem) item).isInitial()) loadSharingContacts(); + if (item instanceof JoinMessageItem && + (((JoinMessageItem) item).isInitial())) { + loadSharingContacts(); } } + } else if (e instanceof GroupInvitationResponseReceivedEvent) { + GroupInvitationResponseReceivedEvent g = + (GroupInvitationResponseReceivedEvent) e; + GroupInvitationResponse r = g.getMessageHeader(); + if (r.getShareableId().equals(groupId) && r.wasAccepted()) { + sharingController.add(g.getContactId()); + } + } else if (e instanceof ContactRelationshipRevealedEvent) { + ContactRelationshipRevealedEvent c = + (ContactRelationshipRevealedEvent) e; + if (c.getGroupId().equals(groupId)) { + sharingController.add(c.getContactId()); + } } else if (e instanceof GroupDissolvedEvent) { GroupDissolvedEvent g = (GroupDissolvedEvent) e; if (g.getGroupId().equals(groupId)) { @@ -139,7 +161,7 @@ class GroupViewModel extends ThreadListViewModel { // check first if group is dissolved isDissolved .postValue(privateGroupManager.isDissolved(txn, groupId)); - // no continue to load the items + // now continue to load the items long start = now(); List headers = privateGroupManager.getHeaders(txn, groupId); @@ -167,16 +189,14 @@ class GroupViewModel extends ThreadListViewModel { @Override public void createAndStoreMessage(String text, - @Nullable GroupMessageItem parentItem) { + @Nullable MessageId parentId) { runOnDbThread(() -> { try { LocalAuthor author = identityManager.getLocalAuthor(); - MessageId parentId = null; MessageId previousMsgId = privateGroupManager.getPreviousMsgId(groupId); GroupCount count = privateGroupManager.getGroupCount(groupId); long timestamp = count.getLatestMsgTime(); - if (parentItem != null) parentId = parentItem.getId(); timestamp = max(clock.currentTimeMillis(), timestamp + 1); createMessage(text, timestamp, parentId, author, previousMsgId); } catch (DbException e) { @@ -221,6 +241,23 @@ class GroupViewModel extends ThreadListViewModel { }); } + public void loadSharingContacts() { + runOnDbThread(() -> { + try { + Collection members = + privateGroupManager.getMembers(groupId); + Collection contactIds = new ArrayList<>(); + for (GroupMember m : members) { + if (m.getContactId() != null) + contactIds.add(m.getContactId()); + } + sharingController.addAll(contactIds); + } catch (DbException e) { + logException(LOG, WARNING, e); + } + }); + } + void deletePrivateGroup() { runOnDbThread(() -> { try { 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 new file mode 100644 index 000000000..9a175534e --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingController.java @@ -0,0 +1,55 @@ +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; + +import java.util.Collection; + +import androidx.annotation.UiThread; +import androidx.lifecycle.LiveData; + +@NotNullByDefault +public interface SharingController { + + /** + * Call this when the owning ViewModel gets cleared, + * so the {@link EventBus} can get unregistered. + */ + void onCleared(); + + /** + * Adds one contact to be tracked. + */ + @UiThread + void add(ContactId c); + + /** + * Adds a collection of contacts to be tracked. + */ + @DatabaseExecutor + void addAll(Collection contacts); + + /** + * Call this when the contact identified by c is no longer sharing + * the given group identified by GroupId g. + */ + @UiThread + void remove(ContactId c); + + /** + * Returns the total number of contacts that have been added. + */ + LiveData getSharingInfo(); + + class SharingInfo { + public final int total, online; + + SharingInfo(int total, int online) { + this.total = total; + this.online = online; + } + } + +} 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 new file mode 100644 index 000000000..55a4149a8 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingControllerImpl.java @@ -0,0 +1,109 @@ +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; +import java.util.Set; + +import javax.inject.Inject; + +import androidx.annotation.UiThread; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +@NotNullByDefault +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<>(); + private final MutableLiveData sharingInfo = + new MutableLiveData<>(); + + @Inject + SharingControllerImpl(EventBus eventBus, + ConnectionRegistry connectionRegistry, + AndroidExecutor executor) { + this.eventBus = eventBus; + this.connectionRegistry = connectionRegistry; + this.executor = executor; + eventBus.addListener(this); + } + + @Override + public void onCleared() { + eventBus.removeListener(this); + } + + @Override + public void eventOccurred(Event e) { + if (e instanceof ContactConnectedEvent) { + setConnected(((ContactConnectedEvent) e).getContactId()); + } else if (e instanceof ContactDisconnectedEvent) { + setConnected(((ContactDisconnectedEvent) e).getContactId()); + } + } + + @UiThread + private void setConnected(ContactId c) { + if (contacts.contains(c)) { + updateLiveData(); + } + } + + @UiThread + private void updateLiveData() { + int online = getOnlineCount(); + sharingInfo.setValue(new SharingInfo(contacts.size(), online)); + } + + private int getOnlineCount() { + int online = 0; + for (ContactId c : contacts) { + if (connectionRegistry.isConnected(c)) online++; + } + return online; + } + + @Override + @DatabaseExecutor + public void addAll(Collection c) { + executor.runOnUiThread(() -> { + contacts.addAll(c); + updateLiveData(); + }); + } + + @UiThread + @Override + public void add(ContactId c) { + contacts.add(c); + updateLiveData(); + } + + @UiThread + @Override + public void remove(ContactId c) { + contacts.remove(c); + updateLiveData(); + } + + @Override + public LiveData getSharingInfo() { + return sharingInfo; + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingModule.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingModule.java index 50bcceb2b..137aa28e4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingModule.java @@ -9,36 +9,47 @@ import dagger.Provides; @Module public class SharingModule { - @ActivityScope - @Provides - ShareForumController provideShareForumController( - ShareForumControllerImpl shareForumController) { - return shareForumController; + @Module + @Deprecated + public static class SharingLegacyModule { + + @ActivityScope + @Provides + ShareForumController provideShareForumController( + ShareForumControllerImpl shareForumController) { + return shareForumController; + } + + @ActivityScope + @Provides + BlogInvitationController provideInvitationBlogController( + BaseActivity activity, + BlogInvitationControllerImpl blogInvitationController) { + activity.addLifecycleController(blogInvitationController); + return blogInvitationController; + } + + @ActivityScope + @Provides + ForumInvitationController provideInvitationForumController( + BaseActivity activity, + ForumInvitationControllerImpl forumInvitationController) { + activity.addLifecycleController(forumInvitationController); + return forumInvitationController; + } + + @ActivityScope + @Provides + ShareBlogController provideShareBlogController( + ShareBlogControllerImpl shareBlogController) { + return shareBlogController; + } } - @ActivityScope @Provides - BlogInvitationController provideInvitationBlogController( - BaseActivity activity, - BlogInvitationControllerImpl blogInvitationController) { - activity.addLifecycleController(blogInvitationController); - return blogInvitationController; - } - - @ActivityScope - @Provides - ForumInvitationController provideInvitationForumController( - BaseActivity activity, - ForumInvitationControllerImpl forumInvitationController) { - activity.addLifecycleController(forumInvitationController); - return forumInvitationController; - } - - @ActivityScope - @Provides - ShareBlogController provideShareBlogController( - ShareBlogControllerImpl shareBlogController) { - return shareBlogController; + SharingController provideSharingController( + SharingControllerImpl sharingController) { + return sharingController; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemList.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemList.java deleted file mode 100644 index f517e8e9c..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemList.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.briarproject.briar.android.threaded; - -import org.briarproject.bramble.api.sync.MessageId; - -import java.util.List; - -import javax.annotation.Nullable; - -public interface ThreadItemList extends List { - - @Nullable - MessageId getFirstVisibleItemId(); - - void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId); -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemListImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemListImpl.java deleted file mode 100644 index bfd63fa5b..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadItemListImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.briarproject.briar.android.threaded; - -import org.briarproject.bramble.api.sync.MessageId; - -import java.util.ArrayList; - -import javax.annotation.Nullable; - -public class ThreadItemListImpl extends ArrayList - implements ThreadItemList { - - private MessageId bottomVisibleItemId; - - @Override - public MessageId getFirstVisibleItemId() { - return bottomVisibleItemId; - } - - @Override - public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) { - this.bottomVisibleItemId = bottomVisibleItemId; - } -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java index 516eec20c..de23e0f2a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListActivity.java @@ -6,19 +6,14 @@ import android.view.MenuItem; import com.google.android.material.snackbar.Snackbar; -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.BriarActivity; -import org.briarproject.briar.android.controller.SharingController; -import org.briarproject.briar.android.controller.SharingController.SharingListener; -import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; +import org.briarproject.briar.android.sharing.SharingController.SharingInfo; import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener; -import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener; import org.briarproject.briar.android.util.BriarSnackbarBuilder; import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.TextInputView; @@ -27,11 +22,9 @@ import org.briarproject.briar.android.view.TextSendController.SendListener; import org.briarproject.briar.android.view.UnreadMessageButton; import org.briarproject.briar.api.attachment.AttachmentHeader; -import java.util.Collection; import java.util.List; import javax.annotation.Nullable; -import javax.inject.Inject; import androidx.annotation.CallSuper; import androidx.annotation.StringRes; @@ -44,26 +37,18 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @MethodsNotNullByDefault @ParametersNotNullByDefault public abstract class ThreadListActivity> - extends BriarActivity - implements ThreadListListener, SendListener, SharingListener, - ThreadItemListener { + extends BriarActivity implements SendListener, ThreadItemListener { protected final A adapter = createAdapter(); - private ThreadScrollListener scrollListener; + protected abstract ThreadListViewModel getViewModel(); + protected abstract A createAdapter(); protected BriarRecyclerView list; - private LinearLayoutManager layoutManager; protected TextInputView textInput; protected TextSendController sendController; protected GroupId groupId; - protected abstract ThreadListController getController(); - - protected abstract ThreadListViewModel getViewModel(); - - protected abstract A createAdapter(); - - @Inject - protected SharingController sharingController; + private LinearLayoutManager layoutManager; + private ThreadScrollListener scrollListener; @CallSuper @Override @@ -76,8 +61,8 @@ public abstract class ThreadListActivity viewModel = getViewModel(); + viewModel.setGroupId(groupId); textInput = findViewById(R.id.text_input_container); sendController = new TextSendController(textInput, this, false); @@ -92,7 +77,7 @@ public abstract class ThreadListActivity(adapter, getViewModel(), + scrollListener = new ThreadScrollListener<>(adapter, viewModel, upButton, downButton); list.getRecyclerView().addOnScrollListener(scrollListener); @@ -109,17 +94,16 @@ public abstract class ThreadListActivity result + viewModel.getItems().observe(this, result -> result .onError(this::handleException) .onSuccess(this::displayItems) ); - getViewModel().getGroupRemoved().observe(this, removed -> { + viewModel.getSharingInfo().observe(this, this::setToolbarSubTitle); + + viewModel.getGroupRemoved().observe(this, removed -> { if (removed) supportFinishAfterTransition(); }); - - sharingController.setSharingListener(this); - loadSharingContacts(); } @CallSuper @@ -127,7 +111,6 @@ public abstract class ThreadListActivity { + // do stuff *after* list had been updated + scrollAfterListCommitted(); + updateTextInput(); + }); } } @@ -197,24 +182,6 @@ public abstract class ThreadListActivity, DbException>( - this) { - @Override - public void onResultUi(Collection contacts) { - sharingController.addAll(contacts); - int online = sharingController.getOnlineCount(); - setToolbarSubTitle(contacts.size(), online); - } - - @Override - public void onExceptionUi(DbException exception) { - handleException(exception); - } - }); - } - @Override public void onReplyClick(I item) { getViewModel().setReplyId(item.getId()); @@ -231,23 +198,11 @@ public abstract class ThreadListActivity headers) { if (isNullOrEmpty(text)) throw new AssertionError(); - I replyItem = adapter.getHighlightedItem(); - getViewModel().createAndStoreMessage(text, replyItem); + MessageId replyId = getViewModel().getReplyId(); + getViewModel().createAndStoreMessage(text, replyId); textInput.hideSoftKeyboard(); textInput.clearText(); getViewModel().setReplyId(null); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListController.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListController.java deleted file mode 100644 index 211182d3c..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListController.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.briarproject.briar.android.threaded; - -import org.briarproject.bramble.api.contact.ContactId; -import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.api.sync.GroupId; -import org.briarproject.briar.android.controller.ActivityLifecycleController; -import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; - -import java.util.Collection; - -import androidx.annotation.UiThread; - -@NotNullByDefault -public interface ThreadListController - extends ActivityLifecycleController { - - void setGroupId(GroupId groupId); - - void loadSharingContacts( - ResultExceptionHandler, DbException> handler); - - interface ThreadListListener { - @UiThread - void onInvitationAccepted(ContactId c); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java deleted file mode 100644 index b96eed677..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListControllerImpl.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.briarproject.briar.android.threaded; - -import android.app.Activity; - -import org.briarproject.bramble.api.crypto.CryptoExecutor; -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.identity.IdentityManager; -import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; -import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; -import org.briarproject.bramble.api.sync.GroupId; -import org.briarproject.bramble.api.system.Clock; -import org.briarproject.briar.android.controller.DbControllerImpl; -import org.briarproject.briar.api.android.AndroidNotificationManager; - -import java.util.concurrent.Executor; - -import androidx.annotation.CallSuper; - -@MethodsNotNullByDefault -@ParametersNotNullByDefault -public abstract class ThreadListControllerImpl - extends DbControllerImpl - implements ThreadListController, EventListener { - - private volatile GroupId groupId; - - private final EventBus eventBus; - protected final IdentityManager identityManager; - protected final AndroidNotificationManager notificationManager; - protected final Executor cryptoExecutor; - protected final Clock clock; - - // UI thread - protected ThreadListListener listener; - - protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor, - LifecycleManager lifecycleManager, IdentityManager identityManager, - @CryptoExecutor Executor cryptoExecutor, EventBus eventBus, - Clock clock, AndroidNotificationManager notificationManager) { - super(dbExecutor, lifecycleManager); - this.identityManager = identityManager; - this.cryptoExecutor = cryptoExecutor; - this.notificationManager = notificationManager; - this.clock = clock; - this.eventBus = eventBus; - } - - @Override - public void setGroupId(GroupId groupId) { - this.groupId = groupId; - } - - @CallSuper - @SuppressWarnings("unchecked") - @Override - public void onActivityCreate(Activity activity) { - listener = (ThreadListListener) activity; - } - - @CallSuper - @Override - public void onActivityStart() { - eventBus.addListener(this); - } - - @CallSuper - @Override - public void onActivityStop() { - eventBus.removeListener(this); - } - - @Override - public void onActivityDestroy() { - - } - - @Override - public void eventOccurred(Event e) { - } - - protected GroupId getGroupId() { - checkGroupId(); - return groupId; - } - - private void checkGroupId() { - if (groupId == null) throw new IllegalStateException(); - } - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListViewModel.java index ea7aac199..422f0a564 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/threaded/ThreadListViewModel.java @@ -19,6 +19,8 @@ import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.android.sharing.SharingController; +import org.briarproject.briar.android.sharing.SharingController.SharingInfo; import org.briarproject.briar.android.viewmodel.DbViewModel; import org.briarproject.briar.android.viewmodel.LiveResult; import org.briarproject.briar.api.android.AndroidNotificationManager; @@ -27,6 +29,7 @@ import org.briarproject.briar.api.client.MessageTree; import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.client.MessageTreeImpl; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.Executor; @@ -58,6 +61,7 @@ public abstract class ThreadListViewModel protected final IdentityManager identityManager; protected final AndroidNotificationManager notificationManager; + protected final SharingController sharingController; protected final Executor cryptoExecutor; protected final Clock clock; private final MessageTracker messageTracker; @@ -85,6 +89,7 @@ public abstract class ThreadListViewModel AndroidExecutor androidExecutor, IdentityManager identityManager, AndroidNotificationManager notificationManager, + SharingController sharingController, @CryptoExecutor Executor cryptoExecutor, Clock clock, MessageTracker messageTracker, @@ -94,6 +99,7 @@ public abstract class ThreadListViewModel this.notificationManager = notificationManager; this.cryptoExecutor = cryptoExecutor; this.clock = clock; + this.sharingController = sharingController; this.messageTracker = messageTracker; this.eventBus = eventBus; this.eventBus.addListener(this); @@ -103,17 +109,19 @@ public abstract class ThreadListViewModel protected void onCleared() { super.onCleared(); eventBus.removeListener(this); + sharingController.onCleared(); } /** * Needs to be called right after initialization, - * before calling other methods. + * before calling any other methods. */ @CallSuper public void setGroupId(GroupId groupId) { this.groupId = groupId; loadStoredMessageId(); loadItems(); + loadSharingContacts(); } public void blockNotifications() { @@ -154,7 +162,13 @@ public abstract class ThreadListViewModel public abstract void loadItems(); public abstract void createAndStoreMessage(String text, - @Nullable I parentItem); + @Nullable MessageId parentMessageId); + + /** + * Loads the ContactIds of all contacts the group is shared with + * and adds them to {@link SharingController}. + */ + public abstract void loadSharingContacts(); @UiThread protected void setItems(LiveResult> items) { @@ -166,7 +180,7 @@ public abstract class ThreadListViewModel Transaction txn, Collection headers, ItemGetter itemGetter) throws DbException { long start = now(); - ThreadItemList items = new ThreadItemListImpl<>(); + List items = new ArrayList<>(); for (H header : headers) { String text = loadMessageText(txn, header); items.add(itemGetter.getItem(header, text)); @@ -235,6 +249,10 @@ public abstract class ThreadListViewModel return items; } + LiveData getSharingInfo() { + return sharingController.getSharingInfo(); + } + LiveData getGroupRemoved() { return groupRemoved; } diff --git a/briar-android/src/main/res/menu/group_actions.xml b/briar-android/src/main/res/menu/group_actions.xml index 000e6e38b..b5bed53b6 100644 --- a/briar-android/src/main/res/menu/group_actions.xml +++ b/briar-android/src/main/res/menu/group_actions.xml @@ -1,36 +1,44 @@ - + + android:visible="false" + app:showAsAction="ifRoom" + tools:visible="true" /> + app:showAsAction="never" /> + android:visible="false" + app:showAsAction="never" + tools:visible="true" /> + android:visible="false" + app:showAsAction="never" + tools:visible="true" /> + android:visible="false" + app:showAsAction="never" + tools:visible="true" /> \ No newline at end of file