From 1a812f13272578317da6c9db3a9501f5a45a1800 Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Thu, 13 Oct 2016 12:28:04 -0300
Subject: [PATCH] UI for creating private groups

---
 briar-android/AndroidManifest.xml             |  11 ++
 .../res/layout/fragment_create_group.xml      |  23 +++
 .../res/menu/groups_list_actions.xml          |   2 +-
 briar-android/res/values/strings.xml          |   5 +-
 .../android/ActivityComponent.java            |   7 +
 .../briarproject/android/ActivityModule.java  |   9 +
 .../creation/CreateGroupActivity.java         | 158 ++++++++++++++++++
 .../creation/CreateGroupController.java       |  19 +++
 .../creation/CreateGroupControllerImpl.java   |  68 ++++++++
 .../creation/CreateGroupFragment.java         |  93 +++++++++++
 .../creation/CreateGroupListener.java         |  15 ++
 .../creation/CreateGroupMessageFragment.java  |  33 ++++
 .../privategroup/list/GroupListFragment.java  |  14 +-
 .../android/sharing/BaseMessageFragment.java  |   3 +-
 14 files changed, 453 insertions(+), 7 deletions(-)
 create mode 100644 briar-android/res/layout/fragment_create_group.xml
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupController.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupFragment.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupListener.java
 create mode 100644 briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java

diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml
index 1bb12bfff4..aad00c6d9e 100644
--- a/briar-android/AndroidManifest.xml
+++ b/briar-android/AndroidManifest.xml
@@ -100,6 +100,17 @@
 				/>
 		</activity>
 
+		<activity
+			android:name=".android.privategroup.creation.CreateGroupActivity"
+			android:label="@string/groups_create_group_title"
+			android:parentActivityName=".android.NavDrawerActivity"
+			android:windowSoftInputMode="adjustResize">
+			<meta-data
+				android:name="android.support.PARENT_ACTIVITY"
+				android:value=".android.NavDrawerActivity"
+				/>
+		</activity>
+
 		<activity
 			android:name=".android.privategroup.conversation.GroupActivity"
 			android:label="@string/app_name"
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 0000000000..a9601e2abb
--- /dev/null
+++ b/briar-android/res/layout/fragment_create_group.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+	xmlns:android="http://schemas.android.com/apk/res/android"
+	android:layout_width="match_parent"
+	android:layout_height="match_parent"
+	android:orientation="vertical"
+	android:padding="@dimen/margin_medium">
+
+	<EditText
+		android:id="@+id/name"
+		android:layout_width="match_parent"
+		android:layout_height="0dp"
+		android:layout_weight="1"
+		android:gravity="bottom"
+		android:hint="@string/groups_create_group_hint"/>
+
+	<Button
+		android:id="@+id/button"
+		style="@style/BriarButton"
+		android:enabled="false"
+		android:text="@string/groups_create_group_button"/>
+
+</LinearLayout>
diff --git a/briar-android/res/menu/groups_list_actions.xml b/briar-android/res/menu/groups_list_actions.xml
index 511224ff16..16eeba4625 100644
--- a/briar-android/res/menu/groups_list_actions.xml
+++ b/briar-android/res/menu/groups_list_actions.xml
@@ -6,7 +6,7 @@
 	<item
 		android:id="@+id/action_add_group"
 		android:icon="@drawable/ic_add_white"
-		android:title="@string/groups_add_group_title"
+		android:title="@string/groups_create_group_title"
 		app:showAsAction="ifRoom"/>
 
 </menu>
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 004c001290..872800c9f0 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -155,8 +155,11 @@
 	<string name="groups_group_is_empty">This group is empty</string>
 	<string name="groups_group_is_dissolved">This group is dissolved</string>
 	<string name="groups_remove">Remove</string>
-	<string name="groups_add_group_title">Add Private Group</string>
+	<string name="groups_create_group_title">Create Private Group</string>
 	<string name="groups_no_messages">This group is empty.\n\nYou can use the pen icon at the top to compose the first message.</string>
+	<string name="groups_create_group_button">Create Group</string>
+	<string name="groups_create_group_invitation_button">Send Invitation</string>
+	<string name="groups_create_group_hint">Add a name for your private group</string>
 	<string name="groups_compose_message">Compose Message</string>
 	<string name="groups_message_sent">Message sent</string>
 	<string name="groups_message_received">Message received</string>
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index 4e66c87aba..1179905b69 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 07cace5662..69505d32f3 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/privategroup/creation/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
new file mode 100644
index 0000000000..991cdf5265
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupActivity.java
@@ -0,0 +1,158 @@
+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 java.util.Collection;
+
+import javax.inject.Inject;
+
+import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
+import static android.widget.Toast.LENGTH_SHORT;
+
+public class CreateGroupActivity extends ContactSelectorActivity implements
+		CreateGroupListener, MessageFragmentListener {
+
+	@Inject
+	protected 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<GroupId, DbException>(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<ContactId> 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(String message) {
+		controller.sendInvitation(groupId, contacts, message,
+				new UiResultExceptionHandler<Void, DbException>(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;
+	}
+
+	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 0000000000..da1a00a0d1
--- /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<GroupId, DbException> result);
+
+	void sendInvitation(GroupId groupId, Collection<ContactId> contacts,
+			String message, ResultExceptionHandler<Void, DbException> result);
+
+}
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
new file mode 100644
index 0000000000..a35c2ac204
--- /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<GroupId, DbException> 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<ContactId> contacts, final String message,
+			final ResultExceptionHandler<Void, DbException> 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 0000000000..7f21508e38
--- /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 0000000000..3347be1586
--- /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 0000000000..d2a6600d49
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupMessageFragment.java
@@ -0,0 +1,33 @@
+package org.briarproject.android.privategroup.creation;
+
+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
+	protected int getButtonText() {
+		return R.string.groups_create_group_invitation_button;
+	}
+
+	@Override
+	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 4489dd6bf0..9e77003ace 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
index 2613fd230b..251846f644 100644
--- a/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
+++ b/briar-android/src/org/briarproject/android/sharing/BaseMessageFragment.java
@@ -4,7 +4,6 @@ import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.StringRes;
 import android.view.LayoutInflater;
-import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -16,7 +15,7 @@ import org.briarproject.android.view.TextInputView.TextInputListener;
 import static org.briarproject.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
 import static org.briarproject.util.StringUtils.truncateUtf8;
 
-abstract class BaseMessageFragment extends BaseFragment
+public abstract class BaseMessageFragment extends BaseFragment
 		implements TextInputListener {
 
 	protected LargeTextInputView message;
-- 
GitLab