diff --git a/Habitica/assets/migrations/Habitica/12.sql b/Habitica/assets/migrations/Habitica/12.sql
new file mode 100644
index 000000000..a05a1977a
--- /dev/null
+++ b/Habitica/assets/migrations/Habitica/12.sql
@@ -0,0 +1 @@
+ALTER TABLE Quest ADD COLUMN RSVPNeeded bool;
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_party_info.xml b/Habitica/res/layout/fragment_party_info.xml
index bd6d5b3bd..597180271 100644
--- a/Habitica/res/layout/fragment_party_info.xml
+++ b/Habitica/res/layout/fragment_party_info.xml
@@ -6,6 +6,7 @@
+
@@ -16,6 +17,10 @@
+
+
@@ -96,6 +101,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/party_member_quest.xml b/Habitica/res/layout/party_member_quest.xml
new file mode 100644
index 000000000..79b841264
--- /dev/null
+++ b/Habitica/res/layout/party_member_quest.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values/colors.xml b/Habitica/res/values/colors.xml
index 1988736b7..7772fd095 100644
--- a/Habitica/res/values/colors.xml
+++ b/Habitica/res/values/colors.xml
@@ -91,4 +91,10 @@
#50ffffff
#c8ffffff
#32000000
+
+
+ #ff5cb85c
+ #fff0ad4e
+ #ffd9534f
+
\ No newline at end of file
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 2578ef363..342dddc87 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -161,6 +161,12 @@
Purchase
Purchase set %s
Due Date
+ Accept
+ Reject
+ Begin
+ Cancel
+ Abort
+ Leave Quest
Version %s (%d)
diff --git a/Habitica/src/com/habitrpg/android/habitica/HabitDatabase.java b/Habitica/src/com/habitrpg/android/habitica/HabitDatabase.java
index fcb7e236e..17ab712b7 100644
--- a/Habitica/src/com/habitrpg/android/habitica/HabitDatabase.java
+++ b/Habitica/src/com/habitrpg/android/habitica/HabitDatabase.java
@@ -10,5 +10,5 @@ public class HabitDatabase {
public static final String NAME = "Habitica";
- public static final int VERSION = 11;
+ public static final int VERSION = 12;
}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/adapter/PartyMemberQuestRecyclerViewAdapter.java b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/PartyMemberQuestRecyclerViewAdapter.java
new file mode 100644
index 000000000..3c42ab3e0
--- /dev/null
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/adapter/PartyMemberQuestRecyclerViewAdapter.java
@@ -0,0 +1,89 @@
+package com.habitrpg.android.habitica.ui.adapter;
+
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.habitrpg.android.habitica.R;
+import com.magicmicky.habitrpgwrapper.lib.models.Group;
+import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
+
+import butterknife.Bind;
+import butterknife.ButterKnife;
+
+/**
+ * Created by Negue on 22.09.2015.
+ */
+public class PartyMemberQuestRecyclerViewAdapter extends RecyclerView.Adapter {
+
+ Group group;
+
+ public void setGroup(Group group) {
+ Log.d("PartyMemberQuestFrag", "set group");
+ this.group = group;
+ this.notifyDataSetChanged();
+ }
+
+ @Override
+ public MemberQuestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.party_member_quest, parent, false);
+ Log.d("PartyMemberQuestFrag", "inflated");
+
+ return new MemberQuestViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(MemberQuestViewHolder holder, int position) {
+ Log.d("PartyMemberQuestFrag", "call binder");
+
+ holder.bind(group.members.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+
+ Log.d("PartyMemberQuestFrag", "group " + ((group == null)? 0 : group.memberCount));
+ return (group == null)? 0 : group.memberCount;
+ }
+
+ class MemberQuestViewHolder extends RecyclerView.ViewHolder {
+
+ @Bind(R.id.username)
+ TextView userName;
+
+ @Bind(R.id.rsvpneeded)
+ TextView rsvpNeeded;
+
+
+ public MemberQuestViewHolder(View itemView) {
+ super(itemView);
+
+ Log.d("PartyMemberQuestFrag", "VH init");
+
+ ButterKnife.bind(this, itemView);
+
+ Log.d("PartyMemberFrag", "VH init");
+ }
+
+ public void bind(HabitRPGUser user) {
+ Log.d("PartyMemberQuestFrag", "bind " + user.getProfile().getName());
+ android.content.Context ctx = itemView.getContext();
+
+ userName.setText(user.getProfile().getName());
+
+ Boolean rsvpneeded = group.quest.members.get(user.getId());
+ if(rsvpneeded == null){
+ rsvpNeeded.setText("Pending");
+ }else if(rsvpneeded == true){
+ rsvpNeeded.setText("Accepted");
+ }else{ // rsvpneeded == false
+ rsvpNeeded.setText("Rejected");
+ }
+ }
+ }
+}
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java
index 7d82d4146..e9e3ace72 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java
@@ -122,7 +122,7 @@ public class PartyFragment extends BaseMainFragment {
switch (position) {
case 0: {
- fragment = partyInformationFragment = PartyInformationFragment.newInstance(group);
+ fragment = partyInformationFragment = PartyInformationFragment.newInstance(group, user, mAPIHelper);
break;
}
case 1: {
diff --git a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInformationFragment.java b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInformationFragment.java
index 5c6704153..c94880999 100644
--- a/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInformationFragment.java
+++ b/Habitica/src/com/habitrpg/android/habitica/ui/fragments/social/party/PartyInformationFragment.java
@@ -1,18 +1,35 @@
package com.habitrpg.android.habitica.ui.fragments.social.party;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
import android.databinding.DataBindingUtil;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.habitrpg.android.habitica.APIHelper;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.databinding.FragmentPartyInfoBinding;
import com.magicmicky.habitrpgwrapper.lib.models.Group;
+import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
import com.magicmicky.habitrpgwrapper.lib.models.QuestContent;
+import butterknife.Bind;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import de.greenrobot.event.EventBus;
+import de.greenrobot.event.EventBusException;
+import retrofit.Callback;
+import retrofit.RetrofitError;
+import retrofit.client.Response;
+
/**
* Created by Negue on 16.09.2015.
*/
@@ -21,16 +38,22 @@ public class PartyInformationFragment extends Fragment {
private View view;
FragmentPartyInfoBinding viewBinding;
+ APIHelper mAPIHelper;
+ @Bind(R.id.questMemberView)
+ LinearLayout questMemberView;
private Group group;
+ private HabitRPGUser user;
- public static PartyInformationFragment newInstance(Group group) {
+ public static PartyInformationFragment newInstance(Group group, HabitRPGUser user, APIHelper mAPIHelper) {
Bundle args = new Bundle();
PartyInformationFragment fragment = new PartyInformationFragment();
fragment.setArguments(args);
fragment.group = group;
+ fragment.user = user;
+ fragment.mAPIHelper = mAPIHelper;
return fragment;
}
@@ -45,18 +68,34 @@ public class PartyInformationFragment extends Fragment {
view = inflater.inflate(R.layout.fragment_party_info, container, false);
viewBinding = DataBindingUtil.bind(view);
+ if (user != null) {
+ viewBinding.setUser(user);
+ }
if (group != null) {
setGroup(group);
}
+ ButterKnife.bind(this, view);
+
return view;
}
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ }
+
public void setGroup(Group group) {
if (viewBinding != null) {
viewBinding.setParty(group);
}
+
+ if (questMemberView != null) {
+ updateQuestMember(group);
+ }
+
+ this.group = group;
}
public void setQuestContent(QuestContent quest) {
@@ -64,4 +103,183 @@ public class PartyInformationFragment extends Fragment {
viewBinding.setQuest(quest);
}
}
+
+ private void updateQuestMember(Group group) {
+
+ questMemberView.removeAllViewsInLayout();
+ if (group.quest.key == null) return;
+ LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ for (HabitRPGUser member : group.members) {
+ final LinearLayout itemView = (LinearLayout) layoutInflater.inflate(R.layout.party_member_quest, null);
+ TextView questResponse = (TextView) itemView.findViewById(R.id.rsvpneeded);
+ TextView userName = (TextView) itemView.findViewById(R.id.username);
+ if (group.quest.leader.equals(member.getId()))
+ userName.setText("* " + member.getProfile().getName());
+ else
+ userName.setText(member.getProfile().getName());
+
+ if (!group.quest.members.containsKey(member.getId()))
+ continue;
+ Boolean questresponse = group.quest.members.get(member.getId());
+ if (group.quest.active) {
+ questResponse.setText("");
+ } else if (questresponse == null) {
+ questResponse.setText("Pending");
+ } else if (questresponse.booleanValue() == true) {
+ questResponse.setText("Accepted");
+ questResponse.setTextColor(Color.parseColor("#2db200"));
+ } else if (questresponse.booleanValue() == false) {
+ questResponse.setText("Rejected");
+ questResponse.setTextColor(Color.parseColor("#b30409"));
+ }
+ questMemberView.post(new Runnable() {
+ @Override
+ public void run() {
+ questMemberView.addView(itemView);
+ }
+ });
+ }
+ }
+
+
+ @OnClick(R.id.btnQuestAccept)
+ public void onQuestAccept() {
+ mAPIHelper.apiService.acceptQuest(group.id, new Callback() {
+ @Override
+ public void success(Void aVoid, Response response) {
+ user.getParty().getQuest().RSVPNeeded = false;
+ group.quest.members.put(user.getId(), true);
+ setGroup(group);
+ viewBinding.setUser(user);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+
+
+ @OnClick(R.id.btnQuestReject)
+ public void onQuestReject() {
+ mAPIHelper.apiService.rejectQuest(group.id, new Callback() {
+ @Override
+ public void success(Void aVoid, Response response) {
+ user.getParty().getQuest().RSVPNeeded = false;
+ group.quest.members.put(user.getId(), false);
+ setGroup(group);
+ viewBinding.setUser(user);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+
+
+ @OnClick(R.id.btnQuestLeave)
+ public void onQuestLeave() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage("Are you sure you want to leave the active quest? All your quest progress will be lost.")
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAPIHelper.apiService.leaveQuest(group.id, new Callback() {
+ @Override
+ public void success(Void aVoid, Response response) {
+ group.quest.members.remove(user.getId());
+ setGroup(group);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+ }).setNegativeButton("No", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ builder.show();
+ }
+
+ @OnClick(R.id.btnQuestBegin)
+ public void onQuestBegin() {
+ mAPIHelper.apiService.forceStartQuest(group.id, group, new Callback() {
+ @Override
+ public void success(Group group, Response response) {
+ setGroup(group);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+
+ @OnClick(R.id.btnQuestCancel)
+ public void onQuestCancel() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage("Are you sure you want to cancel this quest? All invitation acceptances will be lost. The quest owner will retain possession of the quest scroll.")
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAPIHelper.apiService.cancelQuest(group.id, new Callback() {
+ @Override
+ public void success(Void aVoid, Response response) {
+ setGroup(group);
+ setQuestContent(null);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+ }).setNegativeButton("No", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ builder.show();
+ }
+
+ @OnClick(R.id.btnQuestAbort)
+ public void onQuestAbort() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
+ .setMessage("Are you sure you want to abort this mission? It will abort it for everyone in your party and all progress will be lost. The quest scroll will be returned to the quest owner.")
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mAPIHelper.apiService.abortQuest(group.id, new Callback() {
+ @Override
+ public void success(Group group, Response response) {
+ setGroup(group);
+ setQuestContent(null);
+ }
+
+ @Override
+ public void failure(RetrofitError error) {
+
+ }
+ });
+ }
+ }).setNegativeButton("No", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ builder.show();
+ }
+
}
diff --git a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
index 58de1ad8e..b887654aa 100644
--- a/Habitica/src/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
+++ b/Habitica/src/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
@@ -135,4 +135,22 @@ public interface ApiService {
@POST("/user/batch-update")
void batchOperation(@Body List