diff --git a/Habitica/res/drawable-hdpi/ic_people_black_24dp.png b/Habitica/res/drawable-hdpi/ic_people_black_24dp.png
new file mode 100644
index 000000000..792d67d93
Binary files /dev/null and b/Habitica/res/drawable-hdpi/ic_people_black_24dp.png differ
diff --git a/Habitica/res/drawable-mdpi/ic_people_black_24dp.png b/Habitica/res/drawable-mdpi/ic_people_black_24dp.png
new file mode 100644
index 000000000..fdf9d0e85
Binary files /dev/null and b/Habitica/res/drawable-mdpi/ic_people_black_24dp.png differ
diff --git a/Habitica/res/drawable-xhdpi/ic_people_black_24dp.png b/Habitica/res/drawable-xhdpi/ic_people_black_24dp.png
new file mode 100644
index 000000000..de45ada3f
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/ic_people_black_24dp.png differ
diff --git a/Habitica/res/drawable-xxhdpi/ic_people_black_24dp.png b/Habitica/res/drawable-xxhdpi/ic_people_black_24dp.png
new file mode 100644
index 000000000..ba45085a3
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/ic_people_black_24dp.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/ic_people_black_24dp.png b/Habitica/res/drawable-xxxhdpi/ic_people_black_24dp.png
new file mode 100644
index 000000000..65fb74752
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/ic_people_black_24dp.png differ
diff --git a/Habitica/res/layout/challenge_item.xml b/Habitica/res/layout/challenge_item.xml
new file mode 100644
index 000000000..a87b11969
--- /dev/null
+++ b/Habitica/res/layout/challenge_item.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_challengeslist.xml b/Habitica/res/layout/fragment_challengeslist.xml
new file mode 100644
index 000000000..62b3e7db2
--- /dev/null
+++ b/Habitica/res/layout/fragment_challengeslist.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index b8851355b..0fd5c40ec 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -436,4 +436,5 @@ To start, which parts of your life do you want to improve?
Audio Theme
Change Habitica\'s Audio Theme
+ by
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java
index 0af04bf80..bae213ca8 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabitDatabase.java
@@ -7,5 +7,5 @@ public class HabitDatabase {
public static final String NAME = "Habitica";
- public static final int VERSION = 29;
+ public static final int VERSION = 30;
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
index 5c228a15b..ce82fd348 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java
@@ -43,6 +43,8 @@ import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment;
import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment;
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment;
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.ChallengeListFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.ChallengesOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildFragment;
@@ -184,4 +186,8 @@ public interface AppComponent {
void inject(AvatarStatsWidgetProvider avatarStatsWidgetProvider);
void inject(SoundManager soundManager);
+
+ void inject(ChallengesOverviewFragment challengesOverviewFragment);
+
+ void inject(ChallengeListFragment challengeListFragment);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/ChallengesListViewAdapter.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/ChallengesListViewAdapter.java
new file mode 100644
index 000000000..eba041406
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/ChallengesListViewAdapter.java
@@ -0,0 +1,96 @@
+package com.habitrpg.android.habitica.ui.adapter.social;
+
+import android.support.v7.widget.RecyclerView;
+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.R;
+import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
+import com.magicmicky.habitrpgwrapper.lib.models.HabitRPGUser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+import static com.raizlabs.android.dbflow.config.FlowManager.getContext;
+
+
+public class ChallengesListViewAdapter extends RecyclerView.Adapter {
+
+
+ private List challenges = new ArrayList<>();
+
+ public void setChallenges(List challenges) {
+ this.challenges = challenges;
+ this.notifyDataSetChanged();
+ }
+
+ @Override
+ public ChallengeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.challenge_item, parent, false);
+
+ return new ChallengeViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ChallengeViewHolder holder, int position) {
+ holder.bind(challenges.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return challenges.size();
+ }
+
+ public class ChallengeViewHolder extends RecyclerView.ViewHolder {
+ @BindView(R.id.challenge_name)
+ TextView challengeName;
+
+ @BindView(R.id.leader_label)
+ TextView leaderName;
+
+ @BindView(R.id.memberCountTextView)
+ TextView memberCountTextView;
+
+ @BindView(R.id.gem_prize_layout)
+ LinearLayout gem_prize_layout;
+
+ @BindView(R.id.gemPrizeTextView)
+ TextView gemPrizeTextView;
+
+ public ChallengeViewHolder(View itemView) {
+ super(itemView);
+
+ ButterKnife.bind(this, itemView);
+ }
+
+ public void bind(Challenge challenge) {
+ challengeName.setText(challenge.name);
+
+ leaderName.setText(getContext().getString(R.string.by) + " " + getLeaderName(challenge.leader));
+
+ memberCountTextView.setText(challenge.memberCount + "");
+
+ if (challenge.prize == 0) {
+ gem_prize_layout.setVisibility(View.GONE);
+ } else {
+ gem_prize_layout.setVisibility(View.VISIBLE);
+ gemPrizeTextView.setText(challenge.prize + "");
+ }
+ }
+
+ private String getLeaderName(HabitRPGUser user) {
+ try {
+ return user.getProfile().getName();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengeListFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengeListFragment.java
new file mode 100644
index 000000000..7b502dec6
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengeListFragment.java
@@ -0,0 +1,122 @@
+package com.habitrpg.android.habitica.ui.fragments.social;
+
+import android.os.Bundle;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import com.habitrpg.android.habitica.R;
+import com.habitrpg.android.habitica.components.AppComponent;
+import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter;
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
+import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
+
+import java.util.HashSet;
+import java.util.List;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class ChallengeListFragment extends BaseMainFragment implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
+
+ @BindView(R.id.challenges_refresh_layout)
+ SwipeRefreshLayout swipeRefreshLayout;
+
+ @BindView(R.id.challenges_list)
+ RecyclerView recyclerView;
+
+ private ChallengesListViewAdapter challengeAdapter;
+ private boolean viewUserChallengesOnly;
+
+ @Override
+ public void onCreate(Bundle savedInstance) {
+ super.onCreate(savedInstance);
+
+ challengeAdapter = new ChallengesListViewAdapter();
+
+ }
+
+ public void setViewUserChallengesOnly(boolean only) {
+ this.viewUserChallengesOnly = only;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View v = inflater.inflate(R.layout.fragment_challengeslist, container, false);
+ unbinder = ButterKnife.bind(this, v);
+ swipeRefreshLayout.setOnRefreshListener(this);
+
+ recyclerView.setLayoutManager(new LinearLayoutManager(this.activity));
+
+ this.onRefresh();
+ return v;
+ }
+
+ @Override
+ public void injectFragment(AppComponent component) {
+ component.inject(this);
+ }
+
+ @Override
+ public void onRefresh() {
+ swipeRefreshLayout.setRefreshing(true);
+
+ fetchChallenges();
+ }
+
+ private void fetchChallenges() {
+ if (this.apiHelper != null && this.apiHelper.apiService != null) {
+
+ apiHelper.apiService.getUserChallenges().
+ compose(apiHelper.configureApiCallObserver())
+ .subscribe(s -> {
+
+ if (viewUserChallengesOnly) {
+ List userChallenges = this.user.getChallengeList();
+
+ HashSet userChallengesHash = new HashSet();
+
+ for (Challenge userChallenge : userChallenges) {
+ userChallengesHash.add(userChallenge.id);
+ }
+
+ userChallenges.clear();
+
+ for (Challenge challenge : s) {
+ if (userChallengesHash.contains(challenge.id)) {
+ challenge.user_id = this.user.getId();
+ userChallenges.add(challenge);
+ }
+
+ challenge.async().save();
+ }
+
+ challengeAdapter.setChallenges(userChallenges);
+ } else {
+ challengeAdapter.setChallenges(s);
+
+ for (Challenge challenge : s) {
+ challenge.async().save();
+ }
+ }
+ recyclerView.setAdapter(challengeAdapter);
+
+ if (swipeRefreshLayout != null) {
+ swipeRefreshLayout.setRefreshing(false);
+ }
+ }, throwable -> {
+ });
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengesOverviewFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengesOverviewFragment.java
new file mode 100644
index 000000000..627718ac7
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChallengesOverviewFragment.java
@@ -0,0 +1,87 @@
+package com.habitrpg.android.habitica.ui.fragments.social;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.habitrpg.android.habitica.R;
+import com.habitrpg.android.habitica.components.AppComponent;
+import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment;
+
+public class ChallengesOverviewFragment extends BaseMainFragment {
+
+ public ViewPager viewPager;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ this.usesTabLayout = true;
+ super.onCreateView(inflater, container, savedInstanceState);
+ View v = inflater.inflate(R.layout.fragment_viewpager, container, false);
+
+ viewPager = (ViewPager) v.findViewById(R.id.view_pager);
+
+ viewPager.setCurrentItem(1);
+
+ setViewPagerAdapter();
+
+ challengeListFragment = new ChallengeListFragment();
+ challengeListFragment.setUser(this.user);
+ challengeListFragment.setViewUserChallengesOnly(true);
+
+ return v;
+ }
+
+ @Override
+ public void injectFragment(AppComponent component) {
+ component.inject(this);
+ }
+
+ private ChallengeListFragment challengeListFragment;
+
+ public void setViewPagerAdapter() {
+ android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager();
+
+ viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Fragment fragment = new Fragment();
+
+ switch (position) {
+ case 0:
+ return challengeListFragment;
+ case 1:
+ return fragment;
+ }
+
+ return fragment;
+ }
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case 0:
+ return "My Challenges";
+ case 1:
+ return "Public";
+ }
+ return "";
+ }
+ });
+
+ if (tabLayout != null && viewPager != null) {
+ tabLayout.setupWithViewPager(viewPager);
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/MainDrawerBuilder.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/MainDrawerBuilder.java
index 55c9702ae..ef3cb38b4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/MainDrawerBuilder.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/menu/MainDrawerBuilder.java
@@ -15,6 +15,7 @@ import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemsFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.shops.ShopsFragment;
import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragment;
import com.habitrpg.android.habitica.ui.fragments.skills.SkillsFragment;
+import com.habitrpg.android.habitica.ui.fragments.social.ChallengesOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.GuildsOverviewFragment;
import com.habitrpg.android.habitica.ui.fragments.social.InboxFragment;
import com.habitrpg.android.habitica.ui.fragments.social.TavernFragment;
@@ -41,16 +42,17 @@ public class MainDrawerBuilder {
public static final int SIDEBAR_TAVERN = 3;
public static final int SIDEBAR_PARTY = 4;
public static final int SIDEBAR_GUILDS = 5;
- public static final int SIDEBAR_SHOPS = 6;
- public static final int SIDEBAR_AVATAR = 7;
- public static final int SIDEBAR_EQUIPMENT = 8;
- public static final int SIDEBAR_ITEMS = 9;
- public static final int SIDEBAR_STABLE = 10;
- public static final int SIDEBAR_PURCHASE = 11;
- public static final int SIDEBAR_NEWS = 12;
- public static final int SIDEBAR_SETTINGS = 13;
- public static final int SIDEBAR_HELP = 14;
- public static final int SIDEBAR_ABOUT = 15;
+ public static final int SIDEBAR_CHALLENGES = 6;
+ public static final int SIDEBAR_SHOPS = 7;
+ public static final int SIDEBAR_AVATAR = 8;
+ public static final int SIDEBAR_EQUIPMENT = 9;
+ public static final int SIDEBAR_ITEMS = 10;
+ public static final int SIDEBAR_STABLE = 11;
+ public static final int SIDEBAR_PURCHASE = 12;
+ public static final int SIDEBAR_NEWS = 13;
+ public static final int SIDEBAR_SETTINGS = 14;
+ public static final int SIDEBAR_HELP = 15;
+ public static final int SIDEBAR_ABOUT = 16;
public static AccountHeaderBuilder CreateDefaultAccountHeader(final Activity activity) {
return new AccountHeaderBuilder()
@@ -83,6 +85,7 @@ public class MainDrawerBuilder {
new PrimaryDrawerItem().withName(activity.getString(R.string.sidebar_tavern)).withIdentifier(SIDEBAR_TAVERN),
new PrimaryDrawerItem().withName(activity.getString(R.string.sidebar_party)).withIdentifier(SIDEBAR_PARTY),
new PrimaryDrawerItem().withName(activity.getString(R.string.sidebar_guilds)).withIdentifier(SIDEBAR_GUILDS),
+ new PrimaryDrawerItem().withName(activity.getString(R.string.sidebar_challenges)).withIdentifier(SIDEBAR_CHALLENGES),
new SectionIconDrawerItem().withName(activity.getString(R.string.sidebar_section_inventory).toUpperCase()),
new PrimaryDrawerItem().withName(activity.getString(R.string.sidebar_shops)).withIdentifier(SIDEBAR_SHOPS),
@@ -130,6 +133,10 @@ public class MainDrawerBuilder {
fragment = new TavernFragment();
break;
}
+ case SIDEBAR_CHALLENGES: {
+ fragment = new ChallengesOverviewFragment();
+ break;
+ }
case SIDEBAR_SHOPS: {
fragment = new ShopsFragment();
break;
diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
index f7223349e..554c312a7 100644
--- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
+++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/api/ApiService.java
@@ -23,7 +23,9 @@ import com.magicmicky.habitrpgwrapper.lib.models.responses.UnlockResponse;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskList;
+import com.magicmicky.habitrpgwrapper.lib.models.Challenge;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -257,6 +259,9 @@ public interface ApiService {
@DELETE("user/push-devices/{regId}")
Observable deletePushDevice(@Path("regId") String regId);
+ @GET("challenges/user")
+ Observable> getUserChallenges();
+
//DEBUG: These calls only work on a local development server
@POST("debug/add-ten-gems")
diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/Challenge.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/Challenge.java
new file mode 100644
index 000000000..2313f31dd
--- /dev/null
+++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/Challenge.java
@@ -0,0 +1,45 @@
+package com.magicmicky.habitrpgwrapper.lib.models;
+
+import com.habitrpg.android.habitica.HabitDatabase;
+import com.raizlabs.android.dbflow.annotation.Column;
+import com.raizlabs.android.dbflow.annotation.NotNull;
+import com.raizlabs.android.dbflow.annotation.PrimaryKey;
+import com.raizlabs.android.dbflow.annotation.Table;
+import com.raizlabs.android.dbflow.structure.BaseModel;
+
+import java.util.HashMap;
+
+@Table(databaseName = HabitDatabase.NAME)
+public class Challenge extends BaseModel {
+
+ @NotNull
+ @PrimaryKey
+ @Column
+ public String id;
+
+ @Column
+ public String name;
+
+ @Column
+ public String shortName;
+
+ @Column
+ public String description;
+
+ public HabitRPGUser leader;
+
+ public Group group;
+
+ public int prize;
+
+ @Column
+ public boolean official;
+
+ public HashMap tasksOrder;
+
+ @Column
+ public int memberCount;
+
+ @Column
+ public String user_id;
+}
diff --git a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java
index 19c581fd9..044c20825 100644
--- a/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java
+++ b/Habitica/src/main/java/com/magicmicky/habitrpgwrapper/lib/models/HabitRPGUser.java
@@ -31,6 +31,8 @@ public class HabitRPGUser extends BaseModel {
List todos;
List rewards;
List habits;
+ List challengeList;
+
List tags;
@Column
@PrimaryKey
@@ -106,6 +108,24 @@ public class HabitRPGUser extends BaseModel {
private TasksOrder tasksOrder;
+ private List challenges;
+
+
+ @OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "challengeList")
+ public List getChallengeList() {
+ if (challengeList == null) {
+ challengeList = new Select()
+ .from(Challenge.class)
+ .where(Condition.column("user_id").eq(this.id))
+ .queryList();
+ }
+ return challengeList;
+ }
+
+ public void setChallengeList(List challenges) {
+ this.challengeList = challenges;
+ }
+
public Preferences getPreferences() {
return preferences;
}
@@ -307,6 +327,15 @@ public class HabitRPGUser extends BaseModel {
this.pushDevices = pushDevices;
}
+ public List getChallenges() {
+ return challenges;
+ }
+
+ public void setChallenges(List challenges) {
+ this.challenges = challenges;
+ }
+
+
@Override
public void save() {
// We need to set the user_id to all other objects
@@ -345,6 +374,18 @@ public class HabitRPGUser extends BaseModel {
}
}
+ List challenges = new ArrayList<>();
+
+ for (String s : getChallenges()) {
+ Challenge challenge = new Challenge();
+ challenge.id = s;
+ challenge.user_id = id;
+
+ challenges.add(challenge);
+ }
+
+ setChallengeList(challenges);
+
super.save();
}