Merge branch 'contact-selector-controller' into 'master'

Add a controller for contact selection lists

See merge request !401
This commit is contained in:
akwizgran
2016-11-11 10:54:07 +00:00
38 changed files with 973 additions and 519 deletions

View File

@@ -33,17 +33,19 @@ import org.briarproject.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.android.privategroup.creation.GroupInviteFragment;
import org.briarproject.android.privategroup.invitation.GroupInvitationActivity;
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.sharing.ForumInvitationActivity;
import org.briarproject.android.sharing.ForumSharingStatusActivity;
import org.briarproject.android.sharing.ShareBlogActivity;
import org.briarproject.android.sharing.ShareBlogFragment;
import org.briarproject.android.sharing.ShareBlogMessageFragment;
import org.briarproject.android.sharing.ShareForumActivity;
import org.briarproject.android.sharing.ShareForumFragment;
import org.briarproject.android.sharing.ShareForumMessageFragment;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider;
import org.thoughtcrime.securesms.components.emoji.RecentEmojiPageModel;
@@ -142,6 +144,8 @@ public interface ActivityComponent {
void inject(GroupListFragment fragment);
void inject(GroupInviteFragment fragment);
void inject(ForumListFragment fragment);
void inject(FeedFragment fragment);
@@ -152,10 +156,12 @@ public interface ActivityComponent {
void inject(ContactChooserFragment fragment);
void inject(ContactSelectorFragment fragment);
void inject(ShareForumFragment fragment);
void inject(ShareForumMessageFragment fragment);
void inject(ShareBlogFragment fragment);
void inject(ShareBlogMessageFragment fragment);
void inject(IntroductionMessageFragment fragment);

View File

@@ -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) {

View File

@@ -2,6 +2,7 @@ package org.briarproject.android;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@@ -31,7 +32,7 @@ public abstract class BaseActivity extends AppCompatActivity
}
@Override
public void onCreate(Bundle savedInstanceState) {
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -0,0 +1,32 @@
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 org.briarproject.api.nullsafety.NotNullByDefault;
import java.util.ArrayList;
import java.util.Collection;
@NotNullByDefault
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;
}
}

View File

@@ -0,0 +1,143 @@
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.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
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;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
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 = new ArrayList<>();
protected ContactSelectorListener<I> listener;
private GroupId groupId;
@Override
public void onAttach(Context context) {
super.onAttach(context);
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,
@Nullable ViewGroup container,
@Nullable 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();
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(final Collection<ContactId> selection) {
getController().loadContacts(groupId, selection,
new UiResultExceptionHandler<Collection<I>, DbException>(
this) {
@Override
public void onResultUi(Collection<I> contacts) {
if (contacts.isEmpty()) list.showData();
else adapter.addAll(contacts);
onSelectionChanged();
}
@Override
public void onExceptionUi(DbException exception) {
// TODO error handling
finish();
}
});
}
protected abstract void onSelectionChanged();
protected abstract ContactSelectorController<I> getController();
}

View File

@@ -1,34 +1,45 @@
package org.briarproject.android.sharing;
package org.briarproject.android.contactselection;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import org.briarproject.R;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public abstract class ContactSelectorActivity extends BriarActivity implements
BaseFragmentListener, ContactSelectorListener {
@MethodsNotNullByDefault
@ParametersNotNullByDefault
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;
@Override
public void onCreate(Bundle bundle) {
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
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 +51,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 +64,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;
}

View File

@@ -0,0 +1,28 @@
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;
import org.briarproject.api.nullsafety.NotNullByDefault;
@NotNullByDefault
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);
}
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.android.contactselection;
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.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
@NotNullByDefault
public interface ContactSelectorController<I extends SelectableContactItem>
extends DbController {
void loadContacts(GroupId g, Collection<ContactId> selection,
ResultExceptionHandler<Collection<I>, DbException> handler);
}

View File

@@ -0,0 +1,77 @@
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.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
public abstract class ContactSelectorControllerImpl<I extends SelectableContactItem>
extends DbControllerImpl
implements ContactSelectorController<I> {
private static final Logger LOG =
Logger.getLogger(ContactSelectorControllerImpl.class.getName());
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,
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.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)
throws DbException;
@DatabaseExecutor
protected abstract boolean isDisabled(GroupId g, Contact c)
throws DbException;
protected abstract I getItem(Contact c, boolean selected, boolean disabled);
}

