diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index fc47a900d..af9c85fd1 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -150,7 +150,7 @@ android {
buildConfigField "String", "TESTING_LEVEL", "\"production\""
multiDexEnabled true
- versionCode 2140
+ versionCode 2141
versionName "1.10"
}
diff --git a/Habitica/proguard-rules.pro b/Habitica/proguard-rules.pro
index 1da078bc2..001931548 100644
--- a/Habitica/proguard-rules.pro
+++ b/Habitica/proguard-rules.pro
@@ -63,8 +63,8 @@
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-#keep models
--keep class com.habitrpg.android.habitica.models.** { *; }
+#keep Habitica code
+-keep class com.habitrpg.android.habitica.** { *; }
#realm
-keep class io.realm.annotations.RealmModule
diff --git a/Habitica/res/drawable/achievement_badge_bg.xml b/Habitica/res/drawable/achievement_badge_bg.xml
new file mode 100644
index 000000000..47576caee
--- /dev/null
+++ b/Habitica/res/drawable/achievement_badge_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/achievement_section_badge_bg.xml b/Habitica/res/drawable/achievement_section_badge_bg.xml
new file mode 100644
index 000000000..ae119c105
--- /dev/null
+++ b/Habitica/res/drawable/achievement_section_badge_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/circle_gray600.xml b/Habitica/res/drawable/circle_gray600.xml
new file mode 100644
index 000000000..e2ac51390
--- /dev/null
+++ b/Habitica/res/drawable/circle_gray600.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/drawable/ic_round_view_list_24px.xml b/Habitica/res/drawable/ic_round_view_list_24px.xml
new file mode 100644
index 000000000..01cb8ce9a
--- /dev/null
+++ b/Habitica/res/drawable/ic_round_view_list_24px.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/Habitica/res/drawable/ic_round_view_module_24px.xml b/Habitica/res/drawable/ic_round_view_module_24px.xml
new file mode 100644
index 000000000..d7388b410
--- /dev/null
+++ b/Habitica/res/drawable/ic_round_view_module_24px.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/Habitica/res/layout/achievement_grid_item.xml b/Habitica/res/layout/achievement_grid_item.xml
new file mode 100644
index 000000000..6ed1113d8
--- /dev/null
+++ b/Habitica/res/layout/achievement_grid_item.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/achievement_list_item.xml b/Habitica/res/layout/achievement_list_item.xml
new file mode 100644
index 000000000..4c085896d
--- /dev/null
+++ b/Habitica/res/layout/achievement_list_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+ r
+
\ No newline at end of file
diff --git a/Habitica/res/layout/achievement_quest_item.xml b/Habitica/res/layout/achievement_quest_item.xml
new file mode 100644
index 000000000..e02d3234a
--- /dev/null
+++ b/Habitica/res/layout/achievement_quest_item.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/achievement_section_header.xml b/Habitica/res/layout/achievement_section_header.xml
new file mode 100644
index 000000000..edb4961d7
--- /dev/null
+++ b/Habitica/res/layout/achievement_section_header.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/navigation/navigation.xml b/Habitica/res/navigation/navigation.xml
index 9287e4c5f..b5278972c 100644
--- a/Habitica/res/navigation/navigation.xml
+++ b/Habitica/res/navigation/navigation.xml
@@ -289,4 +289,13 @@
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 179521a54..759d54728 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -919,4 +919,10 @@
Pin
Unpin
Take me Back
+ Achievements
+ Basic Achievements
+ Seasonal Achievements
+ Special Achievements
+ Switch to list view
+ Switch to grid view
diff --git a/Habitica/res/values/styles.habitica.xml b/Habitica/res/values/styles.habitica.xml
index eba9a313e..84196ec34 100644
--- a/Habitica/res/values/styles.habitica.xml
+++ b/Habitica/res/values/styles.habitica.xml
@@ -121,4 +121,9 @@
- 12sp
- 0.035
+
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java
index 0979c8a8f..76e26c9f3 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/ApiService.java
@@ -1,6 +1,6 @@
package com.habitrpg.android.habitica.api;
-import com.habitrpg.android.habitica.models.AchievementResult;
+import com.habitrpg.android.habitica.models.Achievement;
import com.habitrpg.android.habitica.models.ContentResult;
import com.habitrpg.android.habitica.models.LeaveChallengeBody;
import com.habitrpg.android.habitica.models.PurchaseValidationRequest;
@@ -284,7 +284,7 @@ public interface ApiService {
Flowable> getMemberWithUsername(@Path("username") String username);
@GET("members/{mid}/achievements")
- Flowable> getMemberAchievements(@Path("mid") String memberId);
+ Flowable>> getMemberAchievements(@Path("mid") String memberId);
@POST("members/send-private-message")
Flowable> postPrivateMessage(@Body Map messageDetails);
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/api/GSonFactoryCreator.java b/Habitica/src/main/java/com/habitrpg/android/habitica/api/GSonFactoryCreator.java
index db90b5088..ecd3f19c1 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/api/GSonFactoryCreator.java
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/api/GSonFactoryCreator.java
@@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
+import com.habitrpg.android.habitica.models.Achievement;
import com.habitrpg.android.habitica.models.ContentResult;
import com.habitrpg.android.habitica.models.FAQArticle;
import com.habitrpg.android.habitica.models.Skill;
@@ -11,8 +12,6 @@ import com.habitrpg.android.habitica.models.TutorialStep;
import com.habitrpg.android.habitica.models.WorldState;
import com.habitrpg.android.habitica.models.inventory.Customization;
import com.habitrpg.android.habitica.models.inventory.Equipment;
-import com.habitrpg.android.habitica.models.inventory.Mount;
-import com.habitrpg.android.habitica.models.inventory.Pet;
import com.habitrpg.android.habitica.models.inventory.Quest;
import com.habitrpg.android.habitica.models.inventory.QuestCollect;
import com.habitrpg.android.habitica.models.inventory.QuestDropItem;
@@ -29,6 +28,7 @@ import com.habitrpg.android.habitica.models.user.OwnedMount;
import com.habitrpg.android.habitica.models.user.OwnedPet;
import com.habitrpg.android.habitica.models.user.Purchases;
import com.habitrpg.android.habitica.models.user.User;
+import com.habitrpg.android.habitica.utils.AchievementListDeserializer;
import com.habitrpg.android.habitica.utils.BooleanAsIntAdapter;
import com.habitrpg.android.habitica.utils.ChallengeDeserializer;
import com.habitrpg.android.habitica.utils.ChallengeListDeserializer;
@@ -53,15 +53,14 @@ import com.habitrpg.android.habitica.utils.QuestDropItemsListSerialization;
import com.habitrpg.android.habitica.utils.SkillDeserializer;
import com.habitrpg.android.habitica.utils.TaskListDeserializer;
import com.habitrpg.android.habitica.utils.TaskSerializer;
-import com.habitrpg.android.habitica.utils.TutorialStepListDeserializer;
import com.habitrpg.android.habitica.utils.TaskTagDeserializer;
+import com.habitrpg.android.habitica.utils.TutorialStepListDeserializer;
import com.habitrpg.android.habitica.utils.UserDeserializer;
import com.habitrpg.android.habitica.utils.WorldStateSerialization;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
-import java.util.Map;
import io.realm.RealmList;
import retrofit2.converter.gson.GsonConverterFactory;
@@ -83,6 +82,7 @@ public class GSonFactoryCreator {
Type ownedItemListType = new TypeToken>() {}.getType();
Type ownedPetListType = new TypeToken>() {}.getType();
Type ownedMountListType = new TypeToken>() {}.getType();
+ Type achievementsListType = new TypeToken>() {}.getType();
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
@@ -113,6 +113,7 @@ public class GSonFactoryCreator {
.registerTypeAdapter(ownedItemListType, new OwnedItemListDeserializer())
.registerTypeAdapter(ownedPetListType, new OwnedPetListDeserializer())
.registerTypeAdapter(ownedMountListType, new OwnedMountListDeserializer())
+ .registerTypeAdapter(achievementsListType, new AchievementListDeserializer())
.registerTypeAdapter(Quest.class, new QuestDeserializer())
.registerTypeAdapter(Member.class, new MemberSerialization())
.registerTypeAdapter(WorldState.class, new WorldStateSerialization())
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 07de5c536..66e2d26e6 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
@@ -41,6 +41,7 @@ import com.habitrpg.android.habitica.ui.adapter.tasks.HabitsRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.TodosRecyclerViewAdapter;
import com.habitrpg.android.habitica.ui.fragments.AboutFragment;
+import com.habitrpg.android.habitica.ui.fragments.AchievementsFragment;
import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment;
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment;
import com.habitrpg.android.habitica.ui.fragments.NewsFragment;
@@ -314,4 +315,6 @@ public interface AppComponent {
void inject(@NotNull ReportMessageActivity reportMessageActivity);
void inject(@NotNull GuildDetailFragment guildDetailFragment);
+
+ void inject(@NotNull AchievementsFragment achievementsFragment);
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
index eb8b6e574..ecccbde42 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/ApiClient.kt
@@ -179,7 +179,7 @@ interface ApiClient {
fun getMember(memberId: String): Flowable
fun getMemberWithUsername(username: String): Flowable
- fun getMemberAchievements(memberId: String): Flowable
+ fun getMemberAchievements(memberId: String): Flowable>
fun postPrivateMessage(messageDetails: Map): Flowable
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
index 62f68a3ba..5d4a8371d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/InventoryRepository.kt
@@ -1,20 +1,11 @@
package com.habitrpg.android.habitica.data
-import com.habitrpg.android.habitica.models.inventory.Egg
-import com.habitrpg.android.habitica.models.inventory.Equipment
-import com.habitrpg.android.habitica.models.inventory.Food
-import com.habitrpg.android.habitica.models.inventory.HatchingPotion
-import com.habitrpg.android.habitica.models.inventory.Item
-import com.habitrpg.android.habitica.models.inventory.Mount
-import com.habitrpg.android.habitica.models.inventory.Pet
-import com.habitrpg.android.habitica.models.inventory.Quest
-import com.habitrpg.android.habitica.models.inventory.QuestContent
+import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.responses.BuyResponse
import com.habitrpg.android.habitica.models.responses.FeedResponse
import com.habitrpg.android.habitica.models.shops.Shop
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.*
-
import io.reactivex.Flowable
import io.realm.RealmResults
@@ -34,6 +25,7 @@ interface InventoryRepository : ContentRepository {
fun getOwnedPets(): Flowable>
fun getQuestContent(key: String): Flowable
+ fun getQuestContent(keys: List): Flowable>
fun getEquipment(searchedKeys: List): Flowable>
fun retrieveInAppRewards(): Flowable>
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
index 899b6330c..9729b1f0e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/SocialRepository.kt
@@ -1,10 +1,13 @@
package com.habitrpg.android.habitica.data
-import com.habitrpg.android.habitica.models.AchievementResult
+import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult
-import com.habitrpg.android.habitica.models.social.*
+import com.habitrpg.android.habitica.models.social.ChatMessage
+import com.habitrpg.android.habitica.models.social.FindUsernameResult
+import com.habitrpg.android.habitica.models.social.Group
+import com.habitrpg.android.habitica.models.social.GroupMembership
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.Single
@@ -75,7 +78,7 @@ interface SocialRepository : BaseRepository {
fun forceStartQuest(party: Group): Flowable
- fun getMemberAchievements(userId: String?): Flowable
+ fun getMemberAchievements(userId: String?): Flowable>
fun getGroupMembership(id: String): Flowable
fun getGroupMemberships(): Flowable>
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt
index 590912b04..b25ae6b31 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt
@@ -1,5 +1,7 @@
package com.habitrpg.android.habitica.data
+import com.habitrpg.android.habitica.models.Achievement
+import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Skill
import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
@@ -69,4 +71,7 @@ interface UserRepository : BaseRepository {
fun bulkAllocatePoints(user: User?, strength: Int, intelligence: Int, constitution: Int, perception: Int): Flowable
fun useCustomization(user: User?, type: String, category: String?, identifier: String): Flowable
+ fun retrieveAchievements(): Flowable>
+ fun getAchievements(): Flowable>
+ fun getQuestAchievements(): Flowable>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
index d1c920078..eae1bb3db 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt
@@ -589,7 +589,7 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener;
return apiService.getMemberWithUsername(username).compose(configureApiCallObserver())
}
- override fun getMemberAchievements(memberId: String): Flowable {
+ override fun getMemberAchievements(memberId: String): Flowable> {
return apiService.getMemberAchievements(memberId).compose(configureApiCallObserver())
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
index 5564e2ded..df46a9f2f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt
@@ -11,9 +11,13 @@ import com.habitrpg.android.habitica.models.shops.Shop
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.*
import io.reactivex.Flowable
+import io.realm.RealmList
import io.realm.RealmResults
class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClient: ApiClient, userID: String, var appConfigManager: AppConfigManager) : ContentRepositoryImpl(localRepository, apiClient, userID), InventoryRepository {
+ override fun getQuestContent(keys: List): Flowable> {
+ return localRepository.getQuestContent(keys)
+ }
override fun getQuestContent(key: String): Flowable {
return localRepository.getQuestContent(key)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
index 60ac890e0..a55dd821f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt
@@ -5,11 +5,14 @@ import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.data.local.SocialLocalRepository
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.models.AchievementResult
+import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult
-import com.habitrpg.android.habitica.models.social.*
+import com.habitrpg.android.habitica.models.social.ChatMessage
+import com.habitrpg.android.habitica.models.social.FindUsernameResult
+import com.habitrpg.android.habitica.models.social.Group
+import com.habitrpg.android.habitica.models.social.GroupMembership
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.Single
@@ -284,7 +287,7 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap
.doOnNext { localRepository.setQuestActivity(party, true) }
}
- override fun getMemberAchievements(userId: String?): Flowable {
+ override fun getMemberAchievements(userId: String?): Flowable> {
return if (userId == null) {
Flowable.empty()
} else apiClient.getMemberAchievements(userId)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
index 5e7a39eb5..0da41bc73 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt
@@ -6,6 +6,8 @@ import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.data.local.UserLocalRepository
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.Achievement
+import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Skill
import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
@@ -303,6 +305,20 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
return updateUser(user, updatePath, identifier)
}
+ override fun retrieveAchievements(): Flowable> {
+ return apiClient.getMemberAchievements(userID).doOnNext {
+ localRepository.save(it)
+ }
+ }
+
+ override fun getAchievements(): Flowable> {
+ return localRepository.getAchievements()
+ }
+
+ override fun getQuestAchievements(): Flowable> {
+ return localRepository.getQuestAchievements(userID)
+ }
+
private fun mergeUser(oldUser: User?, newUser: User): User {
if (oldUser == null || !oldUser.isValid) {
return oldUser ?: newUser
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
index 743a457e9..a9fe60fb5 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/InventoryLocalRepository.kt
@@ -24,6 +24,7 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getInAppRewards(): Flowable>
fun getQuestContent(key: String): Flowable
+ fun getQuestContent(keys: List): Flowable>
fun getEquipment(searchedKeys: List): Flowable>
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/UserLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/UserLocalRepository.kt
index 711ac1af4..0263abf08 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/UserLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/UserLocalRepository.kt
@@ -1,5 +1,7 @@
package com.habitrpg.android.habitica.data.local
+import com.habitrpg.android.habitica.models.Achievement
+import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Skill
import com.habitrpg.android.habitica.models.TutorialStep
import com.habitrpg.android.habitica.models.social.ChatMessage
@@ -20,4 +22,6 @@ interface UserLocalRepository : BaseLocalRepository {
fun getSkills(user: User): Flowable>
fun getSpecialItems(user: User): Flowable>
+ fun getAchievements(): Flowable>
+ fun getQuestAchievements(userID: String): Flowable>
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
index b1723d9c8..8b646df4a 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
@@ -19,6 +19,13 @@ import io.realm.Sort
class RealmInventoryLocalRepository(realm: Realm, private val context: Context) : RealmContentLocalRepository(realm), InventoryLocalRepository {
+ override fun getQuestContent(keys: List): Flowable> {
+ return realm.where(QuestContent::class.java)
+ .`in`("key", keys.toTypedArray())
+ .findAll()
+ .asFlowable()
+ .filter { it.isLoaded }
+ }
override fun getQuestContent(key: String): Flowable {
return realm.where(QuestContent::class.java).equalTo("key", key)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
index 71e57205d..fe583c5e3 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmUserLocalRepository.kt
@@ -1,9 +1,7 @@
package com.habitrpg.android.habitica.data.local.implementation
import com.habitrpg.android.habitica.data.local.UserLocalRepository
-import com.habitrpg.android.habitica.models.Skill
-import com.habitrpg.android.habitica.models.Tag
-import com.habitrpg.android.habitica.models.TutorialStep
+import com.habitrpg.android.habitica.models.*
import com.habitrpg.android.habitica.models.social.ChallengeMembership
import com.habitrpg.android.habitica.models.social.ChatMessage
import com.habitrpg.android.habitica.models.user.User
@@ -12,6 +10,21 @@ import io.realm.Realm
import io.realm.RealmResults
class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), UserLocalRepository {
+ override fun getAchievements(): Flowable> {
+ return realm.where(Achievement::class.java)
+ .sort("index")
+ .findAll()
+ .asFlowable()
+ .filter { it.isLoaded }
+ }
+
+ override fun getQuestAchievements(userID: String): Flowable> {
+ return realm.where(QuestAchievement::class.java)
+ .equalTo("userID", userID)
+ .findAll()
+ .asFlowable()
+ .filter { it.isLoaded }
+ }
override fun getTutorialSteps(): Flowable> = realm.where(TutorialStep::class.java).findAll().asFlowable()
.filter { it.isLoaded }
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.java
deleted file mode 100644
index c0e957f31..000000000
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.habitrpg.android.habitica.models;
-
-public class Achievement {
- public String type;
- public String title;
- public String text;
- public String icon;
- public String category;
- public String key;
- public String value;
- public boolean earned;
- public int index;
- public Integer optionalCount;
-}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.kt
new file mode 100644
index 000000000..9580c1d67
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/Achievement.kt
@@ -0,0 +1,17 @@
+package com.habitrpg.android.habitica.models
+
+import io.realm.RealmObject
+import io.realm.annotations.PrimaryKey
+
+open class Achievement : RealmObject() {
+ @PrimaryKey
+ var key: String? = null
+ var type: String? = null
+ var title: String? = null
+ var text: String? = null
+ var icon: String? = null
+ var category: String? = null
+ var earned: Boolean = false
+ var index: Int = 0
+ var optionalCount: Int? = null
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/QuestAchievement.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/QuestAchievement.kt
new file mode 100644
index 000000000..e55835c59
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/QuestAchievement.kt
@@ -0,0 +1,25 @@
+package com.habitrpg.android.habitica.models
+
+import io.realm.RealmObject
+import io.realm.annotations.Ignore
+import io.realm.annotations.PrimaryKey
+
+open class QuestAchievement: RealmObject() {
+ @PrimaryKey
+ var combinedKey: String? = null
+
+ var questKey: String? = null
+ set(value) {
+ field = value
+ combinedKey = userID + questKey
+ }
+ var userID: String? = null
+ set(value) {
+ field = value
+ combinedKey = userID + questKey
+ }
+ var count: Int = 0
+
+ @Ignore
+ var title: String? = null
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
index bf9ab3912..7deec9cae 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/User.kt
@@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.models.user
import com.google.gson.annotations.SerializedName
import com.habitrpg.android.habitica.models.Avatar
import com.habitrpg.android.habitica.models.PushDevice
+import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Tag
import com.habitrpg.android.habitica.models.invitations.Invitations
import com.habitrpg.android.habitica.models.social.ChallengeMembership
@@ -118,6 +119,11 @@ open class User : RealmObject(), Avatar {
}
var tags = RealmList()
+ var questAchievements = RealmList()
+ set(value) {
+ field = value
+ field.forEach { it.userID = id }
+ }
@Ignore
var pushDevices: List? = null
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt
index f18d9fe85..ce2abff05 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt
@@ -20,15 +20,14 @@ import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.UserStatComputer
-import com.habitrpg.android.habitica.models.AchievementGroup
-import com.habitrpg.android.habitica.models.AchievementResult
+import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.models.members.Member
import com.habitrpg.android.habitica.models.user.Outfit
import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.ui.AvatarView
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel
-import com.habitrpg.android.habitica.ui.adapter.social.AchievementAdapter
+import com.habitrpg.android.habitica.ui.adapter.social.AchievementProfileAdapter
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.helpers.bindView
@@ -208,7 +207,7 @@ class FullProfileActivity : BaseActivity() {
// Load the members achievements now
- compositeSubscription.add(socialRepository.getMemberAchievements(this.userID).subscribe(Consumer { this.fillAchievements(it) }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(socialRepository.getMemberAchievements(this.userID).subscribe(Consumer { this.fillAchievements(it) }, RxErrorHandler.handleEmptyError()))
}
private fun updatePetsMountsView(user: Member) {
@@ -223,17 +222,17 @@ class FullProfileActivity : BaseActivity() {
// region Attributes
- private fun fillAchievements(achievements: AchievementResult?) {
+ private fun fillAchievements(achievements: List?) {
if (achievements == null) {
return
}
val items = ArrayList()
- fillAchievements(achievements.basic, items)
- fillAchievements(achievements.seasonal, items)
- fillAchievements(achievements.special, items)
+ fillAchievements(R.string.basic_achievements, achievements.filter { it.category == "basic" }, items)
+ fillAchievements(R.string.seasonal_achievements, achievements.filter { it.category == "seasonal" }, items)
+ fillAchievements(R.string.special_achievements, achievements.filter { it.category == "special" }, items)
- val adapter = AchievementAdapter()
+ val adapter = AchievementProfileAdapter()
adapter.setItemList(items)
val layoutManager = androidx.recyclerview.widget.GridLayoutManager(this, 3)
@@ -252,12 +251,12 @@ class FullProfileActivity : BaseActivity() {
stopAndHideProgress(achievementProgress)
}
- private fun fillAchievements(achievementGroup: AchievementGroup, targetList: MutableList) {
+ private fun fillAchievements(labelID: Int, achievements: List, targetList: MutableList) {
// Order by ID first
- val achievementList = ArrayList(achievementGroup.achievements.values)
+ val achievementList = ArrayList(achievements)
achievementList.sortWith(Comparator { achievement, t1 -> java.lang.Double.compare(achievement.index.toDouble(), t1.index.toDouble()) })
- targetList.add(achievementGroup.label)
+ targetList.add(getString(labelID))
targetList.addAll(achievementList)
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/AchievementsAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/AchievementsAdapter.kt
new file mode 100644
index 000000000..661d2a643
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/AchievementsAdapter.kt
@@ -0,0 +1,115 @@
+package com.habitrpg.android.habitica.ui.adapter
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.facebook.drawee.view.SimpleDraweeView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.models.Achievement
+import com.habitrpg.android.habitica.models.QuestAchievement
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.helpers.bindOptionalView
+import com.habitrpg.android.habitica.ui.helpers.bindView
+
+class AchievementsAdapter: RecyclerView.Adapter() {
+
+ var useGridLayout: Boolean = false
+ var entries = listOf()
+ var questAchievements = listOf()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return when (viewType) {
+ 0 -> SectionViewHolder(parent.inflate(R.layout.achievement_section_header))
+ 3 -> QuestAchievementViewHolder(parent.inflate(R.layout.achievement_quest_item))
+ else -> AchievementViewHolder(if (useGridLayout) {
+ parent.inflate(R.layout.achievement_grid_item)
+ } else {
+ parent.inflate(R.layout.achievement_list_item)
+ })
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when {
+ entries.size > position -> when (val entry = entries[position]) {
+ is Achievement -> (holder as? AchievementViewHolder)?.bind(entry)
+ is Pair<*, *> -> (holder as? SectionViewHolder)?.bind(entry)
+ }
+ entries.size == position -> (holder as? SectionViewHolder)?.bind(Pair("Quests completed", questAchievements.size))
+ else -> (holder as? QuestAchievementViewHolder)?.bind(questAchievements[position - 1 - entries.size])
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return entries.size + questAchievements.size + 1
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when {
+ entries.size > position -> {
+ val entry = entries[position]
+ if (entry is Pair<*, *>) {
+ 0
+ } else {
+ if (useGridLayout) 1 else 2
+ }
+ }
+ entries.size == position -> 0
+ else -> 3
+ }
+ }
+
+ class SectionViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
+ private val titleView: TextView by bindView(R.id.title)
+ private val countView: TextView by bindView(R.id.count_label)
+
+ fun bind(category: Pair<*, *>) {
+ titleView.text = category.first as? String
+ countView.text = category.second.toString()
+ }
+ }
+
+ class AchievementViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
+ private var achievement: Achievement? = null
+
+ private val achievementContainer: ViewGroup? by bindOptionalView(R.id.achievement_container)
+ private val achievementIconView: SimpleDraweeView by bindView(R.id.achievement_icon)
+ private val achievementCountView: TextView by bindView(R.id.achievement_count_label)
+ private val achievementTitleView: TextView by bindView(R.id.achievement_title)
+ private val achievementDescriptionView: TextView? by bindOptionalView(R.id.achievement_description)
+
+ fun bind(achievement: Achievement) {
+ this.achievement = achievement
+ val iconName = if (achievement.earned) {
+ achievement.icon + "2x"
+ } else {
+ "achievement-unearned2x"
+ }
+ DataBindingUtils.loadImage(achievementIconView, iconName)
+ achievementTitleView.text = achievement.title
+ achievementDescriptionView?.text = achievement.text
+ if (achievement.optionalCount ?: 0 > 0) {
+ achievementCountView.visibility = View.VISIBLE
+ achievementCountView.text = achievement.optionalCount.toString()
+ } else {
+ achievementCountView.visibility = View.GONE
+ }
+ achievementContainer?.clipToOutline = true
+ }
+ }
+
+ class QuestAchievementViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
+ private var achievement: QuestAchievement? = null
+
+ private val achievementCountView: TextView by bindView(R.id.achievement_count_label)
+ private val achievementTitleView: TextView by bindView(R.id.achievement_title)
+
+ fun bind(achievement: QuestAchievement) {
+ this.achievement = achievement
+ achievementTitleView.text = achievement.title
+ achievementCountView.text = achievement.count.toString()
+ }
+ }
+}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementProfileAdapter.kt
similarity index 95%
rename from Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementAdapter.kt
rename to Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementProfileAdapter.kt
index 0f4939a66..63ef0f33b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/AchievementProfileAdapter.kt
@@ -20,7 +20,7 @@ import com.habitrpg.android.habitica.ui.helpers.bindView
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.android.habitica.ui.views.HabiticaAlertDialog
-class AchievementAdapter : RecyclerView.Adapter() {
+class AchievementProfileAdapter : RecyclerView.Adapter() {
var itemType: String? = null
var activity: MainActivity? = null
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt
new file mode 100644
index 000000000..671a2f723
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/AchievementsFragment.kt
@@ -0,0 +1,136 @@
+package com.habitrpg.android.habitica.ui.fragments
+
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.*
+import androidx.core.content.ContextCompat
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.components.AppComponent
+import com.habitrpg.android.habitica.data.InventoryRepository
+import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
+import com.habitrpg.android.habitica.helpers.RxErrorHandler
+import com.habitrpg.android.habitica.models.Achievement
+import com.habitrpg.android.habitica.ui.adapter.AchievementsAdapter
+import com.habitrpg.android.habitica.ui.helpers.bindView
+import com.habitrpg.android.habitica.ui.helpers.resetViews
+import io.reactivex.functions.Action
+import io.reactivex.functions.Consumer
+import io.reactivex.rxkotlin.combineLatest
+import io.realm.RealmResults
+import javax.inject.Inject
+
+class AchievementsFragment: BaseMainFragment(), SwipeRefreshLayout.OnRefreshListener {
+
+ @Inject
+ lateinit var inventoryRepository: InventoryRepository
+
+ private var menuID: Int = 0
+ private lateinit var adapter: AchievementsAdapter
+ private val layoutManager = GridLayoutManager(activity, 2)
+ private var useGridLayout = true
+ set(value) {
+ field = value
+ adapter.useGridLayout = value
+ adapter.notifyDataSetChanged()
+ }
+
+ private val recyclerView: RecyclerView by bindView(R.id.recyclerView)
+ private val refreshLayout: SwipeRefreshLayout by bindView(R.id.refreshLayout)
+
+ override fun injectFragment(component: AppComponent) {
+ component.inject(this)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+ hidesToolbar = true
+ super.onCreateView(inflater, container, savedInstanceState)
+ adapter = AchievementsAdapter()
+ return inflater.inflate(R.layout.fragment_refresh_recyclerview, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ resetViews()
+
+ recyclerView.layoutManager = layoutManager
+ recyclerView.adapter = adapter
+ adapter.useGridLayout = useGridLayout
+ context?.let { recyclerView.background = ColorDrawable(ContextCompat.getColor(it, R.color.white)) }
+
+ layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int): Int {
+ return if (adapter.getItemViewType(position) == 1) {
+ 1
+ } else {
+ 2
+ }
+ }
+ }
+
+ refreshLayout.setOnRefreshListener(this)
+
+ compositeSubscription.add(userRepository.getAchievements().subscribe(Consumer> {
+ val entries = mutableListOf()
+ var lastCategory = ""
+ it.forEach { achievement ->
+ val categoryIdentifier = achievement.category ?: ""
+ if (categoryIdentifier != lastCategory) {
+ val category = Pair(categoryIdentifier, it.count { check ->
+ check.category == categoryIdentifier && check.earned
+ })
+ entries.add(category)
+ lastCategory = categoryIdentifier
+ }
+ entries.add(achievement)
+ }
+ adapter.entries = entries
+ adapter.notifyDataSetChanged()
+ }, RxErrorHandler.handleEmptyError()))
+ compositeSubscription.add(userRepository.getQuestAchievements()
+ .combineLatest(userRepository.getQuestAchievements()
+ .map { it.mapNotNull { achievement -> achievement.questKey } }
+ .flatMap { inventoryRepository.getQuestContent(it) })
+ .subscribeWithErrorHandler(Consumer { result ->
+ val achievements = result.first.map {achievement ->
+ val questContent = result.second.firstOrNull { achievement.questKey == it.key }
+ achievement.title = questContent?.text
+ achievement
+ }
+ adapter.questAchievements = achievements
+ adapter.notifyDataSetChanged()
+ }))
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
+ if (useGridLayout) {
+ val menuItem = menu?.add(R.string.switch_to_list_view)
+ menuID = menuItem?.itemId ?: 0
+ menuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ menuItem?.setIcon(R.drawable.ic_round_view_list_24px)
+
+ } else {
+ val menuItem = menu?.add(R.string.switch_to_grid_view)
+ menuID = menuItem?.itemId ?: 0
+ menuItem?.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ menuItem?.setIcon(R.drawable.ic_round_view_module_24px)
+ }
+ super.onCreateOptionsMenu(menu, inflater)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+ if (item?.itemId == menuID) {
+ useGridLayout = !useGridLayout
+ activity?.invalidateOptionsMenu()
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onRefresh() {
+ compositeSubscription.add(userRepository.retrieveAchievements().subscribe(Consumer {
+ }, RxErrorHandler.handleEmptyError(), Action { refreshLayout.isRefreshing = false }))
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
index 5ca92abc9..e08319ad2 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt
@@ -208,6 +208,7 @@ class NavigationDrawerFragment : DialogFragment() {
items.add(HabiticaDrawerItem(R.id.tasksFragment, SIDEBAR_TASKS, context.getString(R.string.sidebar_tasks)))
items.add(HabiticaDrawerItem(R.id.skillsFragment, SIDEBAR_SKILLS, context.getString(R.string.sidebar_skills)))
items.add(HabiticaDrawerItem(R.id.statsFragment, SIDEBAR_STATS, context.getString(R.string.sidebar_stats)))
+ items.add(HabiticaDrawerItem(R.id.achievementsFragment, SIDEBAR_ACHIEVEMENTS, context.getString(R.string.sidebar_achievements)))
items.add(HabiticaDrawerItem(0, SIDEBAR_SOCIAL, context.getString(R.string.sidebar_section_social), true))
items.add(HabiticaDrawerItem(R.id.tavernFragment, SIDEBAR_TAVERN, context.getString(R.string.sidebar_tavern), false, false))
items.add(HabiticaDrawerItem(R.id.partyFragment, SIDEBAR_PARTY, context.getString(R.string.sidebar_party)))
@@ -327,6 +328,7 @@ class NavigationDrawerFragment : DialogFragment() {
const val SIDEBAR_TASKS = "tasks"
const val SIDEBAR_SKILLS = "skills"
const val SIDEBAR_STATS = "stats"
+ const val SIDEBAR_ACHIEVEMENTS = "achievements"
const val SIDEBAR_SOCIAL = "social"
const val SIDEBAR_INBOX = "inbox"
const val SIDEBAR_TAVERN = "tavern"
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/QuestDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/QuestDetailFragment.kt
index 2d29ad1b1..e95fc3ebf 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/QuestDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/QuestDetailFragment.kt
@@ -232,15 +232,17 @@ class QuestDetailFragment : BaseMainFragment() {
}
private fun onQuestBegin() {
- context?.let {
- val alert = HabiticaAlertDialog(it)
+ val context = context
+ if (context != null) {
+ val alert = HabiticaAlertDialog(context)
alert.setMessage(beginQuestMessage)
alert.addButton(R.string.yes, true) { _, _ ->
- party.notNull { party ->
- socialRepository.forceStartQuest(party)
- .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
- }
- }
+ val party = party
+ if (party != null) {
+ socialRepository.forceStartQuest(party)
+ .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
+ }
+ }
alert.addButton(R.string.no, false)
alert.show()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
index 275e96b81..9015e8c82 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyDetailFragment.kt
@@ -183,8 +183,9 @@ class PartyDetailFragment : BaseFragment() {
}
private fun leaveParty() {
- activity?.let {
- val alert = HabiticaAlertDialog(it)
+ val context = context
+ if (context != null) {
+ val alert = HabiticaAlertDialog(context)
alert.setMessage(R.string.leave_party_confirmation)
alert.addButton(R.string.yes, true) { _, _ ->
viewModel?.leaveGroup { }
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaAlertDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaAlertDialog.kt
index 8b14769c2..e56e84559 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaAlertDialog.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaAlertDialog.kt
@@ -166,7 +166,9 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
val buttonIndex = buttonsWrapper.childCount
buttonView.setOnClickListener {
weakThis.get()?.let { it1 ->
- function?.invoke(it1, buttonIndex)
+ if (function != null) {
+ function(it1, buttonIndex)
+ }
dismiss()
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/AchievementListDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/AchievementListDeserializer.kt
new file mode 100644
index 000000000..5a6f35e1a
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/AchievementListDeserializer.kt
@@ -0,0 +1,33 @@
+package com.habitrpg.android.habitica.utils
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.habitrpg.android.habitica.extensions.getAsString
+import com.habitrpg.android.habitica.models.Achievement
+import java.lang.reflect.Type
+
+class AchievementListDeserializer: JsonDeserializer> {
+
+ override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): List {
+ val achievements = mutableListOf()
+ for (categoryEntry in json?.asJsonObject?.entrySet() ?: emptySet()) {
+ val categoryIdentifier = categoryEntry.key
+ for (entry in categoryEntry.value.asJsonObject.getAsJsonObject("achievements").entrySet()) {
+ var obj = entry.value.asJsonObject
+ val achievement = Achievement()
+ achievement.key = entry.key
+ achievement.category = categoryIdentifier
+ achievement.earned = obj.get("earned").asBoolean
+ achievement.title = obj.getAsString("title")
+ achievement.text = obj.getAsString("text")
+ achievement.icon = obj.getAsString("icon")
+ achievement.index = if (obj.has("index")) obj["index"].asInt else 0
+ achievement.optionalCount = if (obj.has("optionalCount")) obj["optionalCount"].asInt else 0
+ achievements.add(achievement)
+ }
+ }
+ return achievements
+ }
+
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
index 689724bc6..2ab91ec6f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/UserDeserializer.kt
@@ -1,6 +1,5 @@
package com.habitrpg.android.habitica.utils
-import android.os.Trace
import com.google.firebase.perf.FirebasePerformance
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
@@ -8,6 +7,7 @@ import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import com.google.gson.reflect.TypeToken
import com.habitrpg.android.habitica.models.PushDevice
+import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.models.Tag
import com.habitrpg.android.habitica.models.inventory.Quest
import com.habitrpg.android.habitica.models.invitations.Invitations
@@ -118,12 +118,22 @@ class UserDeserializer : JsonDeserializer {
}
if (obj.has("achievements")) {
- if (obj.getAsJsonObject("achievements").has("streak")) {
+ val achievements = obj.getAsJsonObject("achievements")
+ if (achievements.has("streak")) {
try {
user.streakCount = obj.getAsJsonObject("achievements").get("streak").asInt
} catch (ignored: UnsupportedOperationException) {
}
-
+ }
+ if (achievements.has("quests")) {
+ val questAchievements = RealmList()
+ for (entry in achievements.getAsJsonObject("quests").entrySet()) {
+ val questAchievement = QuestAchievement()
+ questAchievement.questKey = entry.key
+ questAchievement.count = entry.value.asInt
+ questAchievements.add(questAchievement)
+ }
+ user.questAchievements = questAchievements
}
}