From 36a15388cc13a820ab7d79cda9958ae2e0ed14d7 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Sat, 8 Sep 2018 16:59:18 +0200 Subject: [PATCH] Can update group membership --- .../client/data/helpers/GroupsHelper.java | 64 +++++++ .../client/ui/adapters/GroupsListAdapter.java | 19 ++ .../CancelGroupMembershipRequestTask.java | 21 +++ .../client/ui/asynctasks/LeaveGroupTask.java | 21 +++ .../RespondGroupInvitationTask.java | 27 +++ .../SendGroupMembershipRequestTask.java | 21 +++ .../groups/AbstractGroupFragment.java | 98 +++++++++- .../fragments/groups/UserGroupsFragment.java | 24 ++- .../OnGroupMembershipUpdateListener.java | 35 ++++ .../ui/views/GroupMembershipStatusView.java | 173 ++++++++++++++++++ .../layout/view_group_membership_status.xml | 137 ++++++++++++++ app/src/main/res/layout/viewholder_group.xml | 14 +- app/src/main/res/values-fr/strings.xml | 12 ++ app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/strings.xml | 12 ++ app/src/main/res/values/styles.xml | 6 + 16 files changed, 676 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/CancelGroupMembershipRequestTask.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/LeaveGroupTask.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondGroupInvitationTask.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/SendGroupMembershipRequestTask.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnGroupMembershipUpdateListener.java create mode 100644 app/src/main/java/org/communiquons/android/comunic/client/ui/views/GroupMembershipStatusView.java create mode 100644 app/src/main/res/layout/view_group_membership_status.xml diff --git a/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/GroupsHelper.java b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/GroupsHelper.java index c74e7b0..6340c21 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/GroupsHelper.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/data/helpers/GroupsHelper.java @@ -161,6 +161,70 @@ public class GroupsHelper extends BaseHelper { } } + /** + * Send a group membership request + * + * @param groupID The ID of the target group + * @return Depends of the success of the operation + */ + public boolean sendRequest(int groupID){ + APIRequest request = new APIRequest(getContext(), "groups/send_request"); + request.addInt("id", groupID); + return performMembershipUpdate(request); + } + + /** + * Cancel a group membership request + * + * @param groupID the ID of the target group + * @return TRUE in case of success / FALSE else + */ + public boolean cancelRequest(int groupID){ + APIRequest request = new APIRequest(getContext(), "groups/cancel_request"); + request.addInt("id", groupID); + return performMembershipUpdate(request); + } + + /** + * Respond to a group membership invitation + * + * @param groupID The ID of the target group + * @param accept TRUE to accept invitation / FALSE else + */ + public boolean respondInvitation(int groupID, boolean accept){ + APIRequest request = new APIRequest(getContext(), "groups/respond_invitation"); + request.addInt("id", groupID); + request.addBoolean("accept", accept); + return performMembershipUpdate(request); + } + + /** + * Leave a group + * + * @param groupID The ID of the group to leave + * @return TRUE for a success / FALSE else + */ + public boolean leaveGroup(int groupID){ + APIRequest request = new APIRequest(getContext(), "groups/remove_membership"); + request.addInt("id", groupID); + return performMembershipUpdate(request); + } + + /** + * Perform on the server a membership update + * + * @param request The request to perform + * @return Depends of the success of the operation + */ + private boolean performMembershipUpdate(APIRequest request){ + try { + return new APIRequestHelper().exec(request).getResponse_code() == 200; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + /** * Parse group information into GroupInfo object * diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java index d4033e2..038d545 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/adapters/GroupsListAdapter.java @@ -10,7 +10,9 @@ import android.widget.TextView; import org.communiquons.android.comunic.client.R; import org.communiquons.android.comunic.client.data.models.GroupInfo; +import org.communiquons.android.comunic.client.ui.listeners.OnGroupMembershipUpdateListener; import org.communiquons.android.comunic.client.ui.views.GroupImageView; +import org.communiquons.android.comunic.client.ui.views.GroupMembershipStatusView; import java.util.ArrayList; @@ -26,6 +28,8 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { */ private ArrayList mList = new ArrayList<>(); + private OnGroupMembershipUpdateListener mOnGroupMembershipUpdateListener; + public GroupsListAdapter(Context context) { super(context); } @@ -39,6 +43,15 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { this.mList = List; } + /** + * Set the group membership update listener + * + * @param onGroupMembershipUpdateListener The listener + */ + public void setOnGroupMembershipUpdateListener(OnGroupMembershipUpdateListener onGroupMembershipUpdateListener) { + this.mOnGroupMembershipUpdateListener = onGroupMembershipUpdateListener; + } + @Override public int getItemCount() { return mList.size(); @@ -57,6 +70,7 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { ((GroupHolder)viewHolder).bind(i); } + /** * Single group holder class */ @@ -64,12 +78,16 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { private GroupImageView mGroupImageView; private TextView mGroupName; + private GroupMembershipStatusView mGroupMembershipStatus; GroupHolder(@NonNull View itemView) { super(itemView); mGroupImageView = itemView.findViewById(R.id.groupImage); mGroupName = itemView.findViewById(R.id.groupName); + mGroupMembershipStatus = itemView.findViewById(R.id.groupMembershipStatusView); + + mGroupMembershipStatus.setOnGroupMembershipUpdateListener(mOnGroupMembershipUpdateListener); } GroupInfo getGroup(int pos){ @@ -80,6 +98,7 @@ public class GroupsListAdapter extends BaseRecyclerViewAdapter { GroupInfo groupInfo = getGroup(pos); mGroupImageView.setGroup(groupInfo); mGroupName.setText(groupInfo.getDisplayName()); + mGroupMembershipStatus.setGroup(groupInfo); } } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/CancelGroupMembershipRequestTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/CancelGroupMembershipRequestTask.java new file mode 100644 index 0000000..8cac59b --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/CancelGroupMembershipRequestTask.java @@ -0,0 +1,21 @@ +package org.communiquons.android.comunic.client.ui.asynctasks; + +import android.content.Context; + +import org.communiquons.android.comunic.client.data.helpers.GroupsHelper; + +/** + * Cancel a group membership request task + * + * @author Pierre HUBERT + */ +public class CancelGroupMembershipRequestTask extends SafeAsyncTask { + public CancelGroupMembershipRequestTask(Context context) { + super(context); + } + + @Override + protected Boolean doInBackground(Integer... integers) { + return new GroupsHelper(getContext()).cancelRequest(integers[0]); + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/LeaveGroupTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/LeaveGroupTask.java new file mode 100644 index 0000000..dad5604 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/LeaveGroupTask.java @@ -0,0 +1,21 @@ +package org.communiquons.android.comunic.client.ui.asynctasks; + +import android.content.Context; + +import org.communiquons.android.comunic.client.data.helpers.GroupsHelper; + +/** + * Leave group task + * + * @author Pierre HUBERT + */ +public class LeaveGroupTask extends SafeAsyncTask { + public LeaveGroupTask(Context context) { + super(context); + } + + @Override + protected Boolean doInBackground(Integer... integers) { + return new GroupsHelper(getContext()).leaveGroup(integers[0]); + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondGroupInvitationTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondGroupInvitationTask.java new file mode 100644 index 0000000..edc28ee --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/RespondGroupInvitationTask.java @@ -0,0 +1,27 @@ +package org.communiquons.android.comunic.client.ui.asynctasks; + +import android.content.Context; + +import org.communiquons.android.comunic.client.data.helpers.GroupsHelper; + +/** + * Respond to a group membership invitation task + * + * @author Pierre HUBERT + */ +public class RespondGroupInvitationTask extends SafeAsyncTask { + + private boolean mAccept; + + + public RespondGroupInvitationTask(Context context, boolean accept) { + super(context); + this.mAccept = accept; + } + + + @Override + protected Boolean doInBackground(Integer... integers) { + return new GroupsHelper(getContext()).respondInvitation(integers[0], mAccept); + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/SendGroupMembershipRequestTask.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/SendGroupMembershipRequestTask.java new file mode 100644 index 0000000..3136aae --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/asynctasks/SendGroupMembershipRequestTask.java @@ -0,0 +1,21 @@ +package org.communiquons.android.comunic.client.ui.asynctasks; + +import android.content.Context; + +import org.communiquons.android.comunic.client.data.helpers.GroupsHelper; + +/** + * Send a group membership request task + * + * @author Pierre HUBERT + */ +public class SendGroupMembershipRequestTask extends SafeAsyncTask { + public SendGroupMembershipRequestTask(Context context) { + super(context); + } + + @Override + protected Boolean doInBackground(Integer... integers) { + return new GroupsHelper(getContext()).sendRequest(integers[0]); + } +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/AbstractGroupFragment.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/AbstractGroupFragment.java index 5156a11..e07e0ad 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/AbstractGroupFragment.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/AbstractGroupFragment.java @@ -1,8 +1,21 @@ package org.communiquons.android.comunic.client.ui.fragments.groups; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.support.annotation.CallSuper; +import android.support.v7.app.AlertDialog; +import android.widget.Toast; + import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.models.GroupInfo; import org.communiquons.android.comunic.client.ui.activities.MainActivity; +import org.communiquons.android.comunic.client.ui.asynctasks.CancelGroupMembershipRequestTask; +import org.communiquons.android.comunic.client.ui.asynctasks.LeaveGroupTask; +import org.communiquons.android.comunic.client.ui.asynctasks.RespondGroupInvitationTask; +import org.communiquons.android.comunic.client.ui.asynctasks.SafeAsyncTask; +import org.communiquons.android.comunic.client.ui.asynctasks.SendGroupMembershipRequestTask; import org.communiquons.android.comunic.client.ui.fragments.AbstractFragment; +import org.communiquons.android.comunic.client.ui.listeners.OnGroupMembershipUpdateListener; import java.util.Objects; @@ -11,7 +24,12 @@ import java.util.Objects; * * @author Pierre HUBERT */ -abstract class AbstractGroupFragment extends AbstractFragment { +abstract class AbstractGroupFragment extends AbstractFragment implements OnGroupMembershipUpdateListener { + + /** + * Debug tag + */ + private static final String TAG = AbstractGroupFragment.class.getSimpleName(); @Override public void onResume() { @@ -20,4 +38,82 @@ abstract class AbstractGroupFragment extends AbstractFragment { MainActivity.SetNavbarSelectedOption(Objects.requireNonNull(getActivity()), R.id.action_personal_page); } + + @Override + public void onRespondInvitation(GroupInfo group, boolean accept) { + RespondGroupInvitationTask respondGroupInvitationTask = + new RespondGroupInvitationTask(getContext(), accept); + performGroupMembershipUpdate(group.getId(), respondGroupInvitationTask); + } + + @Override + public void onCancelRequest(GroupInfo group) { + CancelGroupMembershipRequestTask task = new CancelGroupMembershipRequestTask(getActivity()); + performGroupMembershipUpdate(group.getId(), task); + } + + @Override + public void onSendRequest(GroupInfo group) { + SendGroupMembershipRequestTask task = new SendGroupMembershipRequestTask(getActivity()); + performGroupMembershipUpdate(group.getId(), task); + } + + @Override + public void onLeaveGroup(GroupInfo group) { + final int groupID = group.getId(); + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.dialog_leave_group_title) + .setMessage(R.string.dialog_leave_group_message) + .setNegativeButton(R.string.dialog_leave_group_cancel, null) + + .setPositiveButton(R.string.dialog_leave_group_confirm, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + leaveGroup(groupID); + } + }).show(); + } + + /** + * Do leave the group for the user + * + * @param groupID The ID of the group to leave + */ + private void leaveGroup(final int groupID) { + LeaveGroupTask leaveGroupTask = new LeaveGroupTask(getActivity()); + performGroupMembershipUpdate(groupID, leaveGroupTask); + } + + /** + * Perform group membership update + * + * @param groupID The ID of the target group + * @param task Task to execute + */ + private void performGroupMembershipUpdate(final int groupID, SafeAsyncTask task) { + task.setOnPostExecuteListener(new SafeAsyncTask.OnPostExecuteListener() { + @Override + public void OnPostExecute(Boolean success) { + onGroupMembershipUpdated(success, groupID); + } + }); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupID); + getTasksManager().addTask(task); + } + + /** + * This method is called when the membership to a group has been updated + * + * @param success TRUE if the operation was a success / FALSE else + * @param groupID Information about the updated group + */ + @CallSuper + public void onGroupMembershipUpdated(boolean success, int groupID) { + + //Check for errors + if (!success) + Toast.makeText(getActivity(), R.string.err_update_group_membership, + Toast.LENGTH_SHORT).show(); + + } } diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/UserGroupsFragment.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/UserGroupsFragment.java index d1f0f25..133488d 100644 --- a/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/UserGroupsFragment.java +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/fragments/groups/UserGroupsFragment.java @@ -120,17 +120,29 @@ public class UserGroupsFragment extends AbstractGroupFragment { setProgressBarVisibility(false); - mGroupsAdapter = new GroupsListAdapter(getActivity()); - mGroupsAdapter.setList(new ArrayList<>(mGroupsList.values())); + if(mGroupsAdapter == null) { + mGroupsAdapter = new GroupsListAdapter(getActivity()); - mGroupsView.setAdapter(mGroupsAdapter); - mGroupsView.setLayoutManager(new LinearLayoutManager(getActivity())); - mGroupsView.addItemDecoration(new DividerItemDecoration(getActivity(), - DividerItemDecoration.VERTICAL)); + mGroupsView.setAdapter(mGroupsAdapter); + mGroupsView.setLayoutManager(new LinearLayoutManager(getActivity())); + mGroupsView.addItemDecoration(new DividerItemDecoration(getActivity(), + DividerItemDecoration.VERTICAL)); + } + + + mGroupsAdapter.setOnGroupMembershipUpdateListener(this); + mGroupsAdapter.setList(new ArrayList<>(mGroupsList.values())); + mGroupsAdapter.notifyDataSetChanged(); setNoGroupNoticeVisibility(mGroupsList.size() == 0); } + @Override + public void onGroupMembershipUpdated(boolean success, int groupID) { + super.onGroupMembershipUpdated(success, groupID); + getGroupsList(); + } + /** * Update (set) progressbar visibility * diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnGroupMembershipUpdateListener.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnGroupMembershipUpdateListener.java new file mode 100644 index 0000000..4713c96 --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/listeners/OnGroupMembershipUpdateListener.java @@ -0,0 +1,35 @@ +package org.communiquons.android.comunic.client.ui.listeners; + +import org.communiquons.android.comunic.client.data.models.GroupInfo; + +public interface OnGroupMembershipUpdateListener { + + /** + * Respond to a group membership invitation + * + * @param group Information about the target group + * @param accept TRUE to accept / FALSE else + */ + void onRespondInvitation(GroupInfo group, boolean accept); + + /** + * Cancel a group membership request + * + * @param group The target group + */ + void onCancelRequest(GroupInfo group); + + /** + * Send a request to join a group + * + * @param group The target group + */ + void onSendRequest(GroupInfo group); + + /** + * Send a request to leave a group + * + * @param group The target group + */ + void onLeaveGroup(GroupInfo group); +} diff --git a/app/src/main/java/org/communiquons/android/comunic/client/ui/views/GroupMembershipStatusView.java b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/GroupMembershipStatusView.java new file mode 100644 index 0000000..79de8ba --- /dev/null +++ b/app/src/main/java/org/communiquons/android/comunic/client/ui/views/GroupMembershipStatusView.java @@ -0,0 +1,173 @@ +package org.communiquons.android.comunic.client.ui.views; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.constraint.ConstraintLayout; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; + +import org.communiquons.android.comunic.client.R; +import org.communiquons.android.comunic.client.data.models.GroupInfo; +import org.communiquons.android.comunic.client.ui.listeners.OnGroupMembershipUpdateListener; + +/** + * Group membership button + * + * @author Pierre HUBERT + */ +public class GroupMembershipStatusView extends BaseFrameLayoutView implements View.OnClickListener { + + /** + * Views + */ + private ConstraintLayout respondInvitationForm; + private Button rejectButton; + private Button acceptButton; + private ConstraintLayout cancelRequestForm; + private Button cancelButton; + private ConstraintLayout requestForm; + private Button joinButton; + private ConstraintLayout memberForm; + private Button leaveButton; + + /** + * Group information + */ + private GroupInfo mInfo; + + /** + * Listener + */ + private OnGroupMembershipUpdateListener mOnGroupMembershipUpdateListener; + + + public GroupMembershipStatusView(@NonNull Context context) { + this(context, null); + } + + public GroupMembershipStatusView(@NonNull Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public GroupMembershipStatusView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + View view = inflate(getContext(), R.layout.view_group_membership_status, this); + respondInvitationForm = view.findViewById(R.id.respondInvitationForm); + rejectButton = view.findViewById(R.id.rejectButton); + acceptButton = view.findViewById(R.id.acceptButton); + cancelRequestForm = view.findViewById(R.id.cancelRequestForm); + cancelButton = view.findViewById(R.id.cancelButton); + requestForm = view.findViewById(R.id.requestForm); + joinButton = view.findViewById(R.id.joinButton); + memberForm = view.findViewById(R.id.memberForm); + leaveButton = view.findViewById(R.id.leaveButton); + + //Show only one form + showForm(respondInvitationForm); + + rejectButton.setOnClickListener(this); + acceptButton.setOnClickListener(this); + cancelButton.setOnClickListener(this); + joinButton.setOnClickListener(this); + leaveButton.setOnClickListener(this); + } + + /** + * Set the group membership update listener + * + * @param onGroupMembershipUpdateListener The listener + */ + public void setOnGroupMembershipUpdateListener(OnGroupMembershipUpdateListener onGroupMembershipUpdateListener) { + this.mOnGroupMembershipUpdateListener = onGroupMembershipUpdateListener; + } + + /** + * Assign the view to a new group + * + * @param info Information about the group + */ + public void setGroup(GroupInfo info) { + mInfo = info; + showButtons(true); + + switch (info.getMembershipLevel()) { + case VISITOR: + showForm(requestForm); + return; + + case PENDING: + showForm(cancelRequestForm); + return; + + case INVITED: + showForm(respondInvitationForm); + return; + + default: + showForm(memberForm); + } + } + + /** + * Display the form contained in a specific view and hide the other ones + * + * @param v The container of the form to show + */ + private void showForm(View v) { + + respondInvitationForm.setVisibility(v.equals(respondInvitationForm) + ? View.VISIBLE : View.GONE); + cancelRequestForm.setVisibility(v.equals(cancelRequestForm) + ? View.VISIBLE : View.GONE); + requestForm.setVisibility(v.equals(requestForm) + ? View.VISIBLE : View.GONE); + memberForm.setVisibility(v.equals(memberForm) ? View.VISIBLE : View.GONE); + + } + + /** + * Update buttons visibility + * + * @param visible New visibility for the buttons + */ + private void showButtons(boolean visible){ + rejectButton.setVisibility(visible ? View.VISIBLE : View.GONE); + acceptButton.setVisibility(visible ? View.VISIBLE : View.GONE); + cancelButton.setVisibility(visible ? View.VISIBLE : View.GONE); + joinButton.setVisibility(visible ? View.VISIBLE : View.GONE); + leaveButton.setVisibility(View.VISIBLE); //View cannot be hidden + } + + @Override + public void onClick(View v) { + + if(mOnGroupMembershipUpdateListener == null) + return; + + showButtons(false); + + if(v.equals(rejectButton)){ + mOnGroupMembershipUpdateListener.onRespondInvitation(mInfo, false); + } + + else if(v.equals(acceptButton)){ + mOnGroupMembershipUpdateListener.onRespondInvitation(mInfo, true); + } + + else if(v.equals(cancelButton)){ + mOnGroupMembershipUpdateListener.onCancelRequest(mInfo); + } + + else if (v.equals(joinButton)){ + mOnGroupMembershipUpdateListener.onSendRequest(mInfo); + } + + else if(v.equals(leaveButton)){ + mOnGroupMembershipUpdateListener.onLeaveGroup(mInfo); + } + + } +} diff --git a/app/src/main/res/layout/view_group_membership_status.xml b/app/src/main/res/layout/view_group_membership_status.xml new file mode 100644 index 0000000..eb94f00 --- /dev/null +++ b/app/src/main/res/layout/view_group_membership_status.xml @@ -0,0 +1,137 @@ + + + + + + + +