diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 1bb12bfff..aad00c6d9 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -100,6 +100,17 @@
/>
+
+
+
+
-
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_create_group.xml b/briar-android/res/layout/fragment_create_group.xml
new file mode 100644
index 000000000..a9601e2ab
--- /dev/null
+++ b/briar-android/res/layout/fragment_create_group.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
diff --git a/briar-android/res/layout/fragment_message.xml b/briar-android/res/layout/fragment_message.xml
new file mode 100644
index 000000000..168ff34a4
--- /dev/null
+++ b/briar-android/res/layout/fragment_message.xml
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/briar-android/res/layout/fragment_share_message.xml b/briar-android/res/layout/fragment_share_message.xml
deleted file mode 100644
index 45b33c0ba..000000000
--- a/briar-android/res/layout/fragment_share_message.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/briar-android/res/menu/forum_share_actions.xml b/briar-android/res/menu/contact_selection_actions.xml
similarity index 73%
rename from briar-android/res/menu/forum_share_actions.xml
rename to briar-android/res/menu/contact_selection_actions.xml
index a3d6c1547..fbef25059 100644
--- a/briar-android/res/menu/forum_share_actions.xml
+++ b/briar-android/res/menu/contact_selection_actions.xml
@@ -4,9 +4,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
\ No newline at end of file
diff --git a/briar-android/res/menu/groups_list_actions.xml b/briar-android/res/menu/groups_list_actions.xml
index 511224ff1..16eeba462 100644
--- a/briar-android/res/menu/groups_list_actions.xml
+++ b/briar-android/res/menu/groups_list_actions.xml
@@ -6,7 +6,7 @@
\ No newline at end of file
diff --git a/briar-android/res/values-de/strings.xml b/briar-android/res/values-de/strings.xml
index 2967f16b1..e3f453755 100644
--- a/briar-android/res/values-de/strings.xml
+++ b/briar-android/res/values-de/strings.xml
@@ -171,7 +171,7 @@
Forum wurde verlassen
Forum teilen
- Teile dieses Forum mit den gewählten Kontakten
+ Teile dieses Forum mit den gewählten Kontakten
Kontakte auswählen
Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.
Forum mit gewählten Kontakten geteilt
diff --git a/briar-android/res/values-es/strings.xml b/briar-android/res/values-es/strings.xml
index 521fbd9f8..63135b3bf 100644
--- a/briar-android/res/values-es/strings.xml
+++ b/briar-android/res/values-es/strings.xml
@@ -159,7 +159,7 @@
Foro abandonado
Compartir foro
- Compartir este foro con los contactos seleccionados
+ Compartir este foro con los contactos seleccionados
Elige contactos
Parece que eres nuevo aquí y no tienes contactos aún.\n\nPor favor, vuelve cuando hayas añadido tu primer contacto.
Foro compartido con los contactos seleccionados
diff --git a/briar-android/res/values-it/strings.xml b/briar-android/res/values-it/strings.xml
index 28a764499..e7b7a678f 100644
--- a/briar-android/res/values-it/strings.xml
+++ b/briar-android/res/values-it/strings.xml
@@ -136,7 +136,7 @@
Forum lasciato
Condividi Forum
- Condividi questo forum con i contatti scelti
+ Condividi questo forum con i contatti scelti
Scegli Contatti
Forum condiviso con i contatti scelti
C\'è stato un errore nella condivisione di questo forum.
diff --git a/briar-android/res/values-pt-rBR/strings.xml b/briar-android/res/values-pt-rBR/strings.xml
index 4fd4eb8c7..75d4cc14b 100644
--- a/briar-android/res/values-pt-rBR/strings.xml
+++ b/briar-android/res/values-pt-rBR/strings.xml
@@ -171,7 +171,7 @@ Se sentido sozinho aqui? Compartilhe esse fórum com seus contatos!
Saiu do fórum
Compartilhar fórum
- Compartilhar este fórum com os contatos escolhidos
+ Compartilhar este fórum com os contatos escolhidos
Escolher contatos
Parece que você é novo aqui e não tem nenhum contato ainda.
Por favor volte aqui depois de adicionar um contato.
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index b1652f5fb..872800c9f 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -155,8 +155,11 @@
This group is empty
This group is dissolved
Remove
- Add Private Group
+ Create Private Group
This group is empty.\n\nYou can use the pen icon at the top to compose the first message.
+ Create Group
+ Send Invitation
+ Add a name for your private group
Compose Message
Message sent
Message received
@@ -195,7 +198,7 @@
Share Forum
- Share this forum with chosen contacts
+ Contacts selected
Choose Contacts
It seems that you are new here and have no contacts yet.\n\nPlease come back here after you added your first contact.
Forum shared with chosen contacts
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 4e66c87ab..1179905b6 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -28,7 +28,10 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity;
+import org.briarproject.android.privategroup.creation.CreateGroupActivity;
+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.sharing.ContactSelectorFragment;
import org.briarproject.android.sharing.InvitationsBlogActivity;
@@ -73,6 +76,8 @@ public interface ActivityComponent {
void inject(InvitationsBlogActivity activity);
+ void inject(CreateGroupActivity activity);
+
void inject(GroupActivity activity);
void inject(CreateForumActivity activity);
@@ -118,6 +123,8 @@ public interface ActivityComponent {
// Fragments
void inject(ContactListFragment fragment);
+ void inject(CreateGroupFragment fragment);
+ void inject(CreateGroupMessageFragment fragment);
void inject(GroupListFragment fragment);
void inject(ForumListFragment fragment);
void inject(FeedFragment fragment);
diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java
index 07cace566..69505d32f 100644
--- a/briar-android/src/org/briarproject/android/ActivityModule.java
+++ b/briar-android/src/org/briarproject/android/ActivityModule.java
@@ -23,6 +23,8 @@ import org.briarproject.android.forum.ForumController;
import org.briarproject.android.forum.ForumControllerImpl;
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.list.GroupListController;
import org.briarproject.android.privategroup.list.GroupListControllerImpl;
@@ -101,6 +103,13 @@ public class ActivityModule {
return groupListController;
}
+ @ActivityScope
+ @Provides
+ protected CreateGroupController provideCreateGroupController(
+ CreateGroupControllerImpl createGroupController) {
+ return createGroupController;
+ }
+
@ActivityScope
@Provides
protected GroupController provideGroupController(
diff --git a/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
index 04ee0a747..9e2db81dc 100644
--- a/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BasePostFragment.java
@@ -5,7 +5,6 @@ import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
@@ -36,8 +35,6 @@ abstract class BasePostFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
-
view = inflater.inflate(R.layout.fragment_blog_post, container,
false);
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
@@ -60,17 +57,6 @@ abstract class BasePostFragment extends BaseFragment {
stopPeriodicUpdate();
}
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- getActivity().onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
@UiThread
protected void onBlogPostLoaded(BlogPostItem post) {
progressBar.setVisibility(INVISIBLE);
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
index eb6552091..8809864d5 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogFragment.java
@@ -80,9 +80,6 @@ public class BlogFragment extends BaseFragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
-
- setHasOptionsMenu(true);
-
Bundle args = getArguments();
byte[] b = args.getByteArray(GROUP_ID);
if (b == null) throw new IllegalStateException("No group ID in args");
@@ -151,9 +148,6 @@ public class BlogFragment extends BaseFragment implements
android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
switch (item.getItemId()) {
- case android.R.id.home:
- getActivity().onBackPressed();
- return true;
case R.id.action_write_blog_post:
Intent i = new Intent(getActivity(),
WriteBlogPostActivity.class);
diff --git a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
index 9545f34ff..4be0bc541 100644
--- a/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/FeedFragment.java
@@ -66,7 +66,6 @@ public class FeedFragment extends BaseFragment implements
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
View v = inflater.inflate(R.layout.fragment_blog, container, false);
adapter = new BlogPostAdapter(getActivity(), this);
diff --git a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
index e3d50dfee..479fecaad 100644
--- a/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
+++ b/briar-android/src/org/briarproject/android/blogs/ReblogFragment.java
@@ -72,8 +72,6 @@ public class ReblogFragment extends BaseFragment implements TextInputListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
-
Bundle args = getArguments();
blogId = new GroupId(args.getByteArray(GROUP_ID));
postId = new MessageId(args.getByteArray(POST_ID));
diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
index 474b2cc2b..fe1e62a4d 100644
--- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
+++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java
@@ -106,8 +106,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
-
View contentView =
inflater.inflate(R.layout.list, container,
false);
diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
index e52bc4a89..a5496d9c7 100644
--- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
+++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java
@@ -623,6 +623,7 @@ public class ConversationActivity extends BriarActivity
long timestamp = System.currentTimeMillis();
timestamp = Math.max(timestamp, getMinTimestampForNewMessage());
createMessage(StringUtils.toUtf8(text), timestamp);
+ textInputView.setText("");
}
private long getMinTimestampForNewMessage() {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index 6bbf19657..f1faecd46 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -78,8 +78,6 @@ public class ForumListFragment extends BaseEventFragment implements
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
-
View contentView =
inflater.inflate(R.layout.fragment_forum_list, container,
false);
diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
index 2cb6b13e6..223c01367 100644
--- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
+++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java
@@ -5,6 +5,7 @@ import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
+import android.view.MenuItem;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.DestroyableContext;
@@ -27,6 +28,9 @@ public abstract class BaseFragment extends Fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // allow for "up" button to act as back button
+ setHasOptionsMenu(true);
}
@@ -37,6 +41,17 @@ public abstract class BaseFragment extends Fragment
listener.onFragmentCreated(getUniqueTag());
}
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ listener.onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
@UiThread
protected void finish() {
getActivity().supportFinishAfterTransition();
@@ -47,6 +62,9 @@ public abstract class BaseFragment extends Fragment
@Deprecated
void runOnDbThread(Runnable runnable);
+ @UiThread
+ void onBackPressed();
+
@UiThread
ActivityComponent getActivityComponent();
diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
index 019aff689..f5c9c32a6 100644
--- a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java
@@ -14,6 +14,7 @@ import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.view.TextInputView;
+import org.briarproject.android.view.TextInputView.TextInputListener;
import org.briarproject.api.FormatException;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
@@ -37,7 +38,7 @@ import static java.util.logging.Level.WARNING;
import static org.briarproject.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
public class IntroductionMessageFragment extends BaseFragment
- implements TextInputView.TextInputListener {
+ implements TextInputListener {
public static final String TAG =
IntroductionMessageFragment.class.getName();
@@ -56,7 +57,8 @@ public class IntroductionMessageFragment extends BaseFragment
@Inject
protected volatile IntroductionManager introductionManager;
- public static IntroductionMessageFragment newInstance(int contactId1, int contactId2) {
+ public static IntroductionMessageFragment newInstance(int contactId1,
+ int contactId2) {
Bundle args = new Bundle();
args.putInt(CONTACT_ID_1, contactId1);
args.putInt(CONTACT_ID_2, contactId2);
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
new file mode 100644
index 000000000..355de7c27
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
@@ -0,0 +1,165 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.widget.Toast;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+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.ContactSelectorActivity;
+import org.briarproject.android.sharing.ContactSelectorFragment;
+import org.briarproject.api.contact.Contact;
+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;
+
+import javax.inject.Inject;
+
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.widget.Toast.LENGTH_SHORT;
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
+
+public class CreateGroupActivity extends ContactSelectorActivity implements
+ CreateGroupListener, MessageFragmentListener {
+
+ @Inject
+ CreateGroupController controller;
+
+ @Override
+ public void injectActivity(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ setContentView(R.layout.activity_fragment_container);
+
+ if (bundle == null) {
+ CreateGroupFragment fragment = new CreateGroupFragment();
+ getSupportFragmentManager().beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .commit();
+ } else {
+ byte[] groupBytes = bundle.getByteArray(GROUP_ID);
+ if (groupBytes != null) groupId = new GroupId(groupBytes);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
+ // At this point, the group had been created already,
+ // so don't allow to create it again.
+ openNewGroup();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (groupId != null) {
+ outState.putByteArray(GROUP_ID, groupId.getBytes());
+ }
+ }
+
+ @Override
+ public void onGroupNameChosen(String name) {
+ controller.createGroup(name,
+ new UiResultExceptionHandler(this) {
+ @Override
+ public void onResultUi(GroupId g) {
+ switchToContactSelectorFragment(g);
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO proper error handling
+ finish();
+ }
+ });
+ }
+
+ private void switchToContactSelectorFragment(GroupId g) {
+ ContactSelectorFragment fragment =
+ ContactSelectorFragment.newInstance(g);
+ getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(android.R.anim.fade_in,
+ android.R.anim.fade_out,
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right)
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(fragment.getUniqueTag())
+ .commit();
+ }
+
+ @Override
+ public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+ return false;
+ }
+
+ @Override
+ public void contactsSelected(GroupId groupId,
+ Collection contacts) {
+ super.contactsSelected(groupId, contacts);
+
+ CreateGroupMessageFragment fragment = new CreateGroupMessageFragment();
+ getSupportFragmentManager().beginTransaction()
+ .setCustomAnimations(android.R.anim.fade_in,
+ android.R.anim.fade_out,
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right)
+ .replace(R.id.fragmentContainer, fragment)
+ .addToBackStack(fragment.getUniqueTag())
+ .commit();
+ }
+
+ @Override
+ public boolean onButtonClick(@NotNull String message) {
+ controller.sendInvitation(groupId, contacts, message,
+ new UiResultExceptionHandler(this) {
+ @Override
+ public void onResultUi(Void result) {
+ Toast.makeText(CreateGroupActivity.this,
+ "Inviting members is not yet implemented",
+ LENGTH_SHORT).show();
+ openNewGroup();
+ }
+
+ @Override
+ public void onExceptionUi(DbException exception) {
+ // TODO proper error handling
+ finish();
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public int getMaximumMessageLength() {
+ return MAX_GROUP_INVITATION_MSG_LENGTH;
+ }
+
+ private void openNewGroup() {
+ Intent i = new Intent(this, GroupActivity.class);
+ i.putExtra(GROUP_ID, groupId.getBytes());
+ ActivityOptionsCompat options =
+ makeCustomAnimation(this, android.R.anim.fade_in,
+ android.R.anim.fade_out);
+ ActivityCompat.startActivity(this, i, options.toBundle());
+ // finish this activity, so we can't come back to it
+ finish();
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java
new file mode 100644
index 000000000..da1a00a0d
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java
@@ -0,0 +1,19 @@
+package org.briarproject.android.privategroup.creation;
+
+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 CreateGroupController extends DbController {
+
+ void createGroup(String name,
+ ResultExceptionHandler result);
+
+ void sendInvitation(GroupId groupId, Collection contacts,
+ String message, ResultExceptionHandler 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
new file mode 100644
index 000000000..a35c2ac20
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
@@ -0,0 +1,68 @@
+package org.briarproject.android.privategroup.creation;
+
+import org.briarproject.android.controller.DbControllerImpl;
+import org.briarproject.android.controller.handler.ResultExceptionHandler;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.db.DatabaseExecutor;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.PrivateGroupManager;
+import org.briarproject.api.sync.GroupId;
+
+import java.util.Collection;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+
+public class CreateGroupControllerImpl extends DbControllerImpl
+ implements CreateGroupController {
+
+ private static final Logger LOG =
+ Logger.getLogger(CreateGroupControllerImpl.class.getName());
+
+ private final PrivateGroupManager groupManager;
+
+ @Inject
+ CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
+ LifecycleManager lifecycleManager,
+ PrivateGroupManager groupManager) {
+ super(dbExecutor, lifecycleManager);
+ this.groupManager = groupManager;
+ }
+
+ @Override
+ public void createGroup(final String name,
+ final ResultExceptionHandler handler) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ LOG.info("Adding group to database...");
+ try {
+ handler.onResult(groupManager.addPrivateGroup(name));
+ } catch (DbException e) {
+ if (LOG.isLoggable(WARNING))
+ LOG.log(WARNING, e.toString(), e);
+ handler.onException(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void sendInvitation(final GroupId groupId,
+ final Collection contacts, final String message,
+ final ResultExceptionHandler result) {
+ runOnDbThread(new Runnable() {
+ @Override
+ public void run() {
+ // TODO actually send invitation
+ //noinspection ConstantConditions
+ result.onResult(null);
+ }
+ });
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java
new file mode 100644
index 000000000..7f21508e3
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java
@@ -0,0 +1,93 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.fragment.BaseFragment;
+
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
+
+public class CreateGroupFragment extends BaseFragment {
+
+ public final static String TAG = CreateGroupFragment.class.getName();
+
+ private CreateGroupListener listener;
+ private EditText name;
+ private Button button;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ listener = (CreateGroupListener) context;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ // inflate view
+ View v = inflater.inflate(R.layout.fragment_create_group, container,
+ false);
+ name = (EditText) v.findViewById(R.id.name);
+ name.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count,
+ int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ validateName();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ button = (Button) v.findViewById(R.id.button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.hideSoftKeyboard(name);
+ listener.onGroupNameChosen(name.getText().toString());
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ listener.showSoftKeyboard(name);
+ }
+
+ @Override
+ public void injectFragment(ActivityComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+ private void validateName() {
+ String name = this.name.getText().toString();
+ if (name.length() < 1 || name.length() > MAX_GROUP_NAME_LENGTH)
+ button.setEnabled(false);
+ else if(!button.isEnabled())
+ button.setEnabled(true);
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java
new file mode 100644
index 000000000..3347be158
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java
@@ -0,0 +1,15 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.view.View;
+
+import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
+
+interface CreateGroupListener extends BaseFragmentListener {
+
+ void onGroupNameChosen(String name);
+
+ void showSoftKeyboard(View view);
+
+ void hideSoftKeyboard(View view);
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java
new file mode 100644
index 000000000..a261ce016
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java
@@ -0,0 +1,36 @@
+package org.briarproject.android.privategroup.creation;
+
+import android.support.annotation.StringRes;
+
+import org.briarproject.R;
+import org.briarproject.android.ActivityComponent;
+import org.briarproject.android.sharing.BaseMessageFragment;
+
+public class CreateGroupMessageFragment extends BaseMessageFragment {
+
+ private final static String TAG =
+ CreateGroupMessageFragment.class.getName();
+
+ @Override
+ @StringRes
+ protected int getButtonText() {
+ return R.string.groups_create_group_invitation_button;
+ }
+
+ @Override
+ @StringRes
+ protected int getHintText() {
+ return R.string.forum_share_message;
+ }
+
+ @Override
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+ @Override
+ public void injectFragment(ActivityComponent component) {
+ component.inject(this);
+ }
+
+}
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 4489dd6bf..9e77003ac 100644
--- a/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
+++ b/briar-android/src/org/briarproject/android/privategroup/list/GroupListFragment.java
@@ -1,8 +1,10 @@
package org.briarproject.android.privategroup.list;
+import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
+import android.support.v4.app.ActivityOptionsCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -15,6 +17,7 @@ import org.briarproject.R;
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.list.GroupListController.GroupListListener;
import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
import org.briarproject.android.view.BriarRecyclerView;
@@ -27,6 +30,8 @@ import java.util.logging.Logger;
import javax.inject.Inject;
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+
public class GroupListFragment extends BaseFragment implements
GroupListListener, OnGroupRemoveClickListener {
@@ -48,8 +53,6 @@ public class GroupListFragment extends BaseFragment implements
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- setHasOptionsMenu(true);
-
View v = inflater.inflate(R.layout.list, container, false);
adapter = new GroupListAdapter(getContext(), this);
@@ -94,7 +97,12 @@ public class GroupListFragment extends BaseFragment implements
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_add_group:
- // TODO
+ Intent i = new Intent(getContext(), CreateGroupActivity.class);
+ ActivityOptionsCompat options =
+ makeCustomAnimation(getActivity(),
+ android.R.anim.slide_in_left,
+ android.R.anim.slide_out_right);
+ startActivity(i, options.toBundle());
return true;
default:
return super.onOptionsItemSelected(item);
diff --git a/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
new file mode 100644
index 000000000..eb05d19bc
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
@@ -0,0 +1,98 @@
+package org.briarproject.android.sharing;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.support.design.widget.Snackbar;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.briarproject.R;
+import org.briarproject.android.fragment.BaseFragment;
+import org.briarproject.android.view.LargeTextInputView;
+import org.briarproject.android.view.TextInputView.TextInputListener;
+import org.briarproject.api.nullsafety.NotNullByDefault;
+import org.briarproject.util.StringUtils;
+
+import static android.support.design.widget.Snackbar.LENGTH_SHORT;
+import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
+import static org.briarproject.util.StringUtils.truncateUtf8;
+
+public abstract class BaseMessageFragment extends BaseFragment
+ implements TextInputListener {
+
+ protected LargeTextInputView message;
+ private MessageFragmentListener listener;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ listener = (MessageFragmentListener) context;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ // inflate view
+ View v = inflater.inflate(R.layout.fragment_message, container,
+ false);
+ message = (LargeTextInputView) v.findViewById(R.id.messageView);
+ message.setButtonText(getString(getButtonText()));
+ message.setHint(getHintText());
+ message.setListener(this);
+
+ return v;
+ }
+
+ protected void setTitle(int res) {
+ listener.setTitle(res);
+ }
+
+ @StringRes
+ protected abstract int getButtonText();
+ @StringRes
+ protected abstract int getHintText();
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ message.showSoftKeyboard();
+ }
+
+ @Override
+ public void onSendClick(String msg) {
+ if (StringUtils.isTooLong(msg, listener.getMaximumMessageLength())) {
+ Snackbar.make(message, R.string.text_too_long, LENGTH_SHORT).show();
+ return;
+ }
+
+ // disable button to prevent accidental double actions
+ message.setSendButtonEnabled(false);
+ message.hideSoftKeyboard();
+
+ msg = truncateUtf8(msg, MAX_INVITATION_MESSAGE_LENGTH);
+ if(!listener.onButtonClick(msg)) {
+ message.setSendButtonEnabled(true);
+ message.showSoftKeyboard();
+ }
+ }
+
+ @UiThread
+ @NotNullByDefault
+ public interface MessageFragmentListener {
+
+ void onBackPressed();
+
+ void setTitle(@StringRes int titleRes);
+
+ /** Returns true when the button click has been consumed. */
+ boolean onButtonClick(String message);
+
+ int getMaximumMessageLength();
+
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
new file mode 100644
index 000000000..2dad460ea
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorActivity.java
@@ -0,0 +1,90 @@
+package org.briarproject.android.sharing;
+
+import android.os.Bundle;
+import android.support.annotation.CallSuper;
+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.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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public abstract class ContactSelectorActivity extends BriarActivity implements
+ BaseFragmentListener, ContactSelectorListener {
+
+ final static String CONTACTS = "contacts";
+
+ protected GroupId groupId;
+ protected Collection contacts;
+
+ @Override
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ setContentView(R.layout.activity_fragment_container);
+
+ if (bundle != null) {
+ ArrayList intContacts =
+ bundle.getIntegerArrayList(CONTACTS);
+ if (intContacts != null) {
+ contacts = getContactsFromIntegers(intContacts);
+ }
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ if (contacts != null) {
+ outState.putIntegerArrayList(CONTACTS,
+ getContactsFromIds(contacts));
+ }
+ }
+
+ @CallSuper
+ @UiThread
+ @Override
+ public void contactsSelected(GroupId groupId,
+ Collection contacts) {
+ this.groupId = groupId;
+ this.contacts = contacts;
+ }
+
+ @DatabaseExecutor
+ public abstract boolean isDisabled(GroupId groupId, Contact c)
+ throws DbException;
+
+ static ArrayList getContactsFromIds(
+ Collection contacts) {
+ // transform ContactIds to Integers so they can be added to a bundle
+ ArrayList intContacts = new ArrayList<>(contacts.size());
+ for (ContactId contactId : contacts) {
+ intContacts.add(contactId.getInt());
+ }
+ return intContacts;
+ }
+
+ static Collection getContactsFromIntegers(
+ ArrayList intContacts) {
+ // turn contact integers from a bundle back to ContactIds
+ List contacts = new ArrayList<>(intContacts.size());
+ for (Integer c : intContacts) {
+ contacts.add(new ContactId(c));
+ }
+ return contacts;
+ }
+
+ @Override
+ public void onFragmentCreated(String tag) {
+
+ }
+
+}
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
index 82db75adf..03e4da397 100644
--- a/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorFragment.java
@@ -23,7 +23,6 @@ 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.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId;
@@ -48,7 +47,6 @@ public class ContactSelectorFragment extends BaseFragment implements
public static final String TAG = ContactSelectorFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
- private ShareActivity shareActivity;
private Menu menu;
private BriarRecyclerView list;
private ContactSelectorAdapter adapter;
@@ -59,13 +57,11 @@ public class ContactSelectorFragment extends BaseFragment implements
volatile ContactManager contactManager;
@Inject
volatile IdentityManager identityManager;
- @Inject
- volatile ForumSharingManager forumSharingManager;
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();
@@ -81,14 +77,13 @@ public class ContactSelectorFragment extends BaseFragment implements
@Override
public void onAttach(Context context) {
super.onAttach(context);
- shareActivity = (ShareActivity) context;
+ listener = (ContactSelectorListener) context;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setHasOptionsMenu(true);
Bundle args = getArguments();
byte[] b = args.getByteArray(GROUP_ID);
if (b == null) throw new IllegalStateException("No GroupId");
@@ -139,6 +134,7 @@ public class ContactSelectorFragment extends BaseFragment implements
@Override
public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
if (adapter != null) {
selectedContacts = adapter.getSelectedContactIds();
outState.putIntegerArrayList(CONTACTS,
@@ -148,7 +144,7 @@ public class ContactSelectorFragment extends BaseFragment implements
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- inflater.inflate(R.menu.forum_share_actions, menu);
+ inflater.inflate(R.menu.contact_selection_actions, menu);
super.onCreateOptionsMenu(menu, inflater);
this.menu = menu;
// hide sharing action initially, if no contact is selected
@@ -159,12 +155,9 @@ public class ContactSelectorFragment extends BaseFragment implements
public boolean onOptionsItemSelected(final MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
- case android.R.id.home:
- shareActivity.onBackPressed();
- return true;
- case R.id.action_share_forum:
+ case R.id.action_contacts_selected:
selectedContacts = adapter.getSelectedContactIds();
- shareActivity.showMessageScreen(groupId, selectedContacts);
+ listener.contactsSelected(groupId, selectedContacts);
return true;
default:
return super.onOptionsItemSelected(item);
@@ -185,7 +178,7 @@ public class ContactSelectorFragment extends BaseFragment implements
}
private void loadContacts(@Nullable final Collection selection) {
- shareActivity.runOnDbThread(new Runnable() {
+ listener.runOnDbThread(new Runnable() {
@Override
public void run() {
try {
@@ -199,7 +192,7 @@ public class ContactSelectorFragment extends BaseFragment implements
boolean selected = selection != null &&
selection.contains(c.getId());
// do we have already some sharing with that contact?
- boolean disabled = shareActivity.isDisabled(groupId, c);
+ boolean disabled = listener.isDisabled(groupId, c);
contacts.add(new SelectableContactListItem(c,
localAuthor, groupId, selected, disabled));
}
@@ -216,7 +209,7 @@ public class ContactSelectorFragment extends BaseFragment implements
}
private void displayContacts(final List contacts) {
- shareActivity.runOnUiThreadUnlessDestroyed(new Runnable() {
+ listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
if (contacts.isEmpty()) list.showData();
@@ -228,7 +221,7 @@ public class ContactSelectorFragment extends BaseFragment implements
private void updateMenuItem() {
if (menu == null) return;
- MenuItem item = menu.findItem(R.id.action_share_forum);
+ MenuItem item = menu.findItem(R.id.action_contacts_selected);
if (item == null) return;
selectedContacts = adapter.getSelectedContactIds();
diff --git a/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
new file mode 100644
index 000000000..8016c45e9
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/sharing/ContactSelectorListener.java
@@ -0,0 +1,28 @@
+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;
+
+public interface ContactSelectorListener extends DestroyableContext {
+
+ @Deprecated
+ void runOnDbThread(Runnable runnable);
+
+ @DatabaseExecutor
+ boolean isDisabled(GroupId groupId, Contact c) throws DbException;
+
+ @UiThread
+ void contactsSelected(GroupId groupId, Collection 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 d79a7ed2e..e89fe277e 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareActivity.java
@@ -2,96 +2,110 @@ package org.briarproject.android.sharing;
import android.content.Intent;
import android.os.Bundle;
-import android.view.View;
+import android.support.annotation.StringRes;
+import android.support.annotation.UiThread;
+import android.widget.Toast;
import org.briarproject.R;
-import org.briarproject.android.BriarActivity;
-import org.briarproject.android.fragment.BaseFragment;
-import org.briarproject.api.contact.Contact;
+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.ArrayList;
import java.util.Collection;
-import java.util.List;
+import java.util.logging.Logger;
-public abstract class ShareActivity extends BriarActivity implements
- BaseFragment.BaseFragmentListener {
+import static android.widget.Toast.LENGTH_SHORT;
+import static java.util.logging.Level.WARNING;
- final static String CONTACTS = "contacts";
+public abstract class ShareActivity extends ContactSelectorActivity implements
+ MessageFragmentListener {
+
+ private final static Logger LOG =
+ Logger.getLogger(ShareActivity.class.getName());
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.activity_share);
+ public void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID);
if (b == null) throw new IllegalStateException("No GroupId");
- GroupId groupId = new GroupId(b);
+ groupId = new GroupId(b);
- if (savedInstanceState == null) {
+ if (bundle == null) {
ContactSelectorFragment contactSelectorFragment =
ContactSelectorFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
- .add(R.id.shareContainer, contactSelectorFragment)
+ .add(R.id.fragmentContainer, contactSelectorFragment)
.commit();
}
}
- abstract ShareMessageFragment getMessageFragment(GroupId groupId,
- Collection contacts);
-
- abstract boolean isDisabled(GroupId groupId, Contact c) throws DbException;
-
- void showMessageScreen(GroupId groupId, Collection contacts) {
- ShareMessageFragment messageFragment =
- getMessageFragment(groupId, contacts);
+ @UiThread
+ @Override
+ public void contactsSelected(GroupId groupId,
+ Collection contacts) {
+ super.contactsSelected(groupId, contacts);
+ BaseMessageFragment messageFragment = getMessageFragment();
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(android.R.anim.fade_in,
android.R.anim.fade_out,
android.R.anim.slide_in_left,
android.R.anim.slide_out_right)
- .replace(R.id.shareContainer, messageFragment,
+ .replace(R.id.fragmentContainer, messageFragment,
ContactSelectorFragment.TAG)
.addToBackStack(null)
.commit();
}
- static ArrayList getContactsFromIds(
- Collection contacts) {
-
- // transform ContactIds to Integers so they can be added to a bundle
- ArrayList intContacts = new ArrayList<>(contacts.size());
- for (ContactId contactId : contacts) {
- intContacts.add(contactId.getInt());
- }
- return intContacts;
- }
-
- void sharingSuccessful(View v) {
- setResult(RESULT_OK);
- hideSoftKeyboard(v);
- supportFinishAfterTransition();
- }
-
- static Collection getContactsFromIntegers(
- ArrayList intContacts) {
-
- // turn contact integers from a bundle back to ContactIds
- List contacts = new ArrayList<>(intContacts.size());
- for (Integer c : intContacts) {
- contacts.add(new ContactId(c));
- }
- return contacts;
- }
+ abstract BaseMessageFragment getMessageFragment();
+ @UiThread
@Override
- public void onFragmentCreated(String tag) {
-
+ public boolean onButtonClick(@NotNull String message) {
+ share(groupId, contacts, message);
+ setResult(RESULT_OK);
+ supportFinishAfterTransition();
+ return true;
}
+ private void share(final GroupId g, final Collection 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();
+
}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
index 696ea1452..7d5cdeef3 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogActivity.java
@@ -1,5 +1,6 @@
package org.briarproject.android.sharing;
+import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.contact.Contact;
@@ -7,18 +8,19 @@ 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 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;
- ShareMessageFragment getMessageFragment(GroupId groupId,
- Collection contacts) {
- return ShareBlogMessageFragment.newInstance(groupId, contacts);
+ @Override
+ BaseMessageFragment getMessageFragment() {
+ return ShareBlogMessageFragment.newInstance();
}
@Override
@@ -26,10 +28,24 @@ public class ShareBlogActivity extends ShareActivity {
component.inject(this);
}
- /**
- * This must only be called from a DbThread
- */
- boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+ @Override
+ public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !blogSharingManager.canBeShared(groupId, c);
}
+
+ @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;
+ }
+
+ @Override
+ public int getMaximumMessageLength() {
+ return MAX_MESSAGE_BODY_LENGTH;
+ }
}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
index d9f944ab0..ccee4d8fe 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareBlogMessageFragment.java
@@ -1,41 +1,20 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
+import android.support.annotation.StringRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
-import org.briarproject.api.blogs.BlogSharingManager;
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.db.DbException;
-import org.briarproject.api.sync.GroupId;
-import java.util.Collection;
-import java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import static android.widget.Toast.LENGTH_SHORT;
-import static java.util.logging.Level.WARNING;
-
-public class ShareBlogMessageFragment extends ShareMessageFragment {
+public class ShareBlogMessageFragment extends BaseMessageFragment {
public final static String TAG = ShareBlogMessageFragment.class.getName();
- private static final Logger LOG = Logger.getLogger(TAG);
- // Fields that are accessed from background threads must be volatile
- @Inject
- protected volatile BlogSharingManager blogSharingManager;
-
- public static ShareBlogMessageFragment newInstance(GroupId groupId,
- Collection contacts) {
-
- ShareBlogMessageFragment fragment = new ShareBlogMessageFragment();
- fragment.setArguments(getArguments(groupId, contacts));
- return fragment;
+ public static ShareBlogMessageFragment newInstance() {
+ return new ShareBlogMessageFragment();
}
@Override
@@ -43,10 +22,19 @@ public class ShareBlogMessageFragment extends ShareMessageFragment {
Bundle savedInstanceState) {
setTitle(R.string.blogs_sharing_share);
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
- View v = super.onCreateView(inflater, container, savedInstanceState);
- ui.message.setButtonText(getString(R.string.blogs_sharing_button));
- return v;
+ @Override
+ @StringRes
+ protected int getButtonText() {
+ return R.string.blogs_sharing_button;
+ }
+
+ @Override
+ @StringRes
+ protected int getHintText() {
+ return R.string.forum_share_message;
}
@Override
@@ -59,32 +47,4 @@ public class ShareBlogMessageFragment extends ShareMessageFragment {
return TAG;
}
- @Override
- protected void share(final String msg) {
- listener.runOnDbThread(new Runnable() {
- @Override
- public void run() {
- try {
- for (ContactId c : getContacts()) {
- blogSharingManager.sendInvitation(getGroupId(), c, msg);
- }
- } catch (DbException e) {
- sharingError();
- if (LOG.isLoggable(WARNING))
- LOG.log(WARNING, e.toString(), e);
- }
- }
- });
- }
-
- @Override
- protected void sharingError() {
- listener.runOnUiThreadUnlessDestroyed(new Runnable() {
- @Override
- public void run() {
- int res = R.string.blogs_sharing_error;
- Toast.makeText(getContext(), res, LENGTH_SHORT).show();
- }
- });
- }
}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
index 51a5d5a22..492e3615b 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareForumActivity.java
@@ -1,5 +1,6 @@
package org.briarproject.android.sharing;
+import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
@@ -7,17 +8,19 @@ 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 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;
- ShareMessageFragment getMessageFragment(GroupId groupId,
- Collection contacts) {
- return ShareForumMessageFragment.newInstance(groupId, contacts);
+ @Override
+ BaseMessageFragment getMessageFragment() {
+ return ShareForumMessageFragment.newInstance();
}
@Override
@@ -25,10 +28,24 @@ public class ShareForumActivity extends ShareActivity {
component.inject(this);
}
- /**
- * This must only be called from a DbThread
- */
- boolean isDisabled(GroupId groupId, Contact c) throws DbException {
+ @Override
+ public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
return !forumSharingManager.canBeShared(groupId, c);
}
+
+ @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;
+ }
+
+ @Override
+ public int getMaximumMessageLength() {
+ return MAX_MESSAGE_BODY_LENGTH;
+ }
}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
index 8414096f0..a3faeb630 100644
--- a/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/ShareForumMessageFragment.java
@@ -1,41 +1,20 @@
package org.briarproject.android.sharing;
import android.os.Bundle;
+import android.support.annotation.StringRes;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
-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 java.util.logging.Logger;
-
-import javax.inject.Inject;
-
-import static android.widget.Toast.LENGTH_SHORT;
-import static java.util.logging.Level.WARNING;
-
-public class ShareForumMessageFragment extends ShareMessageFragment {
+public class ShareForumMessageFragment extends BaseMessageFragment {
public final static String TAG = ShareForumMessageFragment.class.getName();
- private static final Logger LOG = Logger.getLogger(TAG);
- // Fields that are accessed from background threads must be volatile
- @Inject
- protected volatile ForumSharingManager forumSharingManager;
-
- public static ShareForumMessageFragment newInstance(GroupId groupId,
- Collection contacts) {
-
- ShareForumMessageFragment fragment = new ShareForumMessageFragment();
- fragment.setArguments(getArguments(groupId, contacts));
- return fragment;
+ public static ShareForumMessageFragment newInstance() {
+ return new ShareForumMessageFragment();
}
@Override
@@ -46,6 +25,18 @@ public class ShareForumMessageFragment extends ShareMessageFragment {
return super.onCreateView(inflater, container, savedInstanceState);
}
+ @Override
+ @StringRes
+ protected int getButtonText() {
+ return R.string.forum_share_button;
+ }
+
+ @Override
+ @StringRes
+ protected int getHintText() {
+ return R.string.forum_share_message;
+ }
+
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
@@ -56,33 +47,4 @@ public class ShareForumMessageFragment extends ShareMessageFragment {
return TAG;
}
- @Override
- protected void share(final String msg) {
- listener.runOnDbThread(new Runnable() {
- @Override
- public void run() {
- try {
- for (ContactId c : getContacts()) {
- forumSharingManager.
- sendInvitation(getGroupId(), c, msg);
- }
- } catch (DbException e) {
- sharingError();
- if (LOG.isLoggable(WARNING))
- LOG.log(WARNING, e.toString(), e);
- }
- }
- });
- }
-
- @Override
- protected void sharingError() {
- listener.runOnUiThreadUnlessDestroyed(new Runnable() {
- @Override
- public void run() {
- int res = R.string.forum_share_error;
- Toast.makeText(getContext(), res, LENGTH_SHORT).show();
- }
- });
- }
}
diff --git a/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java b/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java
deleted file mode 100644
index ea79398cf..000000000
--- a/briar-android/src/org/briarproject/android/sharing/ShareMessageFragment.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package org.briarproject.android.sharing;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-
-import org.briarproject.R;
-import org.briarproject.android.fragment.BaseFragment;
-import org.briarproject.android.view.LargeTextInputView;
-import org.briarproject.android.view.TextInputView.TextInputListener;
-import org.briarproject.api.blogs.BlogSharingManager;
-import org.briarproject.api.contact.ContactId;
-import org.briarproject.api.forum.ForumSharingManager;
-import org.briarproject.api.sync.GroupId;
-import org.briarproject.util.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Collection;
-
-import javax.inject.Inject;
-
-import static org.briarproject.android.sharing.ShareActivity.CONTACTS;
-import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
-import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
-import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
-
-abstract class ShareMessageFragment extends BaseFragment
- implements TextInputListener {
-
- protected ViewHolder ui;
- private ShareActivity shareActivity;
-
- // Fields that are accessed from background threads must be volatile
- @Inject
- protected volatile ForumSharingManager forumSharingManager;
- @Inject
- protected volatile BlogSharingManager blogSharingManager;
- private volatile GroupId groupId;
- private volatile Collection contacts;
-
- protected static Bundle getArguments(GroupId groupId,
- Collection contacts) {
-
- Bundle args = new Bundle();
- args.putByteArray(GROUP_ID, groupId.getBytes());
- args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
- return args;
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- shareActivity = (ShareActivity) context;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- // allow for "up" button to act as back button
- setHasOptionsMenu(true);
-
- // get groupID and contactIDs from fragment arguments
- groupId = new GroupId(getArguments().getByteArray(GROUP_ID));
- ArrayList intContacts =
- getArguments().getIntegerArrayList(CONTACTS);
- if (intContacts == null) throw new IllegalArgumentException();
- contacts = ShareActivity.getContactsFromIntegers(intContacts);
-
- // inflate view
- View v = inflater.inflate(R.layout.fragment_share_message, container,
- false);
- ui = new ViewHolder(v);
- ui.message.setListener(this);
-
- return v;
- }
-
- @Override
- public void onStart() {
- super.onStart();
- ui.message.showSoftKeyboard();
- }
-
- @Override
- public boolean onOptionsItemSelected(final MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- shareActivity.onBackPressed();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- protected void setTitle(int res) {
- shareActivity.setTitle(res);
- }
-
- @Override
- public void onSendClick(String msg) {
- // disable button to prevent accidental double invitations
- ui.message.setSendButtonEnabled(false);
-
- msg = StringUtils.truncateUtf8(msg, MAX_INVITATION_MESSAGE_LENGTH);
- share(msg);
-
- // don't wait for the invitation to be made before finishing activity
- shareActivity.sharingSuccessful(ui.message);
- }
-
- abstract void share(final String msg);
-
- abstract void sharingError();
-
- protected Collection getContacts() {
- return contacts;
- }
-
- protected GroupId getGroupId() {
- return groupId;
- }
-
- protected static class ViewHolder {
- protected final LargeTextInputView message;
-
- private ViewHolder(View v) {
- message = (LargeTextInputView) v
- .findViewById(R.id.invitationMessageView);
- }
- }
-}
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
index 6a1ec4883..a8eefd250 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListActivity.java
@@ -253,6 +253,7 @@ public abstract class ThreadListActivity getPrivateGroups() throws DbException {
- return Collections.emptyList();
+ Collection groups;
+ Transaction txn = db.startTransaction(true);
+ try {
+ groups = db.getGroups(txn, getClientId());
+ txn.setComplete();
+ } finally {
+ db.endTransaction(txn);
+ }
+ try {
+ Collection privateGroups =
+ new ArrayList(groups.size());
+ for (Group g : groups) {
+ privateGroups.add(privateGroupFactory.parsePrivateGroup(g));
+ }
+ return privateGroups;
+ } catch (FormatException e) {
+ throw new DbException(e);
+ }
}
@Override