View File

@@ -0,0 +1,73 @@
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.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.jetbrains.annotations.Nullable;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class ContactSelectorFragment extends
BaseContactSelectorFragment<SelectableContactItem, SelectableContactHolder>
implements OnContactClickListener<SelectableContactItem> {
public static final String TAG = ContactSelectorFragment.class.getName();
private Menu menu;
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable 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
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);
}
}
}

View File

@@ -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 org.briarproject.api.nullsafety.NotNullByDefault;
import java.util.Collection;
@NotNullByDefault
interface ContactSelectorListener<I extends SelectableContactItem>
extends DestroyableContext {
@UiThread
void contactsSelected(Collection<ContactId> contacts);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -1,14 +1,15 @@
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.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
@@ -17,37 +18,18 @@ import javax.inject.Inject;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
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());
}
}
@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 +44,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

View File

@@ -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) {
@@ -76,8 +67,8 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
private void switchToContactSelectorFragment(GroupId g) {
setTitle(R.string.groups_invite_members);
ContactSelectorFragment fragment =
ContactSelectorFragment.newInstance(g);
GroupInviteFragment fragment =
GroupInviteFragment.newInstance(g);
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(android.R.anim.fade_in,
android.R.anim.fade_out,

View File

@@ -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);
}

View File

@@ -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,23 @@ public class CreateGroupControllerImpl extends DbControllerImpl
});
}
@Override
protected boolean isSelected(Contact c, boolean wasSelected)
throws DbException {
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 +210,7 @@ public class CreateGroupControllerImpl extends DbControllerImpl
// Continue
}
}
//noinspection ConstantConditions
handler.onResult(null);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))

View File

@@ -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,26 +21,18 @@ 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.");
groupId = new GroupId(g);
if (bundle == null) {
ContactSelectorFragment fragment =
ContactSelectorFragment.newInstance(groupId);
GroupInviteFragment fragment =
GroupInviteFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit();
}
}
@Override
@DatabaseExecutor
public boolean isDisabled(GroupId g, Contact c) throws DbException {
return !groupInvitationManager.isInvitationAllowed(c, g);
}
}

View File

@@ -0,0 +1,49 @@
package org.briarproject.android.privategroup.creation;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contactselection.ContactSelectorController;
import org.briarproject.android.contactselection.ContactSelectorFragment;
import org.briarproject.android.contactselection.SelectableContactItem;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId;
import javax.inject.Inject;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class GroupInviteFragment extends ContactSelectorFragment {
public static final String TAG = GroupInviteFragment.class.getName();
@Inject
CreateGroupController controller;
public static GroupInviteFragment newInstance(GroupId groupId) {
Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes());
GroupInviteFragment fragment = new GroupInviteFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
protected ContactSelectorController<SelectableContactItem> getController() {
return controller;
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -2,7 +2,6 @@ package org.briarproject.android.sharing;
import org.briarproject.android.controller.handler.ResultExceptionHandler;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DatabaseExecutor;
@@ -22,19 +21,18 @@ import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.blogs.BlogManager.CLIENT_ID;
public class BlogInvitationControllerImpl
extends InvitationControllerImpl<SharingInvitationItem>
implements BlogInvitationController {
private final BlogManager blogManager;
private final BlogSharingManager blogSharingManager;
@Inject
BlogInvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus,
BlogManager blogManager, BlogSharingManager blogSharingManager) {
BlogSharingManager blogSharingManager) {
super(dbExecutor, lifecycleManager, eventBus);
this.blogManager = blogManager;
this.blogSharingManager = blogSharingManager;
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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();
}

View File

@@ -8,7 +8,6 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.SharingInvitationItem;
@@ -26,16 +25,13 @@ public class ForumInvitationControllerImpl
extends InvitationControllerImpl<SharingInvitationItem>
implements ForumInvitationController {
private final ForumManager forumManager;
private final ForumSharingManager forumSharingManager;
@Inject
ForumInvitationControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus,
ForumManager forumManager,
ForumSharingManager forumSharingManager) {
super(dbExecutor, lifecycleManager, eventBus);
this.forumManager = forumManager;
this.forumSharingManager = forumSharingManager;
}

View File

@@ -2,53 +2,42 @@ 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.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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());
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class ShareActivity
extends ContactSelectorActivity<SelectableContactItem>
implements MessageFragmentListener {
@Override
public void onCreate(Bundle bundle) {
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID);
if (b == null) throw new IllegalStateException("No GroupId");
groupId = new GroupId(b);
if (bundle == null) {
ContactSelectorFragment contactSelectorFragment =
ContactSelectorFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, contactSelectorFragment)
.commit();
}
}
@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 +56,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);
}

