Merge branch '722-implement-ux-design-for-inviting-new-members-to-a-group' into 'master'

Implement UX design for inviting new members to a group

Closes #722

See merge request !373
This commit is contained in:
akwizgran
2016-11-01 11:38:29 +00:00
8 changed files with 190 additions and 81 deletions

View File

@@ -142,6 +142,17 @@
/> />
</activity> </activity>
<activity
android:name=".android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity <activity
android:name=".android.sharing.ForumInvitationActivity" android:name=".android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title" android:label="@string/forum_invitations_title"

View File

@@ -17,7 +17,6 @@
<item <item
android:id="@+id/action_group_invite" android:id="@+id/action_group_invite"
android:enabled="false"
android:icon="@drawable/ic_add_white" android:icon="@drawable/ic_add_white"
android:title="@string/groups_invite_members" android:title="@string/groups_invite_members"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>

View File

@@ -157,6 +157,7 @@
<string name="groups_create_group_button">Create Group</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_invitation_button">Send Invitation</string>
<string name="groups_create_group_hint">Add a name for your private group</string> <string name="groups_create_group_hint">Add a name for your private group</string>
<string name="groups_invitation_sent">Group invitation has been sent</string>
<string name="groups_compose_message">Compose Message</string> <string name="groups_compose_message">Compose Message</string>
<string name="groups_message_sent">Message sent</string> <string name="groups_message_sent">Message sent</string>
<string name="groups_message_received">Message received</string> <string name="groups_message_received">Message received</string>

View File

