From d232529eb3873cdfa015848cfa1eea72333ede12 Mon Sep 17 00:00:00 2001 From: Torsten Grote <t@grobox.de> Date: Thu, 10 Nov 2016 13:29:19 -0200 Subject: [PATCH] Add a controller for contact selection lists --- .../android/ActivityComponent.java | 2 +- .../briarproject/android/ActivityModule.java | 18 ++ .../contact/BaseContactListAdapter.java | 6 +- .../android/contact/ContactItem.java | 13 +- .../android/contact/ContactListAdapter.java | 2 +- .../android/contact/ContactListItem.java | 13 +- .../BaseContactSelectorAdapter.java | 30 +++ .../BaseContactSelectorFragment.java | 138 +++++++++++ .../ContactSelectorActivity.java | 20 +- .../ContactSelectorAdapter.java | 26 ++ .../ContactSelectorController.java | 19 ++ .../ContactSelectorControllerImpl.java | 72 ++++++ .../ContactSelectorFragment.java | 89 +++++++ .../ContactSelectorListener.java | 18 ++ .../SelectableContactHolder.java | 4 +- .../SelectableContactItem.java | 10 +- .../creation/BaseGroupInviteActivity.java | 35 +-- .../creation/CreateGroupActivity.java | 11 +- .../creation/CreateGroupController.java | 8 +- .../creation/CreateGroupControllerImpl.java | 25 +- .../creation/GroupInviteActivity.java | 19 +- .../sharing/ContactSelectorAdapter.java | 41 --- .../sharing/ContactSelectorFragment.java | 233 ------------------ .../sharing/ContactSelectorListener.java | 28 --- .../android/sharing/ShareActivity.java | 61 +---- .../android/sharing/ShareBlogActivity.java | 46 ++-- .../android/sharing/ShareBlogController.java | 18 ++ .../sharing/ShareBlogControllerImpl.java | 74 ++++++ .../android/sharing/ShareForumActivity.java | 47 ++-- .../android/sharing/ShareForumController.java | 18 ++ .../sharing/ShareForumControllerImpl.java | 74 ++++++ .../sharing/SharingStatusActivity.java | 15 +- 32 files changed, 745 insertions(+), 488 deletions(-) create mode 100644 briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorAdapter.java create mode 100644 briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorFragment.java rename briar-android/src/org/briarproject/android/{sharing => contactselection}/ContactSelectorActivity.java (72%) create mode 100644 briar-android/src/org/briarproject/android/contactselection/ContactSelectorAdapter.java create mode 100644 briar-android/src/org/briarproject/android/contactselection/ContactSelectorController.java create mode 100644 briar-android/src/org/briarproject/android/contactselection/ContactSelectorControllerImpl.java create mode 100644 briar-android/src/org/briarproject/android/contactselection/ContactSelectorFragment.java create mode 100644 briar-android/src/org/briarproject/android/contactselection/ContactSelectorListener.java rename briar-android/src/org/briarproject/android/{sharing => contactselection}/SelectableContactHolder.java (94%) rename briar-android/src/org/briarproject/android/{sharing => contactselection}/SelectableContactItem.java (68%) delete mode 100644 briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java delete mode 100644 briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java delete mode 100644 briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareBlogController.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareBlogControllerImpl.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareForumController.java create mode 100644 briar-android/src/org/briarproject/android/sharing/ShareForumControllerImpl.java diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java index fa2e845d58..e350683529 100644 --- a/briar-android/src/org/briarproject/android/ActivityComponent.java +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -38,7 +38,7 @@ import org.briarproject.android.privategroup.list.GroupListFragment; import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity; import org.briarproject.android.sharing.BlogInvitationActivity; import org.briarproject.android.sharing.BlogSharingStatusActivity; -import org.briarproject.android.sharing.ContactSelectorFragment; +import org.briarproject.android.contactselection.ContactSelectorFragment; import org.briarproject.android.sharing.ForumInvitationActivity; import org.briarproject.android.sharing.ForumSharingStatusActivity; import org.briarproject.android.sharing.ShareBlogActivity; diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java index 931b54bac1..1c2db12176 100644 --- a/briar-android/src/org/briarproject/android/ActivityModule.java +++ b/briar-android/src/org/briarproject/android/ActivityModule.java @@ -35,6 +35,10 @@ import org.briarproject.android.sharing.BlogInvitationController; import org.briarproject.android.sharing.BlogInvitationControllerImpl; import org.briarproject.android.sharing.ForumInvitationController; import org.briarproject.android.sharing.ForumInvitationControllerImpl; +import org.briarproject.android.sharing.ShareBlogController; +import org.briarproject.android.sharing.ShareBlogControllerImpl; +import org.briarproject.android.sharing.ShareForumController; +import org.briarproject.android.sharing.ShareForumControllerImpl; import dagger.Module; import dagger.Provides; @@ -148,6 +152,13 @@ public class ActivityModule { return forumController; } + @ActivityScope + @Provides + ShareForumController provideShareForumController( + ShareForumControllerImpl shareForumController) { + return shareForumController; + } + @ActivityScope @Provides protected ForumInvitationController provideInvitationForumController( @@ -171,6 +182,13 @@ public class ActivityModule { return blogController; } + @ActivityScope + @Provides + ShareBlogController provideShareBlogController( + ShareBlogControllerImpl shareBlogController) { + return shareBlogController; + } + @ActivityScope @Provides FeedController provideFeedController(FeedControllerImpl feedController) { diff --git a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java index 78af58b5b8..22e8391485 100644 --- a/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/BaseContactListAdapter.java @@ -39,10 +39,8 @@ public abstract class BaseContactListAdapter<I extends ContactItem, VH extends C } @Override - public boolean areContentsTheSame(I c1, I c2) { - // check for all properties that influence visual - // representation of contact - return c1.isConnected() == c2.isConnected(); + public boolean areContentsTheSame(ContactItem c1, ContactItem c2) { + return true; } int findItemPosition(ContactId c) { diff --git a/briar-android/src/org/briarproject/android/contact/ContactItem.java b/briar-android/src/org/briarproject/android/contact/ContactItem.java index c5e5764a75..eaa7f66c57 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactItem.java +++ b/briar-android/src/org/briarproject/android/contact/ContactItem.java @@ -11,23 +11,12 @@ public class ContactItem { private final Contact contact; - private boolean connected; - - public ContactItem(Contact contact, boolean connected) { + public ContactItem(Contact contact) { this.contact = contact; - this.connected = connected; } public Contact getContact() { return contact; } - boolean isConnected() { - return connected; - } - - void setConnected(boolean connected) { - this.connected = connected; - } - } diff --git a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java index ae6c45b9aa..707ceeacdd 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListAdapter.java @@ -34,7 +34,7 @@ public class ContactListAdapter extends if (c1.getTimestamp() != c2.getTimestamp()) { return false; } - return super.areContentsTheSame(c1, c2); + return c1.isConnected() == c2.isConnected(); } @Override diff --git a/briar-android/src/org/briarproject/android/contact/ContactListItem.java b/briar-android/src/org/briarproject/android/contact/ContactListItem.java index 73f49047ad..18e4946c16 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListItem.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListItem.java @@ -10,13 +10,14 @@ import javax.annotation.concurrent.NotThreadSafe; @NotNullByDefault public class ContactListItem extends ContactItem { - private boolean empty; + private boolean connected, empty; private long timestamp; private int unread; public ContactListItem(Contact contact, boolean connected, GroupCount count) { - super(contact, connected); + super(contact); + this.connected = connected; this.empty = count.getMsgCount() == 0; this.unread = count.getUnreadCount(); this.timestamp = count.getLatestMsgTime(); @@ -29,6 +30,14 @@ public class ContactListItem extends ContactItem { unread++; } + boolean isConnected() { + return connected; + } + + void setConnected(boolean connected) { + this.connected = connected; + } + boolean isEmpty() { return empty; } diff --git a/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorAdapter.java new file mode 100644 index 0000000000..8134d5d941 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorAdapter.java @@ -0,0 +1,30 @@ +package org.briarproject.android.contactselection; + +import android.content.Context; + +import org.briarproject.android.contact.BaseContactListAdapter; +import org.briarproject.android.contact.ContactItemViewHolder; +import org.briarproject.api.contact.ContactId; + +import java.util.ArrayList; +import java.util.Collection; + +public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem, H extends ContactItemViewHolder<I>> + extends BaseContactListAdapter<I, H> { + + BaseContactSelectorAdapter(Context context, Class<I> c, + OnContactClickListener<I> listener) { + super(context, c, listener); + } + + Collection<ContactId> getSelectedContactIds() { + Collection<ContactId> selected = new ArrayList<>(); + + for (int i = 0; i < items.size(); i++) { + SelectableContactItem item = items.get(i); + if (item.isSelected()) selected.add(item.getContact().getId()); + } + return selected; + } + +} diff --git a/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorFragment.java b/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorFragment.java new file mode 100644 index 0000000000..2f9c91c6f8 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/BaseContactSelectorFragment.java @@ -0,0 +1,138 @@ +package org.briarproject.android.contactselection; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.transition.Fade; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.briarproject.R; +import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener; +import org.briarproject.android.contact.ContactItemViewHolder; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; +import org.briarproject.android.fragment.BaseFragment; +import org.briarproject.android.view.BriarRecyclerView; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; + +import java.util.ArrayList; +import java.util.Collection; + +import static org.briarproject.android.contactselection.ContactSelectorActivity.CONTACTS; +import static org.briarproject.android.contactselection.ContactSelectorActivity.getContactsFromIds; +import static org.briarproject.android.contactselection.ContactSelectorActivity.getContactsFromIntegers; +import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; + +public abstract class BaseContactSelectorFragment<I extends SelectableContactItem, H extends ContactItemViewHolder<I>> + extends BaseFragment + implements OnContactClickListener<I> { + + protected BriarRecyclerView list; + protected BaseContactSelectorAdapter<I, H> adapter; + protected Collection<ContactId> selectedContacts; + protected ContactSelectorListener<I> listener; + + private GroupId groupId; + private ContactSelectorController<I> controller; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + //noinspection unchecked + listener = (ContactSelectorListener<I>) context; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + byte[] b = args.getByteArray(GROUP_ID); + if (b == null) throw new IllegalStateException("No GroupId"); + groupId = new GroupId(b); + } + + @Override + @CallSuper + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + + View contentView = inflater.inflate(R.layout.list, container, false); + + if (Build.VERSION.SDK_INT >= 21) { + setExitTransition(new Fade()); + } + + list = (BriarRecyclerView) contentView.findViewById(R.id.list); + list.setLayoutManager(new LinearLayoutManager(getActivity())); + list.setEmptyText(getString(R.string.no_contacts_selector)); + + // restore selected contacts if available + if (savedInstanceState != null) { + ArrayList<Integer> intContacts = + savedInstanceState.getIntegerArrayList(CONTACTS); + if (intContacts != null) { + selectedContacts = getContactsFromIntegers(intContacts); + } + } + return contentView; + } + + @Override + public void onStart() { + super.onStart(); + controller = listener.getController(); + loadContacts(selectedContacts); + } + + @Override + public void onStop() { + super.onStop(); + adapter.clear(); + list.showProgressBar(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + if (adapter != null) { + selectedContacts = adapter.getSelectedContactIds(); + outState.putIntegerArrayList(CONTACTS, + getContactsFromIds(selectedContacts)); + } + } + + @Override + public void onItemClick(View view, I item) { + item.toggleSelected(); + adapter.notifyItemChanged(adapter.findItemPosition(item), item); + onSelectionChanged(); + } + + private void loadContacts(@Nullable final Collection<ContactId> selection) { + controller.loadContacts(groupId, selection, + new UiResultExceptionHandler<Collection<I>, DbException>( + listener) { + @Override + public void onResultUi(Collection<I> contacts) { + if (contacts.isEmpty()) list.showData(); + else adapter.addAll(contacts); + onSelectionChanged(); + } + + @Override + public void onExceptionUi(DbException exception) { + + } + }); + } + + protected abstract void onSelectionChanged(); + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorActivity.java similarity index 72% rename from briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java rename to briar-android/src/org/briarproject/android/contactselection/ContactSelectorActivity.java index 4c7d513895..1a84cf43fd 100644 --- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorActivity.java @@ -1,4 +1,4 @@ -package org.briarproject.android.sharing; +package org.briarproject.android.contactselection; import android.os.Bundle; import android.support.annotation.CallSuper; @@ -14,11 +14,13 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -public abstract class ContactSelectorActivity extends BriarActivity implements - BaseFragmentListener, ContactSelectorListener { +public abstract class ContactSelectorActivity<I extends SelectableContactItem> + extends BriarActivity + implements BaseFragmentListener, ContactSelectorListener<I> { final static String CONTACTS = "contacts"; + // Subclasses may initialise the group ID in different places protected GroupId groupId; protected Collection<ContactId> contacts; @@ -29,6 +31,10 @@ public abstract class ContactSelectorActivity extends BriarActivity implements setContentView(R.layout.activity_fragment_container); if (bundle != null) { + // restore group ID if it was saved + byte[] groupBytes = bundle.getByteArray(GROUP_ID); + if (groupBytes != null) groupId = new GroupId(groupBytes); + // restore selected contacts if a selection was saved ArrayList<Integer> intContacts = bundle.getIntegerArrayList(CONTACTS); if (intContacts != null) { @@ -40,6 +46,10 @@ public abstract class ContactSelectorActivity extends BriarActivity implements @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); + if (groupId != null) { + // save the group ID here regardless of how subclasses initialize it + outState.putByteArray(GROUP_ID, groupId.getBytes()); + } if (contacts != null) { outState.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts)); @@ -49,9 +59,7 @@ public abstract class ContactSelectorActivity extends BriarActivity implements @CallSuper @UiThread @Override - public void contactsSelected(GroupId groupId, - Collection<ContactId> contacts) { - this.groupId = groupId; + public void contactsSelected(Collection<ContactId> contacts) { this.contacts = contacts; } diff --git a/briar-android/src/org/briarproject/android/contactselection/ContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorAdapter.java new file mode 100644 index 0000000000..3605210112 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorAdapter.java @@ -0,0 +1,26 @@ +package org.briarproject.android.contactselection; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.briarproject.R; + +public class ContactSelectorAdapter extends + BaseContactSelectorAdapter<SelectableContactItem, SelectableContactHolder> { + + ContactSelectorAdapter(Context context, + OnContactClickListener<SelectableContactItem> listener) { + super(context, SelectableContactItem.class, listener); + } + + @Override + public SelectableContactHolder onCreateViewHolder(ViewGroup viewGroup, + int i) { + View v = LayoutInflater.from(ctx).inflate( + R.layout.list_item_selectable_contact, viewGroup, false); + return new SelectableContactHolder(v); + } + +} diff --git a/briar-android/src/org/briarproject/android/contactselection/ContactSelectorController.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorController.java new file mode 100644 index 0000000000..6ae4465290 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorController.java @@ -0,0 +1,19 @@ +package org.briarproject.android.contactselection; + +import android.support.annotation.Nullable; + +import org.briarproject.android.controller.DbController; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +public interface ContactSelectorController<I extends SelectableContactItem> + extends DbController { + + void loadContacts(GroupId g, @Nullable Collection<ContactId> selection, + ResultExceptionHandler<Collection<I>, DbException> handler); + +} diff --git a/briar-android/src/org/briarproject/android/contactselection/ContactSelectorControllerImpl.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorControllerImpl.java new file mode 100644 index 0000000000..ce12b5b992 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorControllerImpl.java @@ -0,0 +1,72 @@ +package org.briarproject.android.contactselection; + +import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.db.DbException; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.sync.GroupId; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +public abstract class ContactSelectorControllerImpl<I extends SelectableContactItem> + extends DbControllerImpl + implements ContactSelectorController<I> { + + protected static final Logger LOG = + Logger.getLogger("ContactSelectorController"); + + private final ContactManager contactManager; + + public ContactSelectorControllerImpl(@DatabaseExecutor Executor dbExecutor, + LifecycleManager lifecycleManager, ContactManager contactManager) { + super(dbExecutor, lifecycleManager); + this.contactManager = contactManager; + } + + @Override + public void loadContacts(final GroupId g, + @Nullable final Collection<ContactId> selection, + final ResultExceptionHandler<Collection<I>, DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + Collection<I> contacts = new ArrayList<>(); + for (Contact c : contactManager.getActiveContacts()) { + // was this contact already selected? + boolean selected = isSelected(c, selection != null && + selection.contains(c.getId())); + // can this contact be selected? + boolean disabled = isDisabled(g, c); + contacts.add(getItem(c, selected, disabled)); + } + handler.onResult(contacts); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + + @DatabaseExecutor + protected abstract boolean isSelected(Contact c, boolean wasSelected); + + @DatabaseExecutor + protected abstract boolean isDisabled(GroupId g, Contact c) + throws DbException; + + protected abstract I getItem(Contact c, boolean selected, boolean disabled); + +} diff --git a/briar-android/src/org/briarproject/android/contactselection/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorFragment.java new file mode 100644 index 0000000000..b766b95e7a --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorFragment.java @@ -0,0 +1,89 @@ +package org.briarproject.android.contactselection; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import org.briarproject.R; +import org.briarproject.android.ActivityComponent; +import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener; +import org.briarproject.api.sync.GroupId; + +import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; + +public class ContactSelectorFragment extends + BaseContactSelectorFragment<SelectableContactItem, SelectableContactHolder> + implements OnContactClickListener<SelectableContactItem> { + + public static final String TAG = ContactSelectorFragment.class.getName(); + + private Menu menu; + + public static ContactSelectorFragment newInstance(GroupId groupId) { + Bundle args = new Bundle(); + args.putByteArray(GROUP_ID, groupId.getBytes()); + ContactSelectorFragment fragment = new ContactSelectorFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void injectFragment(ActivityComponent component) { + component.inject(this); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View contentView = + super.onCreateView(inflater, container, savedInstanceState); + adapter = new ContactSelectorAdapter(getActivity(), this); + list.setAdapter(adapter); + return contentView; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.contact_selection_actions, menu); + super.onCreateOptionsMenu(menu, inflater); + this.menu = menu; + // hide sharing action initially, if no contact is selected + onSelectionChanged(); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case R.id.action_contacts_selected: + selectedContacts = adapter.getSelectedContactIds(); + listener.contactsSelected(selectedContacts); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public String getUniqueTag() { + return TAG; + } + + @Override + protected void onSelectionChanged() { + if (menu == null) return; + MenuItem item = menu.findItem(R.id.action_contacts_selected); + if (item == null) return; + + selectedContacts = adapter.getSelectedContactIds(); + if (selectedContacts.size() > 0) { + item.setVisible(true); + } else { + item.setVisible(false); + } + } + +} diff --git a/briar-android/src/org/briarproject/android/contactselection/ContactSelectorListener.java b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorListener.java new file mode 100644 index 0000000000..fa8bc3d789 --- /dev/null +++ b/briar-android/src/org/briarproject/android/contactselection/ContactSelectorListener.java @@ -0,0 +1,18 @@ +package org.briarproject.android.contactselection; + +import android.support.annotation.UiThread; + +import org.briarproject.android.DestroyableContext; +import org.briarproject.api.contact.ContactId; + +import java.util.Collection; + +interface ContactSelectorListener<I extends SelectableContactItem> + extends DestroyableContext { + + ContactSelectorController<I> getController(); + + @UiThread + void contactsSelected(Collection<ContactId> contacts); + +} diff --git a/briar-android/src/org/briarproject/android/sharing/SelectableContactHolder.java b/briar-android/src/org/briarproject/android/contactselection/SelectableContactHolder.java similarity index 94% rename from briar-android/src/org/briarproject/android/sharing/SelectableContactHolder.java rename to briar-android/src/org/briarproject/android/contactselection/SelectableContactHolder.java index 9f0eddfe8c..47173b51a8 100644 --- a/briar-android/src/org/briarproject/android/sharing/SelectableContactHolder.java +++ b/briar-android/src/org/briarproject/android/contactselection/SelectableContactHolder.java @@ -1,4 +1,4 @@ -package org.briarproject.android.sharing; +package org.briarproject.android.contactselection; import android.support.annotation.UiThread; import android.view.View; @@ -16,7 +16,7 @@ import static android.view.View.VISIBLE; @UiThread @NotNullByDefault -class SelectableContactHolder +public class SelectableContactHolder extends ContactItemViewHolder<SelectableContactItem> { private final CheckBox checkBox; diff --git a/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java b/briar-android/src/org/briarproject/android/contactselection/SelectableContactItem.java similarity index 68% rename from briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java rename to briar-android/src/org/briarproject/android/contactselection/SelectableContactItem.java index 03a9ae3fce..23ed9e0ec1 100644 --- a/briar-android/src/org/briarproject/android/sharing/SelectableContactItem.java +++ b/briar-android/src/org/briarproject/android/contactselection/SelectableContactItem.java @@ -1,4 +1,4 @@ -package org.briarproject.android.sharing; +package org.briarproject.android.contactselection; import org.briarproject.android.contact.ContactItem; import org.briarproject.api.contact.Contact; @@ -8,13 +8,13 @@ import javax.annotation.concurrent.NotThreadSafe; @NotThreadSafe @NotNullByDefault -class SelectableContactItem extends ContactItem { +public class SelectableContactItem extends ContactItem { private boolean selected, disabled; - SelectableContactItem(Contact contact, boolean connected, - boolean selected, boolean disabled) { - super(contact, connected); + public SelectableContactItem(Contact contact, boolean selected, + boolean disabled) { + super(contact); this.selected = selected; this.disabled = disabled; } diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java index 895f6188f7..8359cfe917 100644 --- a/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java +++ b/briar-android/src/org/briarproject/android/privategroup/creation/BaseGroupInviteActivity.java @@ -1,14 +1,13 @@ package org.briarproject.android.privategroup.creation; -import android.os.Bundle; - import org.briarproject.R; +import org.briarproject.android.contactselection.ContactSelectorActivity; +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; -import org.briarproject.android.sharing.ContactSelectorActivity; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; -import org.briarproject.api.sync.GroupId; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -18,36 +17,20 @@ import javax.inject.Inject; import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH; public abstract class BaseGroupInviteActivity - extends ContactSelectorActivity + extends ContactSelectorActivity<SelectableContactItem> implements MessageFragmentListener { @Inject CreateGroupController controller; @Override - public void onCreate(Bundle bundle) { - super.onCreate(bundle); - - // Subclasses may initialise the group ID in different places, - // restore it if it was saved - if (bundle != null) { - byte[] groupBytes = bundle.getByteArray(GROUP_ID); - if (groupBytes != null) groupId = new GroupId(groupBytes); - } - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (groupId != null) { - outState.putByteArray(GROUP_ID, groupId.getBytes()); - } + public ContactSelectorController<SelectableContactItem> getController() { + return controller; } @Override - public void contactsSelected(GroupId groupId, - Collection<ContactId> contacts) { - super.contactsSelected(groupId, contacts); + public void contactsSelected(Collection<ContactId> contacts) { + super.contactsSelected(contacts); CreateGroupMessageFragment fragment = new CreateGroupMessageFragment(); getSupportFragmentManager().beginTransaction() @@ -62,6 +45,8 @@ public abstract class BaseGroupInviteActivity @Override public boolean onButtonClick(@NotNull String message) { + if (groupId == null) + throw new IllegalStateException("GroupId was not initialized"); controller.sendInvitation(groupId, contacts, message, new UiResultExceptionHandler<Void, DbException>(this) { @Override diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java index a82b4cc1de..5b571fe4a7 100644 --- a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java +++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java @@ -7,12 +7,10 @@ import android.support.v4.app.ActivityOptionsCompat; import org.briarproject.R; import org.briarproject.android.ActivityComponent; +import org.briarproject.android.contactselection.ContactSelectorFragment; import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.privategroup.conversation.GroupActivity; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; -import org.briarproject.android.sharing.ContactSelectorFragment; -import org.briarproject.api.contact.Contact; -import org.briarproject.api.db.DatabaseExecutor; import org.briarproject.api.db.DbException; import org.briarproject.api.sync.GroupId; @@ -38,13 +36,6 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements } } - @Override - @DatabaseExecutor - public boolean isDisabled(GroupId groupId, Contact c) throws DbException { - // contacts can always be invited into a new group - return false; - } - @Override public void onBackPressed() { if (getSupportFragmentManager().getBackStackEntryCount() == 1) { diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java index cc46f6e47a..2434ccd7d2 100644 --- a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java +++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java @@ -1,6 +1,7 @@ package org.briarproject.android.privategroup.creation; -import org.briarproject.android.controller.DbController; +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; @@ -10,12 +11,13 @@ import org.briarproject.api.sync.GroupId; import java.util.Collection; @NotNullByDefault -public interface CreateGroupController extends DbController { +public interface CreateGroupController + extends ContactSelectorController<SelectableContactItem> { void createGroup(String name, ResultExceptionHandler<GroupId, DbException> result); - void sendInvitation(GroupId groupId, Collection<ContactId> contacts, + void sendInvitation(GroupId g, Collection<ContactId> contacts, String message, ResultExceptionHandler<Void, DbException> result); } diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java index 4f7ff84a55..a0a849c7c0 100644 --- a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java +++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java @@ -1,6 +1,7 @@ package org.briarproject.android.privategroup.creation; -import org.briarproject.android.controller.DbControllerImpl; +import org.briarproject.android.contactselection.ContactSelectorControllerImpl; +import org.briarproject.android.contactselection.SelectableContactItem; import org.briarproject.android.controller.handler.ResultExceptionHandler; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; @@ -36,7 +37,8 @@ import static java.util.logging.Level.WARNING; @Immutable @NotNullByDefault -public class CreateGroupControllerImpl extends DbControllerImpl +public class CreateGroupControllerImpl + extends ContactSelectorControllerImpl<SelectableContactItem> implements CreateGroupController { private static final Logger LOG = @@ -61,7 +63,7 @@ public class CreateGroupControllerImpl extends DbControllerImpl PrivateGroupManager groupManager, GroupInvitationFactory groupInvitationFactory, GroupInvitationManager groupInvitationManager, Clock clock) { - super(dbExecutor, lifecycleManager); + super(dbExecutor, lifecycleManager, contactManager); this.cryptoExecutor = cryptoExecutor; this.contactManager = contactManager; this.identityManager = identityManager; @@ -128,6 +130,22 @@ public class CreateGroupControllerImpl extends DbControllerImpl }); } + @Override + protected boolean isSelected(Contact c, boolean wasSelected) { + return wasSelected; + } + + @Override + protected boolean isDisabled(GroupId g, Contact c) throws DbException { + return !groupInvitationManager.isInvitationAllowed(c, g); + } + + @Override + protected SelectableContactItem getItem(Contact c, boolean selected, + boolean disabled) { + return new SelectableContactItem(c, selected, disabled); + } + @Override public void sendInvitation(final GroupId g, final Collection<ContactId> contactIds, final String message, @@ -191,6 +209,7 @@ public class CreateGroupControllerImpl extends DbControllerImpl // Continue } } + //noinspection ConstantConditions handler.onResult(null); } catch (DbException e) { if (LOG.isLoggable(WARNING)) diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/GroupInviteActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/GroupInviteActivity.java index abc776f513..d4b225cfca 100644 --- a/briar-android/src/org/briarproject/android/privategroup/creation/GroupInviteActivity.java +++ b/briar-android/src/org/briarproject/android/privategroup/creation/GroupInviteActivity.java @@ -5,22 +5,13 @@ import android.os.Bundle; import org.briarproject.R; import org.briarproject.android.ActivityComponent; +import org.briarproject.android.contactselection.ContactSelectorFragment; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; -import org.briarproject.android.sharing.ContactSelectorFragment; -import org.briarproject.api.contact.Contact; -import org.briarproject.api.db.DatabaseExecutor; -import org.briarproject.api.db.DbException; -import org.briarproject.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.api.sync.GroupId; -import javax.inject.Inject; - public class GroupInviteActivity extends BaseGroupInviteActivity implements MessageFragmentListener { - @Inject - GroupInvitationManager groupInvitationManager; - @Override public void injectActivity(ActivityComponent component) { component.inject(this); @@ -30,8 +21,6 @@ public class GroupInviteActivity extends BaseGroupInviteActivity public void onCreate(Bundle bundle) { super.onCreate(bundle); - // Initialise the group ID, - // it will be saved and restored by the superclass Intent i = getIntent(); byte[] g = i.getByteArrayExtra(GROUP_ID); if (g == null) throw new IllegalStateException("No GroupId in intent."); @@ -46,10 +35,4 @@ public class GroupInviteActivity extends BaseGroupInviteActivity } } - @Override - @DatabaseExecutor - public boolean isDisabled(GroupId g, Contact c) throws DbException { - return !groupInvitationManager.isInvitationAllowed(c, g); - } - } diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java deleted file mode 100644 index 9573c6398d..0000000000 --- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.briarproject.android.sharing; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.briarproject.R; -import org.briarproject.android.contact.BaseContactListAdapter; -import org.briarproject.api.contact.ContactId; - -import java.util.ArrayList; -import java.util.Collection; - -class ContactSelectorAdapter extends - BaseContactListAdapter<SelectableContactItem, SelectableContactHolder> { - - ContactSelectorAdapter(Context context, - OnContactClickListener<SelectableContactItem> listener) { - super(context, SelectableContactItem.class, listener); - } - - @Override - public SelectableContactHolder onCreateViewHolder(ViewGroup viewGroup, - int i) { - View v = LayoutInflater.from(ctx).inflate( - R.layout.list_item_selectable_contact, viewGroup, false); - return new SelectableContactHolder(v); - } - - Collection<ContactId> getSelectedContactIds() { - Collection<ContactId> selected = new ArrayList<>(); - - for (int i = 0; i < items.size(); i++) { - SelectableContactItem item = items.get(i); - if (item.isSelected()) selected.add(item.getContact().getId()); - } - return selected; - } - -} diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java deleted file mode 100644 index cc8f46230b..0000000000 --- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java +++ /dev/null @@ -1,233 +0,0 @@ -package org.briarproject.android.sharing; - -import android.content.Context; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.transition.Fade; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - -import org.briarproject.R; -import org.briarproject.android.ActivityComponent; -import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener; -import org.briarproject.android.fragment.BaseFragment; -import org.briarproject.android.view.BriarRecyclerView; -import org.briarproject.api.contact.Contact; -import org.briarproject.api.contact.ContactId; -import org.briarproject.api.contact.ContactManager; -import org.briarproject.api.db.DbException; -import org.briarproject.api.plugins.ConnectionRegistry; -import org.briarproject.api.sync.GroupId; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.logging.Logger; - -import javax.inject.Inject; - -import static java.util.logging.Level.INFO; -import static java.util.logging.Level.WARNING; -import static org.briarproject.android.sharing.ShareActivity.CONTACTS; -import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds; -import static org.briarproject.android.sharing.ShareActivity.getContactsFromIntegers; -import static org.briarproject.api.sharing.SharingConstants.GROUP_ID; - -public class ContactSelectorFragment extends BaseFragment - implements OnContactClickListener<SelectableContactItem> { - - public static final String TAG = ContactSelectorFragment.class.getName(); - private static final Logger LOG = Logger.getLogger(TAG); - - private Menu menu; - private BriarRecyclerView list; - private ContactSelectorAdapter adapter; - private Collection<ContactId> selectedContacts; - - // Fields that are accessed from background threads must be volatile - @Inject - volatile ContactManager contactManager; - @Inject - volatile ConnectionRegistry connectionRegistry; - - private volatile GroupId groupId; - private volatile ContactSelectorListener listener; - - public static ContactSelectorFragment newInstance(GroupId groupId) { - Bundle args = new Bundle(); - args.putByteArray(GROUP_ID, groupId.getBytes()); - ContactSelectorFragment fragment = new ContactSelectorFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void injectFragment(ActivityComponent component) { - component.inject(this); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - listener = (ContactSelectorListener) context; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle args = getArguments(); - byte[] b = args.getByteArray(GROUP_ID); - if (b == null) throw new IllegalStateException("No GroupId"); - groupId = new GroupId(b); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - View contentView = inflater.inflate(R.layout.list, container, false); - - if (Build.VERSION.SDK_INT >= 21) { - setExitTransition(new Fade()); - } - - adapter = new ContactSelectorAdapter(getActivity(), this); - - list = (BriarRecyclerView) contentView.findViewById(R.id.list); - list.setLayoutManager(new LinearLayoutManager(getActivity())); - list.setAdapter(adapter); - list.setEmptyText(getString(R.string.no_contacts_selector)); - - // restore selected contacts if available - if (savedInstanceState != null) { - ArrayList<Integer> intContacts = - savedInstanceState.getIntegerArrayList(CONTACTS); - if (intContacts != null) { - selectedContacts = getContactsFromIntegers(intContacts); - } - } - return contentView; - } - - @Override - public void onStart() { - super.onStart(); - loadContacts(selectedContacts); - } - - @Override - public void onStop() { - super.onStop(); - adapter.clear(); - list.showProgressBar(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - if (adapter != null) { - selectedContacts = adapter.getSelectedContactIds(); - outState.putIntegerArrayList(CONTACTS, - getContactsFromIds(selectedContacts)); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.contact_selection_actions, menu); - super.onCreateOptionsMenu(menu, inflater); - this.menu = menu; - // hide sharing action initially, if no contact is selected - updateMenuItem(); - } - - @Override - public boolean onOptionsItemSelected(final MenuItem item) { - // Handle presses on the action bar items - switch (item.getItemId()) { - case R.id.action_contacts_selected: - selectedContacts = adapter.getSelectedContactIds(); - listener.contactsSelected(groupId, selectedContacts); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public String getUniqueTag() { - return TAG; - } - - @Override - public void onItemClick(View view, SelectableContactItem item) { - item.toggleSelected(); - adapter.notifyItemChanged(adapter.findItemPosition(item), item); - - updateMenuItem(); - } - - private void loadContacts(@Nullable final Collection<ContactId> selection) { - listener.runOnDbThread(new Runnable() { - @Override - public void run() { - try { - long now = System.currentTimeMillis(); - List<SelectableContactItem> contacts = - new ArrayList<>(); - for (Contact c : contactManager.getActiveContacts()) { - // is this contact online? - boolean connected = - connectionRegistry.isConnected(c.getId()); - // was this contact already selected? - boolean selected = selection != null && - selection.contains(c.getId()); - // do we have already some sharing with that contact? - boolean disabled = listener.isDisabled(groupId, c); - contacts.add(new SelectableContactItem(c, connected, - selected, disabled)); - } - long duration = System.currentTimeMillis() - now; - if (LOG.isLoggable(INFO)) - LOG.info("Load took " + duration + " ms"); - displayContacts(contacts); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void displayContacts( - final List<SelectableContactItem> contacts) { - listener.runOnUiThreadUnlessDestroyed(new Runnable() { - @Override - public void run() { - if (contacts.isEmpty()) list.showData(); - else adapter.addAll(contacts); - updateMenuItem(); - } - }); - } - - private void updateMenuItem() { - if (menu == null) return; - MenuItem item = menu.findItem(R.id.action_contacts_selected); - if (item == null) return; - - selectedContacts = adapter.getSelectedContactIds(); - if (selectedContacts.size() > 0) { - item.setVisible(true); - } else { - item.setVisible(false); - } - } -} diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java deleted file mode 100644 index 48949b14cd..0000000000 --- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.briarproject.android.sharing; - -import android.support.annotation.UiThread; - -import org.briarproject.android.DestroyableContext; -import org.briarproject.api.contact.Contact; -import org.briarproject.api.contact.ContactId; -import org.briarproject.api.db.DatabaseExecutor; -import org.briarproject.api.db.DbException; -import org.briarproject.api.sync.GroupId; - -import java.util.Collection; - -interface ContactSelectorListener extends DestroyableContext { - - @Deprecated - void runOnDbThread(Runnable runnable); - - @DatabaseExecutor - boolean isDisabled(GroupId groupId, Contact c) throws DbException; - - @UiThread - void contactsSelected(GroupId groupId, Collection<ContactId> contacts); - - @UiThread - void onBackPressed(); - -} diff --git a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java index e89fe277e1..53d93628eb 100644 --- a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java @@ -2,29 +2,22 @@ package org.briarproject.android.sharing; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.StringRes; import android.support.annotation.UiThread; -import android.widget.Toast; import org.briarproject.R; +import org.briarproject.android.contactselection.ContactSelectorActivity; +import org.briarproject.android.contactselection.ContactSelectorFragment; +import org.briarproject.android.contactselection.SelectableContactItem; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; import org.briarproject.api.contact.ContactId; -import org.briarproject.api.db.DatabaseExecutor; -import org.briarproject.api.db.DbException; import org.briarproject.api.sync.GroupId; import org.jetbrains.annotations.NotNull; import java.util.Collection; -import java.util.logging.Logger; -import static android.widget.Toast.LENGTH_SHORT; -import static java.util.logging.Level.WARNING; - -public abstract class ShareActivity extends ContactSelectorActivity implements - MessageFragmentListener { - - private final static Logger LOG = - Logger.getLogger(ShareActivity.class.getName()); +public abstract class ShareActivity + extends ContactSelectorActivity<SelectableContactItem> + implements MessageFragmentListener { @Override public void onCreate(Bundle bundle) { @@ -46,9 +39,8 @@ public abstract class ShareActivity extends ContactSelectorActivity implements @UiThread @Override - public void contactsSelected(GroupId groupId, - Collection<ContactId> contacts) { - super.contactsSelected(groupId, contacts); + public void contactsSelected(Collection<ContactId> contacts) { + super.contactsSelected(contacts); BaseMessageFragment messageFragment = getMessageFragment(); getSupportFragmentManager().beginTransaction() @@ -67,45 +59,12 @@ public abstract class ShareActivity extends ContactSelectorActivity implements @UiThread @Override public boolean onButtonClick(@NotNull String message) { - share(groupId, contacts, message); + share(contacts, message); setResult(RESULT_OK); supportFinishAfterTransition(); return true; } - private void share(final GroupId g, final Collection<ContactId> contacts, - final String msg) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - for (ContactId c : contacts) { - share(g, c, msg); - } - } catch (DbException e) { - // TODO proper error handling - sharingError(); - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - @DatabaseExecutor - protected abstract void share(GroupId g, ContactId c, String msg) - throws DbException; - - private void sharingError() { - runOnUiThreadUnlessDestroyed(new Runnable() { - @Override - public void run() { - int res = getSharingError(); - Toast.makeText(ShareActivity.this, res, LENGTH_SHORT).show(); - } - }); - } - - protected abstract @StringRes int getSharingError(); + abstract void share(Collection<ContactId> contacts, String msg); } diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java index 7d5cdeef3e..ffd218a381 100644 --- a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java @@ -1,22 +1,26 @@ package org.briarproject.android.sharing; +import android.widget.Toast; + import org.briarproject.R; import org.briarproject.android.ActivityComponent; -import org.briarproject.api.blogs.BlogSharingManager; -import org.briarproject.api.contact.Contact; +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; -import org.briarproject.api.sync.GroupId; + +import java.util.Collection; import javax.inject.Inject; +import static android.widget.Toast.LENGTH_SHORT; import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; public class ShareBlogActivity extends ShareActivity { - // Fields that are accessed from background threads must be volatile @Inject - volatile BlogSharingManager blogSharingManager; + ShareBlogController controller; @Override BaseMessageFragment getMessageFragment() { @@ -29,23 +33,33 @@ public class ShareBlogActivity extends ShareActivity { } @Override - public boolean isDisabled(GroupId groupId, Contact c) throws DbException { - return !blogSharingManager.canBeShared(groupId, c); + public ContactSelectorController<SelectableContactItem> getController() { + return controller; } @Override - protected void share(GroupId g, ContactId c, String msg) - throws DbException { - blogSharingManager.sendInvitation(g, c, msg); + public int getMaximumMessageLength() { + return MAX_MESSAGE_BODY_LENGTH; } @Override - protected int getSharingError() { - return R.string.blogs_sharing_error; - } + void share(Collection<ContactId> contacts, String msg) { + controller.share(groupId, contacts, msg, + new UiResultExceptionHandler<Void, DbException>(this) { + @Override + public void onResultUi(Void result) { + + } + + @Override + public void onExceptionUi(DbException exception) { + // TODO proper error handling + Toast.makeText(ShareBlogActivity.this, + R.string.blogs_sharing_error, LENGTH_SHORT) + .show(); + } + }); - @Override - public int getMaximumMessageLength() { - return MAX_MESSAGE_BODY_LENGTH; } + } diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogController.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogController.java new file mode 100644 index 0000000000..ffaa0ecac9 --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogController.java @@ -0,0 +1,18 @@ +package org.briarproject.android.sharing; + +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +public interface ShareBlogController + extends ContactSelectorController<SelectableContactItem> { + + void share(GroupId g, Collection<ContactId> contacts, String msg, + ResultExceptionHandler<Void, DbException> handler); + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogControllerImpl.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogControllerImpl.java new file mode 100644 index 0000000000..0955989972 --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogControllerImpl.java @@ -0,0 +1,74 @@ +package org.briarproject.android.sharing; + +import org.briarproject.android.contactselection.ContactSelectorControllerImpl; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.blogs.BlogSharingManager; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.db.DbException; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +import static java.util.logging.Level.WARNING; + +public class ShareBlogControllerImpl + extends ContactSelectorControllerImpl<SelectableContactItem> + implements ShareBlogController { + + private final BlogSharingManager blogSharingManager; + + @Inject + public ShareBlogControllerImpl( + @DatabaseExecutor Executor dbExecutor, + LifecycleManager lifecycleManager, + ContactManager contactManager, + BlogSharingManager blogSharingManager) { + super(dbExecutor, lifecycleManager, contactManager); + this.blogSharingManager = blogSharingManager; + } + + @Override + protected boolean isSelected(Contact c, boolean wasSelected) { + return wasSelected; + } + + @Override + protected boolean isDisabled(GroupId g, Contact c) throws DbException { + return !blogSharingManager.canBeShared(g, c); + } + + @Override + protected SelectableContactItem getItem(Contact c, boolean selected, + boolean disabled) { + return new SelectableContactItem(c, selected, disabled); + } + + @Override + public void share(final GroupId g, final Collection<ContactId> contacts, + final String msg, + final ResultExceptionHandler<Void, DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + for (ContactId c : contacts) { + blogSharingManager.sendInvitation(g, c, msg); + } + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java index 492e3615b2..16133e3cfc 100644 --- a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java @@ -1,22 +1,27 @@ package org.briarproject.android.sharing; +import android.os.Bundle; +import android.widget.Toast; + import org.briarproject.R; import org.briarproject.android.ActivityComponent; -import org.briarproject.api.contact.Contact; +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.api.contact.ContactId; import org.briarproject.api.db.DbException; -import org.briarproject.api.forum.ForumSharingManager; -import org.briarproject.api.sync.GroupId; + +import java.util.Collection; import javax.inject.Inject; +import static android.widget.Toast.LENGTH_SHORT; import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; public class ShareForumActivity extends ShareActivity { - // Fields that are accessed from background threads must be volatile @Inject - volatile ForumSharingManager forumSharingManager; + ShareForumController controller; @Override BaseMessageFragment getMessageFragment() { @@ -29,23 +34,37 @@ public class ShareForumActivity extends ShareActivity { } @Override - public boolean isDisabled(GroupId groupId, Contact c) throws DbException { - return !forumSharingManager.canBeShared(groupId, c); + public ContactSelectorController<SelectableContactItem> getController() { + return controller; } @Override - protected void share(GroupId g, ContactId c, String msg) - throws DbException { - forumSharingManager.sendInvitation(g, c, msg); + public void onCreate(Bundle bundle) { + super.onCreate(bundle); } @Override - protected int getSharingError() { - return R.string.forum_share_error; + public int getMaximumMessageLength() { + return MAX_MESSAGE_BODY_LENGTH; } @Override - public int getMaximumMessageLength() { - return MAX_MESSAGE_BODY_LENGTH; + void share(Collection<ContactId> contacts, String msg) { + controller.share(groupId, contacts, msg, + new UiResultExceptionHandler<Void, DbException>(this) { + @Override + public void onResultUi(Void result) { + + } + + @Override + public void onExceptionUi(DbException exception) { + // TODO proper error handling + Toast.makeText(ShareForumActivity.this, + R.string.forum_share_error, LENGTH_SHORT) + .show(); + } + }); } + } diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumController.java b/briar-android/src/org/briarproject/android/sharing/ShareForumController.java new file mode 100644 index 0000000000..000398f5fa --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/ShareForumController.java @@ -0,0 +1,18 @@ +package org.briarproject.android.sharing; + +import org.briarproject.android.contactselection.ContactSelectorController; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +public interface ShareForumController + extends ContactSelectorController<SelectableContactItem> { + + void share(GroupId g, Collection<ContactId> contacts, String msg, + ResultExceptionHandler<Void, DbException> handler); + +} diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumControllerImpl.java b/briar-android/src/org/briarproject/android/sharing/ShareForumControllerImpl.java new file mode 100644 index 0000000000..e4bf4a5559 --- /dev/null +++ b/briar-android/src/org/briarproject/android/sharing/ShareForumControllerImpl.java @@ -0,0 +1,74 @@ +package org.briarproject.android.sharing; + +import org.briarproject.android.contactselection.ContactSelectorControllerImpl; +import org.briarproject.android.contactselection.SelectableContactItem; +import org.briarproject.android.controller.handler.ResultExceptionHandler; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.db.DatabaseExecutor; +import org.briarproject.api.db.DbException; +import org.briarproject.api.forum.ForumSharingManager; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +import static java.util.logging.Level.WARNING; + +public class ShareForumControllerImpl + extends ContactSelectorControllerImpl<SelectableContactItem> + implements ShareForumController { + + private final ForumSharingManager forumSharingManager; + + @Inject + public ShareForumControllerImpl( + @DatabaseExecutor Executor dbExecutor, + LifecycleManager lifecycleManager, + ContactManager contactManager, + ForumSharingManager forumSharingManager) { + super(dbExecutor, lifecycleManager, contactManager); + this.forumSharingManager = forumSharingManager; + } + + @Override + protected boolean isSelected(Contact c, boolean wasSelected) { + return wasSelected; + } + + @Override + protected boolean isDisabled(GroupId g, Contact c) throws DbException { + return !forumSharingManager.canBeShared(g, c); + } + + @Override + protected SelectableContactItem getItem(Contact c, boolean selected, + boolean disabled) { + return new SelectableContactItem(c, selected, disabled); + } + + @Override + public void share(final GroupId g, final Collection<ContactId> contacts, + final String msg, + final ResultExceptionHandler<Void, DbException> handler) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + for (ContactId c : contacts) { + forumSharingManager.sendInvitation(g, c, msg); + } + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + handler.onException(e); + } + } + }); + } + +} diff --git a/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java index f1a7bda253..4e62598ae5 100644 --- a/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java +++ b/briar-android/src/org/briarproject/android/sharing/SharingStatusActivity.java @@ -11,7 +11,6 @@ import org.briarproject.android.contact.ContactItem; import org.briarproject.android.view.BriarRecyclerView; import org.briarproject.api.contact.Contact; import org.briarproject.api.db.DbException; -import org.briarproject.api.plugins.ConnectionRegistry; import org.briarproject.api.sync.GroupId; import java.util.ArrayList; @@ -19,8 +18,6 @@ import java.util.Collection; import java.util.List; import java.util.logging.Logger; -import javax.inject.Inject; - import static java.util.logging.Level.WARNING; abstract class SharingStatusActivity extends BriarActivity { @@ -32,10 +29,6 @@ abstract class SharingStatusActivity extends BriarActivity { private BriarRecyclerView sharedByList, sharedWithList; private SharingStatusAdapter sharedByAdapter, sharedWithAdapter; - // Fields that are accessed from background threads must be volatile - @Inject - volatile ConnectionRegistry connectionRegistry; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -109,9 +102,7 @@ abstract class SharingStatusActivity extends BriarActivity { try { List<ContactItem> contactItems = new ArrayList<>(); for (Contact c : getSharedBy()) { - boolean isConnected = - connectionRegistry.isConnected(c.getId()); - ContactItem item = new ContactItem(c, isConnected); + ContactItem item = new ContactItem(c); contactItems.add(item); } displaySharedBy(contactItems); @@ -140,9 +131,7 @@ abstract class SharingStatusActivity extends BriarActivity { try { List<ContactItem> contactItems = new ArrayList<>(); for (Contact c : getSharedWith()) { - boolean isConnected = - connectionRegistry.isConnected(c.getId()); - ContactItem item = new ContactItem(c, isConnected); + ContactItem item = new ContactItem(c); contactItems.add(item); } displaySharedWith(contactItems); -- GitLab