View File

@@ -1,22 +1,30 @@
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.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
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 org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.jetbrains.annotations.Nullable;
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;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
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 +37,40 @@ public class ShareBlogActivity extends ShareActivity {
}
@Override
public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !blogSharingManager.canBeShared(groupId, c);
}
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
@Override
protected void share(GroupId g, ContactId c, String msg)
throws DbException {
blogSharingManager.sendInvitation(g, c, msg);
}
@Override
protected int getSharingError() {
return R.string.blogs_sharing_error;
if (bundle == null) {
ShareBlogFragment fragment = ShareBlogFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
@Override
public int getMaximumMessageLength() {
return MAX_MESSAGE_BODY_LENGTH;
}
@Override
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();
}
});
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,90 @@
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.db.NoSuchContactException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
public class ShareBlogControllerImpl
extends ContactSelectorControllerImpl<SelectableContactItem>
implements ShareBlogController {
private final static Logger LOG =
Logger.getLogger(ShareBlogControllerImpl.class.getName());
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)
throws DbException {
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) {
try {
blogSharingManager.sendInvitation(g, c, msg);
} catch (NoSuchContactException | NoSuchGroupException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
handler.onException(e);
}
}
});
}
}

View File

@@ -0,0 +1,49 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contactselection.ContactSelectorController;
import org.briarproject.android.contactselection.ContactSelectorFragment;
import org.briarproject.android.contactselection.SelectableContactItem;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId;
import javax.inject.Inject;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ShareBlogFragment extends ContactSelectorFragment {
public static final String TAG = ShareBlogFragment.class.getName();
@Inject
ShareBlogController controller;
public static ShareBlogFragment newInstance(GroupId groupId) {
Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes());
ShareBlogFragment fragment = new ShareBlogFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
protected ContactSelectorController<SelectableContactItem> getController() {
return controller;
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -1,22 +1,30 @@
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.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 org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.jetbrains.annotations.Nullable;
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;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
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 +37,40 @@ public class ShareForumActivity extends ShareActivity {
}
@Override
public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !forumSharingManager.canBeShared(groupId, c);
}
public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle);
@Override
protected void share(GroupId g, ContactId c, String msg)
throws DbException {
forumSharingManager.sendInvitation(g, c, msg);
}
@Override
protected int getSharingError() {
return R.string.forum_share_error;
if (bundle == null) {
ShareForumFragment fragment =
ShareForumFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, fragment)
.commit();
}
}
@Override
public int getMaximumMessageLength() {
return MAX_MESSAGE_BODY_LENGTH;
}
@Override
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();
}
});
}
}

View File

@@ -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);
}

View File

@@ -0,0 +1,90 @@
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.db.NoSuchContactException;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
@Immutable
@NotNullByDefault
public class ShareForumControllerImpl
extends ContactSelectorControllerImpl<SelectableContactItem>
implements ShareForumController {
private final static Logger LOG =
Logger.getLogger(ShareForumControllerImpl.class.getName());
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)
throws DbException {
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) {
try {
forumSharingManager.sendInvitation(g, c, msg);
} catch (NoSuchContactException | NoSuchGroupException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
handler.onException(e);
}
}
});
}
}

View File

@@ -0,0 +1,49 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contactselection.ContactSelectorController;
import org.briarproject.android.contactselection.ContactSelectorFragment;
import org.briarproject.android.contactselection.SelectableContactItem;
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.api.sync.GroupId;
import javax.inject.Inject;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ShareForumFragment extends ContactSelectorFragment {
public static final String TAG = ShareForumFragment.class.getName();
@Inject
ShareForumController controller;
public static ShareForumFragment newInstance(GroupId groupId) {
Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes());
ShareForumFragment fragment = new ShareForumFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
protected ContactSelectorController<SelectableContactItem> getController() {
return controller;
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -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);