@@ -32,6 +32,7 @@ import org.briarproject.android.privategroup.conversation.GroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupActivity; import org.briarproject.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.android.privategroup.creation.CreateGroupFragment; import org.briarproject.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment; import org.briarproject.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.android.privategroup.invitation.GroupInvitationActivity; import org.briarproject.android.privategroup.invitation.GroupInvitationActivity;
import org.briarproject.android.privategroup.list.GroupListFragment; import org.briarproject.android.privategroup.list.GroupListFragment;
import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity; import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
@@ -80,6 +81,7 @@ public interface ActivityComponent {
void inject(CreateGroupActivity activity); void inject(CreateGroupActivity activity);
void inject(GroupActivity activity); void inject(GroupActivity activity);
void inject(GroupInviteActivity activity);
void inject(GroupInvitationActivity activity); void inject(GroupInvitationActivity activity);
void inject(GroupMemberListActivity activity); void inject(GroupMemberListActivity activity);

View File

@@ -19,6 +19,7 @@ import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity; import org.briarproject.android.privategroup.memberlist.GroupMemberListActivity;
import org.briarproject.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.android.threaded.ThreadListActivity; import org.briarproject.android.threaded.ThreadListActivity;
import org.briarproject.android.threaded.ThreadListController; import org.briarproject.android.threaded.ThreadListController;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
@@ -34,6 +35,8 @@ public class GroupActivity extends
ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageHeader> ThreadListActivity<PrivateGroup, GroupMessageItem, GroupMessageHeader>
implements OnClickListener { implements OnClickListener {
private final static int REQUEST_INVITE = 1;
@Inject @Inject
GroupController controller; GroupController controller;
@@ -133,17 +136,23 @@ public class GroupActivity extends
@Override @Override
public boolean onOptionsItemSelected(final MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
ActivityOptionsCompat options =
makeCustomAnimation(this, android.R.anim.slide_in_left,
android.R.anim.slide_out_right);
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_group_compose_message: case R.id.action_group_compose_message:
showTextInput(null); showTextInput(null);
return true; return true;
case R.id.action_group_member_list: case R.id.action_group_member_list:
Intent i = new Intent(this, GroupMemberListActivity.class); Intent i1 = new Intent(this, GroupMemberListActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes()); i1.putExtra(GROUP_ID, groupId.getBytes());
ActivityOptionsCompat options = ActivityCompat.startActivity(this, i1, options.toBundle());
makeCustomAnimation(this, android.R.anim.slide_in_left, return true;
android.R.anim.slide_out_right); case R.id.action_group_invite:
ActivityCompat.startActivity(this, i, options.toBundle()); Intent i2 = new Intent(this, GroupInviteActivity.class);
i2.putExtra(GROUP_ID, groupId.getBytes());
ActivityCompat.startActivityForResult(this, i2, REQUEST_INVITE,
options.toBundle());
return true; return true;
case R.id.action_group_leave: case R.id.action_group_leave:
showLeaveGroupDialog(); showLeaveGroupDialog();
@@ -155,6 +164,13 @@ public class GroupActivity extends
} }
} }
@Override
protected void onActivityResult(int request, int result, Intent data) {
if (request == REQUEST_INVITE && result == RESULT_OK) {
displaySnackbarShort(R.string.groups_invitation_sent);
} else super.onActivityResult(request, result, data);
}
@Override @Override
protected int getMaxBodyLength() { protected int getMaxBodyLength() {
return MAX_GROUP_POST_BODY_LENGTH; return MAX_GROUP_POST_BODY_LENGTH;

View File

@@ -0,0 +1,95 @@
package org.briarproject.android.privategroup.creation;
import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
import org.briarproject.android.sharing.ContactSelectorActivity;
import org.briarproject.api.contact.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 org.jetbrains.annotations.NotNull;
import java.util.Collection;
import javax.inject.Inject;
import static android.widget.Toast.LENGTH_SHORT;
import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
public abstract class BaseGroupInviteActivity
extends ContactSelectorActivity
implements MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
// Subclasses may initialise the group ID in different places,
// restore it if it was saved
if (bundle != null) {
byte[] groupBytes = bundle.getByteArray(GROUP_ID);
if (groupBytes != null) groupId = new GroupId(groupBytes);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (groupId != null) {
outState.putByteArray(GROUP_ID, groupId.getBytes());
}
}
@Override
public void contactsSelected(GroupId groupId,
Collection<ContactId> contacts) {
super.contactsSelected(groupId, contacts);
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<Void, DbException>(this) {
@Override
public void onResultUi(Void result) {
Toast.makeText(BaseGroupInviteActivity.this,
"Inviting members is not yet implemented",
LENGTH_SHORT).show();
setResult(RESULT_OK);
supportFinishAfterTransition();
}
@Override
public void onExceptionUi(DbException exception) {
// TODO proper error handling
setResult(RESULT_CANCELED);
finish();
}
});
return true;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
}

View File

@@ -4,35 +4,23 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
import android.widget.Toast;
import org.briarproject.R; import org.briarproject.R;
import org.briarproject.android.ActivityComponent; import org.briarproject.android.ActivityComponent;
import org.briarproject.android.controller.handler.UiResultExceptionHandler; import org.briarproject.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.android.privategroup.conversation.GroupActivity; import org.briarproject.android.privategroup.conversation.GroupActivity;
import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener; import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
import org.briarproject.android.sharing.ContactSelectorActivity;
import org.briarproject.android.sharing.ContactSelectorFragment; import org.briarproject.android.sharing.ContactSelectorFragment;
import org.briarproject.api.contact.Contact; 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.db.DbException;
import org.briarproject.api.sync.GroupId; 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.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 public class CreateGroupActivity extends BaseGroupInviteActivity implements
CreateGroupListener, MessageFragmentListener { CreateGroupListener, MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override @Override
public void injectActivity(ActivityComponent component) { public void injectActivity(ActivityComponent component) {
component.inject(this); component.inject(this);
@@ -42,19 +30,21 @@ public class CreateGroupActivity extends ContactSelectorActivity implements
public void onCreate(Bundle bundle) { public void onCreate(Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
setContentView(R.layout.activity_fragment_container);
if (bundle == null) { if (bundle == null) {
CreateGroupFragment fragment = new CreateGroupFragment(); CreateGroupFragment fragment = new CreateGroupFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.fragmentContainer, fragment) .add(R.id.fragmentContainer, fragment)
.commit(); .commit();
} else {
byte[] groupBytes = bundle.getByteArray(GROUP_ID);
if (groupBytes != null) groupId = new GroupId(groupBytes);
} }
} }
@Override
@DatabaseExecutor
public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
// contacts can always be invited into a new group
return false;
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 1) { if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
@@ -66,14 +56,6 @@ public class CreateGroupActivity extends ContactSelectorActivity implements
} }
} }
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (groupId != null) {
outState.putByteArray(GROUP_ID, groupId.getBytes());
}
}
@Override @Override
public void onGroupNameChosen(String name) { public void onGroupNameChosen(String name) {
controller.createGroup(name, controller.createGroup(name,
@@ -106,53 +88,6 @@ public class CreateGroupActivity extends ContactSelectorActivity implements
.commit(); .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(@NotNull 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;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
private void openNewGroup() { private void openNewGroup() {
Intent i = new Intent(this, GroupActivity.class); Intent i = new Intent(this, GroupActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes()); i.putExtra(GROUP_ID, groupId.getBytes());

View File

@@ -0,0 +1,50 @@
package org.briarproject.android.privategroup.creation;
import android.content.Intent;
import android.os.Bundle;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.sharing.BaseMessageFragment.MessageFragmentListener;
import org.briarproject.android.sharing.ContactSelectorFragment;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
public class GroupInviteActivity extends BaseGroupInviteActivity
implements MessageFragmentListener {
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
// Initialise the group ID,
// it will be saved and restored by the superclass
Intent i = getIntent();
byte[] g = i.getByteArrayExtra(GROUP_ID);
if (g == null) throw new IllegalStateException("No GroupId in intent.");
groupId = new GroupId(g);
if (bundle == null) {
ContactSelectorFragment fragment =
ContactSelectorFragment.newInstance(groupId);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, fragment)
.commit();
}
}
@Override
@DatabaseExecutor
public boolean isDisabled(GroupId groupId, Contact c) throws DbException {
// TODO disable contacts that can not be invited
return false;
}
}