diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index aad00c6d9..19b30172c 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -122,6 +122,16 @@
/>
+
+
+
+
Leave Group
Dissolve Group
+
+ Group Invitations
+ Joined Group
+ Group Invitation Declined
+
+ - %d open group invitation
+ - %d open group invitations
+
+
You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.
New Forum
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 1179905b6..61889b113 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -33,6 +33,7 @@ import org.briarproject.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.android.privategroup.conversation.GroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.android.privategroup.list.GroupListFragment;
+import org.briarproject.android.privategroup.invitation.InvitationsGroupActivity;
import org.briarproject.android.sharing.ContactSelectorFragment;
import org.briarproject.android.sharing.InvitationsBlogActivity;
import org.briarproject.android.sharing.InvitationsForumActivity;
@@ -77,8 +78,8 @@ public interface ActivityComponent {
void inject(InvitationsBlogActivity activity);
void inject(CreateGroupActivity activity);
-
void inject(GroupActivity activity);
+ void inject(InvitationsGroupActivity activity);
void inject(CreateForumActivity activity);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index c5a1b275d..39d460151 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -25,6 +25,8 @@ import org.briarproject.android.privategroup.conversation.GroupController;
import org.briarproject.android.privategroup.conversation.GroupControllerImpl;
import org.briarproject.android.privategroup.creation.CreateGroupController;
import org.briarproject.android.privategroup.creation.CreateGroupControllerImpl;
+import org.briarproject.android.privategroup.invitation.InvitationsGroupController;
+import org.briarproject.android.privategroup.invitation.InvitationsGroupControllerImpl;
import org.briarproject.android.privategroup.list.GroupListController;
import org.briarproject.android.privategroup.list.GroupListControllerImpl;
import org.briarproject.android.sharing.InvitationsBlogController;
@@ -121,6 +123,13 @@ public class ActivityModule {
return groupController;
}
+ @ActivityScope
+ @Provides
+ protected InvitationsGroupController provideInvitationsGroupController(
+ InvitationsGroupControllerImpl invitationsGroupController) {
+ return invitationsGroupController;
+ }
+
@ActivityScope
@Provides
protected ForumController provideForumController(
diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index ceb3cd9e0..6081f1da9 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -35,6 +35,7 @@ import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.api.settings.SettingsManager;
import org.briarproject.api.system.Clock;
import org.briarproject.plugins.AndroidPluginsModule;
@@ -96,6 +97,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
PrivateGroupManager privateGroupManager();
+ GroupInvitationManager groupInvitationManager();
+
ForumManager forumManager();
ForumSharingManager forumSharingManager();
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index f1faecd46..779fac49b 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -220,10 +220,10 @@ public class ForumListFragment extends BaseEventFragment implements
if (availableCount == 0) {
snackbar.dismiss();
} else {
- snackbar.show();
snackbar.setText(getResources().getQuantityString(
R.plurals.forums_shared, availableCount,
availableCount));
+ if (!snackbar.isShownOrQueued()) snackbar.show();
}
}
});
diff --git a/briar-android/src/org/briarproject/android/privategroup/invitation/GroupInvitationViewHolder.java b/briar-android/src/org/briarproject/android/privategroup/invitation/GroupInvitationViewHolder.java
new file mode 100644
index 000000000..25aef8ce8
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/invitation/GroupInvitationViewHolder.java
@@ -0,0 +1,28 @@
+package org.briarproject.android.privategroup.invitation;
+
+import android.support.annotation.Nullable;
+import android.view.View;
+
+import org.briarproject.R;
+import org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
+import org.briarproject.android.sharing.InvitationViewHolder;
+import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
+
+public class GroupInvitationViewHolder extends InvitationViewHolder {
+
+ public GroupInvitationViewHolder(View v) {
+ super(v);
+ }
+
+ @Override
+ public void onBind(@Nullable final GroupInvitationItem item,
+ final InvitationClickListener listener) {
+ super.onBind(item, listener);
+ if (item == null) return;
+
+ sharedBy.setText(
+ sharedBy.getContext().getString(R.string.groups_created_by,
+ item.getCreator().getAuthor().getName()));
+ }
+
+}
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationGroupAdapter.java b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationGroupAdapter.java
new file mode 100644
index 000000000..7a53d48bd
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationGroupAdapter.java
@@ -0,0 +1,28 @@
+package org.briarproject.android.privategroup.invitation;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import org.briarproject.android.sharing.InvitationAdapter;
+import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
+
+class InvitationGroupAdapter extends
+ InvitationAdapter {
+
+ InvitationGroupAdapter(Context ctx,
+ InvitationClickListener listener) {
+ super(ctx, GroupInvitationItem.class, listener);
+ }
+
+ @Override
+ public GroupInvitationViewHolder onCreateViewHolder(ViewGroup parent,
+ int viewType) {
+ return new GroupInvitationViewHolder(getView(parent));
+ }
+
+ @Override
+ public boolean areContentsTheSame(GroupInvitationItem item1,
+ GroupInvitationItem item2) {
+ return item1.isSubscribed() == item2.isSubscribed();
+ }
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupActivity.java
new file mode 100644
index 000000000..2815e6fb3
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupActivity.java
@@ -0,0 +1,47 @@
+package org.briarproject.android.privategroup.invitation;
+
+import android.content.Context;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.sharing.InvitationAdapter;
+import org.briarproject.android.sharing.InvitationsActivity;
+import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
+
+import javax.inject.Inject;
+
+import static org.briarproject.android.sharing.InvitationAdapter.InvitationClickListener;
+
+public class InvitationsGroupActivity
+ extends InvitationsActivity {
+
+ @Inject
+ protected InvitationsGroupController controller;
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ protected InvitationsGroupController getController() {
+ return controller;
+ }
+
+ @Override
+ protected InvitationAdapter getAdapter(Context ctx,
+ InvitationClickListener listener) {
+ return new InvitationGroupAdapter(ctx, listener);
+ }
+
+ @Override
+ protected int getAcceptRes() {
+ return R.string.groups_invitations_joined;
+ }
+
+ @Override
+ protected int getDeclineRes() {
+ return R.string.groups_invitations_declined;
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupController.java b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupController.java
new file mode 100644
index 000000000..f158064ba
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupController.java
@@ -0,0 +1,8 @@
+package org.briarproject.android.privategroup.invitation;
+
+import org.briarproject.android.sharing.InvitationsController;
+import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
+
+public interface InvitationsGroupController
+ extends InvitationsController {
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupControllerImpl.java
new file mode 100644
index 000000000..0bde16e85
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/invitation/InvitationsGroupControllerImpl.java
@@ -0,0 +1,83 @@
+package org.briarproject.android.privategroup.invitation;
+
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.android.sharing.InvitationsControllerImpl;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.event.GroupInvitationReceivedEvent;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
+import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
+import org.briarproject.api.sync.ClientId;
+
+import java.util.Collection;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+
+public class InvitationsGroupControllerImpl
+ extends InvitationsControllerImpl
+ implements InvitationsGroupController {
+
+ private final PrivateGroupManager privateGroupManager;
+ private final GroupInvitationManager groupInvitationManager;
+
+ @Inject
+ InvitationsGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
+ LifecycleManager lifecycleManager, EventBus eventBus,
+ PrivateGroupManager privateGroupManager,
+ GroupInvitationManager groupInvitationManager) {
+ super(dbExecutor, lifecycleManager, eventBus);
+ this.privateGroupManager = privateGroupManager;
+ this.groupInvitationManager = groupInvitationManager;
+ }
+
+ @Override
+ public void eventOccurred(Event e) {
+ super.eventOccurred(e);
+
+ if (e instanceof GroupInvitationReceivedEvent) {
+ LOG.info("Group invitation received, reloading");
+ listener.loadInvitations(false);
+ }
+ }
+
+ @Override
+ protected ClientId getClientId() {
+ return privateGroupManager.getClientId();
+ }
+
+ @Override
+ protected Collection getInvitations()
+ throws DbException {
+ return groupInvitationManager.getInvitations();
+ }
+
+ @Override
+ public void respondToInvitation(final GroupInvitationItem item,
+ final boolean accept,
+ final ResultExceptionHandler handler) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ PrivateGroup g = (PrivateGroup) item.getShareable();
+ Contact c = item.getCreator();
+ groupInvitationManager.respondToInvitation(g, c, accept);
+ } 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/privategroup/list/GroupListController.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
index fbe05315d..f102f82e4 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListController.java
@@ -30,6 +30,9 @@ public interface GroupListController extends DbController {
void removeGroup(GroupId g,
ResultExceptionHandler result);
+ void loadAvailableGroups(
+ ResultExceptionHandler result);
+
interface GroupListListener extends DestroyableContext {
@UiThread
diff --git a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
index efa36fe5f..76856f244 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListControllerImpl.java
@@ -20,6 +20,7 @@ import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId;
@@ -41,6 +42,7 @@ public class GroupListControllerImpl extends DbControllerImpl
Logger.getLogger(GroupListControllerImpl.class.getName());
private final PrivateGroupManager groupManager;
+ private final GroupInvitationManager groupInvitationManager;
private final EventBus eventBus;
private final AndroidNotificationManager notificationManager;
private final IdentityManager identityManager;
@@ -50,10 +52,12 @@ public class GroupListControllerImpl extends DbControllerImpl
@Inject
GroupListControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, PrivateGroupManager groupManager,
- EventBus eventBus, AndroidNotificationManager notificationManager,
+ GroupInvitationManager groupInvitationManager, EventBus eventBus,
+ AndroidNotificationManager notificationManager,
IdentityManager identityManager) {
super(dbExecutor, lifecycleManager);
this.groupManager = groupManager;
+ this.groupInvitationManager = groupInvitationManager;
this.eventBus = eventBus;
this.notificationManager = notificationManager;
this.identityManager = identityManager;
@@ -187,4 +191,22 @@ public class GroupListControllerImpl extends DbControllerImpl
});
}
+ @Override
+ public void loadAvailableGroups(
+ final ResultExceptionHandler handler) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ handler.onResult(
+ groupInvitationManager.getInvitations().size());
+ } 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/privategroup/list/GroupListFragment.java b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
index 9e77003ac..597e8b120 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
@@ -4,13 +4,16 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.briarproject.R;
@@ -18,6 +21,7 @@ import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.privategroup.creation.CreateGroupActivity;
+import org.briarproject.android.privategroup.invitation.InvitationsGroupActivity;
import org.briarproject.android.privategroup.list.GroupListController.GroupListListener;
import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
import org.briarproject.android.view.BriarRecyclerView;
@@ -30,10 +34,11 @@ import java.util.logging.Logger;
import javax.inject.Inject;
+import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
public class GroupListFragment extends BaseFragment implements
- GroupListListener, OnGroupRemoveClickListener {
+ GroupListListener, OnGroupRemoveClickListener, OnClickListener {
public final static String TAG = GroupListFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@@ -47,6 +52,7 @@ public class GroupListFragment extends BaseFragment implements
private BriarRecyclerView list;
private GroupListAdapter adapter;
+ private Snackbar snackbar;
@Nullable
@Override
@@ -61,6 +67,12 @@ public class GroupListFragment extends BaseFragment implements
list.setLayoutManager(new LinearLayoutManager(getContext()));
list.setAdapter(adapter);
+ snackbar = Snackbar.make(list, "", LENGTH_INDEFINITE);
+ snackbar.getView().setBackgroundResource(R.color.briar_primary);
+ snackbar.setAction(R.string.show, this);
+ snackbar.setActionTextColor(ContextCompat
+ .getColor(getContext(), R.color.briar_button_positive));
+
return v;
}
@@ -76,6 +88,7 @@ public class GroupListFragment extends BaseFragment implements
controller.onStart();
list.startPeriodicUpdate();
loadGroups();
+ loadAvailableGroups();
}
@Override
@@ -180,4 +193,40 @@ public class GroupListFragment extends BaseFragment implements
});
}
+ private void loadAvailableGroups() {
+ controller.loadAvailableGroups(
+ new UiResultExceptionHandler(this) {
+ @Override
+ public void onResultUi(Integer num) {
+ if (num == 0) {
+ snackbar.dismiss();
+ } else {
+ snackbar.setText(getResources().getQuantityString(
+ R.plurals.groups_invitations_open, num,
+ num));
+ if (!snackbar.isShownOrQueued()) snackbar.show();
+ }
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO handle this error
+ finish();
+ }
+ });
+ }
+
+ /**
+ * This method is handling the available groups snackbar action
+ */
+ @Override
+ public void onClick(View v) {
+ Intent i = new Intent(getContext(), InvitationsGroupActivity.class);
+ ActivityOptionsCompat options =
+ makeCustomAnimation(getActivity(),
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right);
+ startActivity(i, options.toBundle());
+ }
+
}