Improve content loading

This commit is contained in:
Phillip Thelen 2019-04-08 17:41:54 +02:00
parent 008fdc1d22
commit 9dc2a79d17
28 changed files with 263 additions and 590 deletions

View file

@ -150,7 +150,7 @@ android {
buildConfigField "String", "STORE", "\"google\""
multiDexEnabled true
versionCode 2081
versionCode 2083
versionName "1.9"
}

View file

@ -28,6 +28,7 @@ import com.habitrpg.android.habitica.models.social.FindUsernameResult;
import com.habitrpg.android.habitica.models.social.Group;
import com.habitrpg.android.habitica.models.tasks.Task;
import com.habitrpg.android.habitica.models.tasks.TaskList;
import com.habitrpg.android.habitica.models.user.OwnedItem;
import com.habitrpg.android.habitica.models.user.Purchases;
import com.habitrpg.android.habitica.models.user.User;
import com.habitrpg.android.habitica.utils.BooleanAsIntAdapter;
@ -38,24 +39,21 @@ import com.habitrpg.android.habitica.utils.ChatMessageListDeserializer;
import com.habitrpg.android.habitica.utils.ContentDeserializer;
import com.habitrpg.android.habitica.utils.CustomizationDeserializer;
import com.habitrpg.android.habitica.utils.DateDeserializer;
import com.habitrpg.android.habitica.utils.EggListDeserializer;
import com.habitrpg.android.habitica.utils.EquipmentListDeserializer;
import com.habitrpg.android.habitica.utils.FAQArticleListDeserilializer;
import com.habitrpg.android.habitica.utils.FeedResponseDeserializer;
import com.habitrpg.android.habitica.utils.FindUsernameResultDeserializer;
import com.habitrpg.android.habitica.utils.FoodListDeserializer;
import com.habitrpg.android.habitica.utils.GroupSerialization;
import com.habitrpg.android.habitica.utils.HatchingPotionListDeserializer;
import com.habitrpg.android.habitica.utils.MemberSerialization;
import com.habitrpg.android.habitica.utils.MountListDeserializer;
import com.habitrpg.android.habitica.utils.MountMapDeserializer;
import com.habitrpg.android.habitica.utils.OwnedItemListDeserializer;
import com.habitrpg.android.habitica.utils.PetListDeserializer;
import com.habitrpg.android.habitica.utils.PetMapDeserializer;
import com.habitrpg.android.habitica.utils.PurchasedDeserializer;
import com.habitrpg.android.habitica.utils.QuestCollectDeserializer;
import com.habitrpg.android.habitica.utils.QuestDeserializer;
import com.habitrpg.android.habitica.utils.QuestDropItemsListSerialization;
import com.habitrpg.android.habitica.utils.QuestListDeserializer;
import com.habitrpg.android.habitica.utils.SkillDeserializer;
import com.habitrpg.android.habitica.utils.TaskListDeserializer;
import com.habitrpg.android.habitica.utils.TaskSerializer;
@ -81,10 +79,6 @@ public class GSonFactoryCreator {
Type tutorialStepListType = new TypeToken<RealmList<TutorialStep>>() {}.getType();
Type faqArticleListType = new TypeToken<RealmList<FAQArticle>>() {}.getType();
Type itemDataListType = new TypeToken<RealmList<Equipment>>() {}.getType();
Type eggListType = new TypeToken<RealmList<Egg>>() {}.getType();
Type foodListType = new TypeToken<RealmList<Food>>() {}.getType();
Type hatchingPotionListType = new TypeToken<RealmList<HatchingPotion>>() {}.getType();
Type questContentListType = new TypeToken<RealmList<QuestContent>>() {}.getType();
Type petMapType = new TypeToken<Map<String, Pet>>() {}.getType();
Type mountMapType = new TypeToken<Map<String, Mount>>() {}.getType();
Type petListType = new TypeToken<RealmList<Pet>>() {}.getType();
@ -94,6 +88,7 @@ public class GSonFactoryCreator {
Type challengeListType = new TypeToken<List<Challenge>>() {}.getType();
Type challengeRealmListType = new TypeToken<RealmList<Challenge>>() {}.getType();
Type questDropItemListType = new TypeToken<RealmList<QuestDropItem>>() {}.getType();
Type ownedItemListType = new TypeToken<RealmList<OwnedItem>>() {}.getType();
//Exclusion strategy needed for DBFlow https://github.com/Raizlabs/DBFlow/issues/121
Gson gson = new GsonBuilder()
@ -109,10 +104,6 @@ public class GSonFactoryCreator {
.registerTypeAdapter(Group.class, new GroupSerialization())
.registerTypeAdapter(Date.class, new DateDeserializer())
.registerTypeAdapter(itemDataListType, new EquipmentListDeserializer())
.registerTypeAdapter(eggListType, new EggListDeserializer())
.registerTypeAdapter(foodListType, new FoodListDeserializer())
.registerTypeAdapter(hatchingPotionListType, new HatchingPotionListDeserializer())
.registerTypeAdapter(questContentListType, new QuestListDeserializer())
.registerTypeAdapter(petListType, new PetListDeserializer())
.registerTypeAdapter(mountListType, new MountListDeserializer())
.registerTypeAdapter(petMapType, new PetMapDeserializer())
@ -128,6 +119,7 @@ public class GSonFactoryCreator {
.registerTypeAdapter(challengeListType, new ChallengeListDeserializer())
.registerTypeAdapter(challengeRealmListType, new ChallengeListDeserializer())
.registerTypeAdapter(questDropItemListType, new QuestDropItemsListSerialization())
.registerTypeAdapter(ownedItemListType, new OwnedItemListDeserializer())
.registerTypeAdapter(Quest.class, new QuestDeserializer())
.registerTypeAdapter(Member.class, new MemberSerialization())
.registerTypeAdapter(WorldState.class, new WorldStateSerialization())

View file

@ -14,6 +14,7 @@ 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.Items
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
@ -36,13 +37,13 @@ interface InventoryRepository : ContentRepository {
fun getOwnedPets(): Flowable<RealmResults<Pet>>
fun getQuestContent(key: String): Flowable<QuestContent>
fun getItems(searchedKeys: List<String>): Flowable<RealmResults<Equipment>>
fun getEquipment(searchedKeys: List<String>): Flowable<RealmResults<Equipment>>
fun retrieveInAppRewards(): Flowable<List<ShopItem>>
fun getOwnedEquipment(type: String): Flowable<RealmResults<Equipment>>
fun getOwnedItems(itemClass: Class<out Item>, user: User?): Flowable<out RealmResults<out Item>>
fun getOwnedItems(user: User): Flowable<out Map<String, Item>>
fun getOwnedItems(itemType: String): Flowable<RealmResults<OwnedItem>>
fun getOwnedItems(user: User): Flowable<Map<String, OwnedItem>>
fun getEquipment(key: String): Flowable<Equipment>
@ -84,4 +85,5 @@ interface InventoryRepository : ContentRepository {
fun purchaseItem(purchaseType: String, key: String): Flowable<Any>
fun togglePinnedItem(item: ShopItem): Flowable<List<ShopItem>>
fun getItems(itemClass: Class<out Item>, keys: Array<String>, user: User?): Flowable<out RealmResults<out Item>>
}

View file

@ -10,6 +10,7 @@ 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.Items
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.realm.RealmResults
@ -20,7 +21,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getQuestContent(key)
}
override fun getItems(searchedKeys: List<String>): Flowable<RealmResults<Equipment>> {
override fun getEquipment(searchedKeys: List<String>): Flowable<RealmResults<Equipment>> {
return localRepository.getEquipment(searchedKeys)
}
@ -44,14 +45,18 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getOwnedEquipment()
}
override fun getOwnedItems(itemClass: Class<out Item>, user: User?): Flowable<out RealmResults<out Item>> {
return localRepository.getOwnedItems(itemClass, user)
override fun getOwnedItems(itemType: String): Flowable<RealmResults<OwnedItem>> {
return localRepository.getOwnedItems(itemType, userID)
}
override fun getOwnedItems(user: User): Flowable<out Map<String, Item>> {
override fun getOwnedItems(user: User): Flowable<Map<String, OwnedItem>> {
return localRepository.getOwnedItems(user)
}
override fun getItems(itemClass: Class<out Item>, keys: Array<String>, user: User?): Flowable<out RealmResults<out Item>> {
return localRepository.getItems(itemClass, keys, user)
}
override fun getEquipment(key: String): Flowable<Equipment> {
return localRepository.getEquipment(key)
}
@ -105,7 +110,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
}
override fun changeOwnedCount(type: String, key: String, amountToAdd: Int) {
localRepository.changeOwnedCount(type, key, amountToAdd)
localRepository.changeOwnedCount(type, key, userID, amountToAdd)
}
override fun sellItem(user: User?, type: String, key: String): Flowable<User> {
@ -124,7 +129,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
val newItems = realm.copyToRealmOrUpdate(items)
user.items = newItems
} else {
item.owned = item.owned - 1
//item.owned = item.owned - 1
}
val stats = user1.stats
if (stats != null) {
@ -173,12 +178,12 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return@doOnNext
}
localRepository.executeTransaction {
val newEquipped = items.gear.equipped
val newEquipped = items.gear?.equipped
val oldEquipped = user.items?.gear?.equipped
val newCostume = items.gear.costume
val newCostume = items.gear?.costume
val oldCostume = user.items?.gear?.costume
oldEquipped?.updateWith(newEquipped)
oldCostume?.updateWith(newCostume)
newEquipped?.let { equipped -> oldEquipped?.updateWith(equipped) }
newCostume?.let { costume -> oldCostume?.updateWith(costume) }
user.items?.currentMount = items.currentMount
user.items?.currentPet = items.currentPet
user.balance = user.balance
@ -189,7 +194,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
override fun feedPet(pet: Pet, food: Food): Flowable<FeedResponse> {
return apiClient.feedPet(pet.key, food.key)
.doOnNext { feedResponse ->
localRepository.changeOwnedCount(food, -1)
localRepository.changeOwnedCount("food", food.key, userID, -1)
localRepository.executeTransaction { pet.trained = feedResponse.value }
}
}
@ -197,15 +202,15 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
override fun hatchPet(egg: Egg, hatchingPotion: HatchingPotion): Flowable<Items> {
return apiClient.hatchPet(egg.key, hatchingPotion.key)
.doOnNext {
localRepository.changeOwnedCount(egg, -1)
localRepository.changeOwnedCount(hatchingPotion, -1)
localRepository.changeOwnedCount("egg", egg.key, userID, -1)
localRepository.changeOwnedCount("hatchingPotions", hatchingPotion.key, userID, -1)
localRepository.changePetFeedStatus(egg.key+"-"+hatchingPotion.key, 5)
}
}
override fun inviteToQuest(quest: QuestContent): Flowable<Quest> {
return apiClient.inviteToQuest("party", quest.key)
.doOnNext { localRepository.changeOwnedCount(quest, -1) }
.doOnNext { localRepository.changeOwnedCount("quests", quest.key, userID, -1) }
}
override fun buyItem(user: User?, id: String, value: Double): Flowable<BuyResponse> {

View file

@ -2,6 +2,7 @@ package com.habitrpg.android.habitica.data.local
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.realm.RealmResults
@ -26,8 +27,9 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getOwnedEquipment(type: String): Flowable<RealmResults<Equipment>>
fun getOwnedItems(itemClass: Class<out Item>, user: User?): Flowable<out RealmResults<out Item>>
fun getOwnedItems(user: User): Flowable<out Map<String, Item>>
fun getItems(itemClass: Class<out Item>, keys: Array<String>, user: User?): Flowable<out RealmResults<out Item>>
fun getOwnedItems(itemType: String, userID: String): Flowable<RealmResults<OwnedItem>>
fun getOwnedItems(user: User): Flowable<Map<String, OwnedItem>>
fun getEquipment(key: String): Flowable<Equipment>
fun getMounts(type: String, group: String): Flowable<RealmResults<Mount>>
@ -37,8 +39,8 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun updateOwnedEquipment(user: User)
fun changeOwnedCount(type: String, key: String, amountToAdd: Int)
fun changeOwnedCount(item: Item, amountToAdd: Int?)
fun changeOwnedCount(type: String, key: String, userID: String, amountToAdd: Int)
fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?)
fun getItem(type: String, key: String): Flowable<Item>

View file

@ -6,6 +6,7 @@ import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import io.reactivex.Flowable
import io.reactivex.functions.Consumer
@ -62,44 +63,39 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
override fun getOwnedItems(itemClass: Class<out Item>, user: User?): Flowable<out RealmResults<out Item>> {
var query = realm.where(itemClass)
if (SpecialItem::class.java.isAssignableFrom(itemClass)) {
if (user?.purchased?.plan != null) {
val mysticItem: SpecialItem = if (query.count() == 0L) {
SpecialItem.makeMysteryItem(context)
} else {
getUnmanagedCopy((query.findFirst() as SpecialItem?)!!)
}
mysticItem.owned = user.purchased?.plan?.mysteryItemCount
this.save(mysticItem)
}
} else {
query = query.greaterThan("owned", 0)
}
return query.findAllAsync().asFlowable()
override fun getOwnedItems(itemType: String, userID: String): Flowable<RealmResults<OwnedItem>> {
return realm.where(OwnedItem::class.java)
.equalTo("itemType", itemType)
.equalTo("userID", userID)
.findAll()
.asFlowable()
.filter { it.isLoaded }
}
override fun getOwnedItems(user: User): Flowable<out Map<String, Item>> {
override fun getItems(itemClass: Class<out Item>, keys: Array<String>, user: User?): Flowable<out RealmResults<out Item>> {
return realm.where(itemClass).`in`("key", keys).findAllAsync().asFlowable()
.filter { it.isLoaded }
}
override fun getOwnedItems(user: User): Flowable<Map<String, OwnedItem>> {
return Flowable.combineLatest(
getOwnedItems(Egg::class.java, user),
getOwnedItems(HatchingPotion::class.java, user),
getOwnedItems(Food::class.java, user),
getOwnedItems(QuestContent::class.java, user),
getOwnedItems("eggs", user.id ?: ""),
getOwnedItems("hatchingPotions", user.id ?: ""),
getOwnedItems("food", user.id ?: ""),
getOwnedItems("questContent", user.id ?: ""),
Function4 { eggs, hatchingPotions, food, quests ->
val items = HashMap<String, Item>()
val items = HashMap<String, OwnedItem>()
for (item in eggs) {
items[item.key + "-" + item.type] = item
items[item.key + "-" + item.itemType] = item
}
for (item in hatchingPotions) {
items[item.key + "-" + item.type] = item
items[item.key + "-" + item.itemType] = item
}
for (item in food) {
items[item.key + "-" + item.type] = item
items[item.key + "-" + item.itemType] = item
}
for (item in quests) {
items[item.key + "-" + item.type] = item
items[item.key + "-" + item.itemType] = item
}
items
}
@ -196,16 +192,26 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
}
override fun changeOwnedCount(type: String, key: String, amountToAdd: Int) {
getItem(type, key).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError())
override fun changeOwnedCount(type: String, key: String, userID: String, amountToAdd: Int) {
getOwnedItem(type, key, userID).firstElement().subscribe( Consumer { changeOwnedCount(it, amountToAdd)}, RxErrorHandler.handleEmptyError())
}
override fun changeOwnedCount(item: Item, amountToAdd: Int?) {
override fun changeOwnedCount(item: OwnedItem, amountToAdd: Int?) {
amountToAdd.notNull { amount ->
realm.executeTransaction { item.owned = item.owned + amount }
realm.executeTransaction { item.numberOwned = item.numberOwned + amount }
}
}
fun getOwnedItem(type: String, key: String, userID: String): Flowable<OwnedItem> {
return realm.where(OwnedItem::class.java)
.equalTo("itemType", type)
.equalTo("key", key)
.equalTo("userID", userID)
.findFirstAsync()
.asFlowable<OwnedItem>()
.filter { realmObject -> realmObject.isLoaded }
}
override fun getItem(type: String, key: String): Flowable<Item> {
val itemClass: Class<out RealmObject> = when (type) {
"eggs" -> Egg::class.java
@ -226,7 +232,7 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
val item = realm.where(SpecialItem::class.java).equalTo("isMysteryItem", true).findFirst()
realm.executeTransaction {
if (item != null && item.isValid) {
item.owned = item.owned - 1
//item.owned = item.owned - 1
}
if (user.isValid) {
user.purchased?.plan?.mysteryItemCount = (user.purchased?.plan?.mysteryItemCount ?: 0) - 1

View file

@ -8,7 +8,7 @@ public class Egg extends RealmObject implements Item {
@PrimaryKey
String key;
String text, notes;
Integer value, owned;
Integer value;
String adjective, mountText;
Integer stableOwned, stableTotal;
@ -57,20 +57,10 @@ public class Egg extends RealmObject implements Item {
return key;
}
@Override
public void setOwned(int size) {
owned = size;
}
public String getText() {
return text;
}
@Override
public Integer getOwned() {
return owned;
}
@Override
public Integer getValue() {
return value;

View file

@ -8,7 +8,7 @@ public class Food extends RealmObject implements Item {
@PrimaryKey
String key;
String text, notes;
Integer value, owned;
Integer value;
String target, article;
Boolean canDrop;
@ -45,20 +45,11 @@ public class Food extends RealmObject implements Item {
return key;
}
@Override
public void setOwned(int size) {
owned = size;
}
@Override
public String getText() {
return text;
}
public Integer getOwned() {
return owned;
}
@Override
public Integer getValue() {
return value;

View file

@ -8,7 +8,8 @@ public class HatchingPotion extends RealmObject implements Item {
@PrimaryKey
String key;
String text, notes;
Integer value, owned; Boolean limited, premium;
Integer value;
Boolean limited, premium;
public Boolean getLimited() {
return limited;
@ -36,20 +37,10 @@ public class HatchingPotion extends RealmObject implements Item {
return key;
}
@Override
public void setOwned(int size) {
owned = size;
}
public String getText() {
return text;
}
@Override
public Integer getOwned() {
return owned;
}
@Override
public Integer getValue() {
return value;

View file

@ -8,11 +8,7 @@ public interface Item extends RealmModel {
String getKey();
void setOwned(int size);
String getText();
Integer getOwned();
Integer getValue();
}

View file

@ -19,7 +19,6 @@ open class QuestContent : RealmObject(), Item {
internal var text: String = ""
var notes: String = ""
internal var value: Int = 0
internal var owned: Int = 0
var previous: String? = null
var lvl: Int = 0
var isCanBuy: Boolean = false
@ -59,18 +58,10 @@ open class QuestContent : RealmObject(), Item {
return key
}
override fun setOwned(size: Int) {
owned = size
}
override fun getText(): String {
return text
}
override fun getOwned(): Int? {
return owned
}
override fun getValue(): Int? {
return value
}

View file

@ -17,7 +17,7 @@ public class SpecialItem extends RealmObject implements Item {
@PrimaryKey
String key;
String text, notes;
Integer value, owned;
Integer value;
public boolean isMysteryItem;
public static SpecialItem makeMysteryItem(Context context) {
@ -41,21 +41,11 @@ public class SpecialItem extends RealmObject implements Item {
return key;
}
@Override
public void setOwned(int size) {
owned = size;
}
@Override
public String getText() {
return text;
}
@Override
public Integer getOwned() {
return owned;
}
@Override
public Integer getValue() {
return value;

View file

@ -1,155 +0,0 @@
package com.habitrpg.android.habitica.models.user;
import com.habitrpg.android.habitica.models.inventory.Egg;
import com.habitrpg.android.habitica.models.inventory.Food;
import com.habitrpg.android.habitica.models.inventory.HatchingPotion;
import com.habitrpg.android.habitica.models.inventory.Mount;
import com.habitrpg.android.habitica.models.inventory.Pet;
import com.habitrpg.android.habitica.models.inventory.QuestContent;
import java.util.Date;
import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
public class Items extends RealmObject {
@PrimaryKey
private String userId;
public RealmList<Egg> eggs;
public RealmList<Food> food;
public RealmList<HatchingPotion> hatchingPotions;
public RealmList<QuestContent> quests;
User user;
RealmList<Pet> pets;
RealmList<Mount> mounts;
private String currentMount;
private String currentPet;
private int lastDrop_count;
private Date lastDrop_date;
//private QuestContent quest;
private Gear gear;
private SpecialItems special;
public Items(String currentMount, String currentPet, int lastDrop_count, Date lastDrop_date) {
this.currentMount = currentMount;
this.currentPet = currentPet;
this.lastDrop_count = lastDrop_count;
this.lastDrop_date = lastDrop_date;
}
public Items() {
}
public String getCurrentMount() {
return currentMount;
}
public void setCurrentMount(String currentMount) {
this.currentMount = currentMount;
}
public String getCurrentPet() {
return currentPet;
}
public void setCurrentPet(String currentPet) {
this.currentPet = currentPet;
}
public int getLastDrop_count() {
return lastDrop_count;
}
public void setLastDrop_count(int lastDrop_count) {
this.lastDrop_count = lastDrop_count;
}
public Date getLastDrop_date() {
return lastDrop_date;
}
public void setLastDrop_date(Date lastDrop_date) {
this.lastDrop_date = lastDrop_date;
}
public Gear getGear() {
return gear;
}
public void setGear(Gear gear) {
this.gear = gear;
}
public SpecialItems getSpecial() {
return special;
}
public void setSpecial(SpecialItems specialItems) {
this.special = specialItems;
}
public RealmList<Egg> getEggs() {
return eggs;
}
public void setEggs(RealmList<Egg> eggs) {
this.eggs = eggs;
}
public RealmList<Food> getFood() {
return food;
}
public void setFood(RealmList<Food> food) {
this.food = food;
}
public RealmList<HatchingPotion> getHatchingPotions() {
return hatchingPotions;
}
public void setHatchingPotions(RealmList<HatchingPotion> hatchingPotions) {
this.hatchingPotions = hatchingPotions;
}
public RealmList<QuestContent> getQuests() {
return quests;
}
public void setQuests(RealmList<QuestContent> quests) {
this.quests = quests;
}
public RealmList<Pet> getPets() {
return pets;
}
public void setPets(RealmList<Pet> pets) {
this.pets = pets;
}
public RealmList<Mount> getMounts() {
return mounts;
}
public void setMounts(RealmList<Mount> mounts) {
this.mounts = mounts;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
if (gear != null && !gear.isManaged()) {
gear.setUserId(userId);
}
if (special != null && !special.isManaged()) {
special.setUserId(userId);
}
}
}

View file

@ -0,0 +1,76 @@
package com.habitrpg.android.habitica.models.user
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.Food
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.models.inventory.Mount
import com.habitrpg.android.habitica.models.inventory.Pet
import com.habitrpg.android.habitica.models.inventory.QuestContent
import java.util.Date
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class Items : RealmObject {
@PrimaryKey
var userId: String? = null
set(userId) {
field = userId
if (gear != null && gear?.isManaged == false) {
gear?.userId = userId
}
if (special != null && special?.isManaged == false) {
special?.userId = userId
}
eggs?.forEach {
if (!it.isManaged) {
it.userID = userId
it.itemType = "eggs"
}
}
food?.forEach {
if (!it.isManaged) {
it.userID = userId
it.itemType = "food"
}
}
hatchingPotions?.forEach {
if (!it.isManaged) {
it.userID = userId
it.itemType = "hatchingPotions"
}
}
quests?.forEach {
if (!it.isManaged) {
it.userID = userId
it.itemType = "quests"
}
}
}
var eggs: RealmList<OwnedItem>? = null
var food: RealmList<OwnedItem>? = null
var hatchingPotions: RealmList<OwnedItem>? = null
var quests: RealmList<OwnedItem>? = null
var pets: RealmList<Pet>? = null
var mounts: RealmList<Mount>? = null
var currentMount: String? = null
var currentPet: String? = null
var lastDrop_count: Int = 0
var lastDrop_date: Date? = null
//private QuestContent quest;
var gear: Gear? = null
var special: SpecialItems? = null
constructor(currentMount: String, currentPet: String, lastDrop_count: Int, lastDrop_date: Date) {
this.currentMount = currentMount
this.currentPet = currentPet
this.lastDrop_count = lastDrop_count
this.lastDrop_date = lastDrop_date
}
constructor()
}

View file

@ -0,0 +1,23 @@
package com.habitrpg.android.habitica.models.user
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
open class OwnedItem : RealmObject() {
@PrimaryKey
var combinedKey: String? = null
var userID: String? = null
set(value) {
field = value
combinedKey = field + key
}
var key: String? = null
set(value) {
field = value
combinedKey = field + key
}
var itemType: String? = null
var numberOwned = 0
}

View file

@ -143,10 +143,10 @@ open class User : RealmObject(), Avatar {
var streakCount: Int = 0
val petsFoundCount: Int
get() = this.items?.getPets()?.size ?: 0
get() = this.items?.pets?.size ?: 0
val mountsTamedCount: Int
get() = this.items?.getMounts()?.size ?: 0
get() = this.items?.mounts?.size ?: 0
val contributorColor: Int
get() = this.contributor?.contributorColor ?: android.R.color.black

View file

@ -1,7 +1,6 @@
package com.habitrpg.android.habitica.ui.activities
import android.content.Context
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import androidx.core.content.ContextCompat
@ -338,7 +337,7 @@ class FullProfileActivity : BaseActivity() {
outfitList.add(outfit.shield)
outfitList.add(outfit.weapon)
}
return inventoryRepository.getItems(outfitList)
return inventoryRepository.getEquipment(outfitList)
}
private fun gotGear(equipmentList: List<Equipment>, user: Member) {

View file

@ -14,6 +14,7 @@ import com.habitrpg.android.habitica.events.commands.HatchingCommand
import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.extensions.notNull
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemRecyclerFragment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.helpers.bindView
@ -27,7 +28,7 @@ import io.realm.RealmRecyclerViewAdapter
import io.realm.RealmResults
import org.greenrobot.eventbus.EventBus
class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boolean) : RealmRecyclerViewAdapter<Item, ItemRecyclerAdapter.ItemViewHolder>(data, autoUpdate) {
class ItemRecyclerAdapter(data: OrderedRealmCollection<OwnedItem>?, autoUpdate: Boolean) : RealmRecyclerViewAdapter<OwnedItem, ItemRecyclerAdapter.ItemViewHolder>(data, autoUpdate) {
var isHatching: Boolean = false
var isFeeding: Boolean = false
@ -36,6 +37,11 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
var fragment: ItemRecyclerFragment? = null
private var ownedPets: RealmResults<Pet>? = null
var context: Context? = null
var items: Map<String, Item>? = null
set(value) {
field = value
notifyDataSetChanged()
}
private val sellItemEvents = PublishSubject.create<Item>()
private val questInvitationEvents = PublishSubject.create<QuestContent>()
@ -53,7 +59,10 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
}
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
data.notNull { holder.bind(it[position]) }
data.notNull {
val ownedItem = it[position]
holder.bind(ownedItem, items?.get(ownedItem.key))
}
}
fun setOwnedPets(pets: RealmResults<Pet>) {
@ -62,6 +71,7 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
inner class ItemViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
var ownedItem: OwnedItem? = null
var item: Item? = null
private val titleTextView: TextView by bindView(R.id.titleTextView)
@ -84,10 +94,11 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
itemView.setOnClickListener(this)
}
fun bind(item: Item) {
fun bind(ownedItem: OwnedItem, item: Item?) {
this.ownedItem = ownedItem
this.item = item
titleTextView.text = item.text
ownedTextView.text = item.owned.toString()
titleTextView.text = item?.text
ownedTextView.text = ownedItem.numberOwned.toString()
var disabled = false
val imageName: String?
@ -102,7 +113,7 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
is HatchingPotion -> "HatchingPotion"
else -> ""
}
imageName = "Pet_" + type + "_" + item.key
imageName = "Pet_" + type + "_" + item?.key
if (isHatching) {
disabled = this.isPetOwned ?: false
@ -133,7 +144,7 @@ class ItemRecyclerAdapter(data: OrderedRealmCollection<Item>?, autoUpdate: Boole
menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.invite_party)))
} else if (item is SpecialItem) {
val specialItem = item as SpecialItem
if (specialItem.isMysteryItem && specialItem.owned > 0) {
if (specialItem.isMysteryItem && ownedItem?.numberOwned ?: 0 > 0) {
menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.open)))
}
}

View file

@ -16,6 +16,7 @@ import com.habitrpg.android.habitica.models.inventory.Item
import com.habitrpg.android.habitica.models.shops.Shop
import com.habitrpg.android.habitica.models.shops.ShopCategory
import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder
@ -26,7 +27,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<an
private val items: MutableList<Any> = ArrayList()
private var shopIdentifier: String? = null
private var ownedItems: Map<String, Item> = HashMap()
private var ownedItems: Map<String, OwnedItem> = HashMap()
var shopSpriteSuffix: String = ""
@ -140,7 +141,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<an
val itemHolder = holder as? ShopItemViewHolder ?: return
itemHolder.bind(item, item.canAfford(user))
if (ownedItems.containsKey(item.key+"-"+item.pinType)) {
itemHolder.itemCount = ownedItems[item.key+"-"+item.pinType]?.owned ?: 0
itemHolder.itemCount = ownedItems[item.key+"-"+item.pinType]?.numberOwned ?: 0
}
itemHolder.isPinned = pinnedItemKeys.contains(item.key)
}
@ -210,7 +211,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<an
private fun getSelectedShopCategory() =
gearCategories.firstOrNull { selectedGearCategory == it.identifier }
fun setOwnedItems(ownedItems: Map<String, Item>) {
fun setOwnedItems(ownedItems: Map<String, OwnedItem>) {
this.ownedItems = ownedItems
this.notifyDataSetChanged()
}

View file

@ -245,7 +245,7 @@ class StatsFragment: BaseMainFragment() {
outfitList.add(thisOutfit.weapon)
}
compositeSubscription.add(inventoryRepository.getItems(outfitList).firstElement()
compositeSubscription.add(inventoryRepository.getEquipment(outfitList).firstElement()
.retry(1)
.subscribe(Consumer {
val userStatComputer = UserStatComputer()

View file

@ -171,11 +171,26 @@ class ItemRecyclerFragment : BaseFragment() {
"special" -> SpecialItem::class.java
else -> Egg::class.java
}
compositeSubscription.add(inventoryRepository.getOwnedItems(itemClass, user).firstElement().subscribe(Consumer { items ->
if (items.size > 0) {
adapter?.updateData(items as? OrderedRealmCollection<Item>)
}
}, RxErrorHandler.handleEmptyError()))
itemType?.let { type ->
compositeSubscription.add(inventoryRepository.getOwnedItems(type)
.doOnNext { items ->
if (items.size > 0) {
adapter?.updateData(items)
}
}
.map { items -> items.mapNotNull { it.key } }
.flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray(), user) }
.map {
val itemMap = mutableMapOf<String, Item>()
for (item in it) {
itemMap[item.key] = item
}
itemMap
}
.subscribe(Consumer { items ->
adapter?.items = items
}, RxErrorHandler.handleEmptyError()))
}
compositeSubscription.add(inventoryRepository.getOwnedPets().subscribe(Consumer { adapter?.setOwnedPets(it) }, RxErrorHandler.handleEmptyError()))
}

View file

@ -41,10 +41,22 @@ class ContentDeserializer : JsonDeserializer<ContentResult> {
result.armoire = context.deserialize(obj.get("armoire"), Equipment::class.java)
result.gear = context.deserialize(obj.get("gear"), ContentGear::class.java)
result.quests = context.deserialize(obj.get("quests"), object : TypeToken<RealmList<QuestContent>>() {}.type)
result.eggs = context.deserialize(obj.get("eggs"), object : TypeToken<RealmList<Egg>>() {}.type)
result.food = context.deserialize(obj.get("food"), object : TypeToken<RealmList<Food>>() {}.type)
result.hatchingPotions = context.deserialize(obj.get("hatchingPotions"), object : TypeToken<RealmList<HatchingPotion>>() {}.type)
result.quests = RealmList()
for (entry in obj.get("quests").asJsonObject.entrySet()) {
result.quests.add(context.deserialize(entry.value, QuestContent::class.java))
}
result.eggs = RealmList()
for (entry in obj.get("eggs").asJsonObject.entrySet()) {
result.eggs.add(context.deserialize(entry.value, Egg::class.java))
}
result.food = RealmList()
for (entry in obj.get("food").asJsonObject.entrySet()) {
result.food.add(context.deserialize(entry.value, Food::class.java))
}
result.hatchingPotions = RealmList()
for (entry in obj.get("hatchingPotions").asJsonObject.entrySet()) {
result.hatchingPotions.add(context.deserialize(entry.value, HatchingPotion::class.java))
}
result.pets = RealmList()
for (key in obj.getAsJsonObject("petInfo").keySet()) {

View file

@ -1,70 +0,0 @@
package com.habitrpg.android.habitica.utils;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.habitrpg.android.habitica.models.inventory.Egg;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import io.realm.Realm;
import io.realm.RealmList;
public class EggListDeserializer implements JsonDeserializer<List<Egg>> {
@Override
public List<Egg> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
RealmList<Egg> vals = new RealmList<>();
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();
Realm realm = Realm.getDefaultInstance();
List<Egg> existingItems = realm.copyFromRealm(realm.where(Egg.class).findAll());
realm.close();
for (Egg item : existingItems) {
if (object.has(item.getKey())) {
JsonElement itemObject = object.get(item.getKey());
if (itemObject.isJsonObject()) {
Egg parsedItem = context.deserialize(itemObject.getAsJsonObject(), Egg.class);
item.setText(parsedItem.getText());
item.setNotes(parsedItem.getNotes());
item.setValue(parsedItem.getValue());
item.setAdjective(parsedItem.getAdjective());
item.setMountText(parsedItem.getMountText());
} else {
item.setOwned(itemObject.getAsInt());
}
vals.add(item);
object.remove(item.getKey());
}
}
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Egg item;
if (entry.getValue().isJsonObject()) {
item = context.deserialize(entry.getValue(), Egg.class);
} else {
item = new Egg();
item.setKey(entry.getKey());
if (entry.getValue().isJsonNull()) {
item.setOwned(0);
} else {
item.setOwned(entry.getValue().getAsInt());
}
}
vals.add(item);
}
} else {
for (JsonElement item : json.getAsJsonArray()) {
vals.add(context.deserialize(item.getAsJsonObject(), Egg.class));
}
}
return vals;
}
}

View file

@ -1,71 +0,0 @@
package com.habitrpg.android.habitica.utils;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.habitrpg.android.habitica.models.inventory.Food;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import io.realm.Realm;
import io.realm.RealmList;
public class FoodListDeserializer implements JsonDeserializer<List<Food>> {
@Override
public List<Food> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
RealmList<Food> vals = new RealmList<>();
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();
Realm realm = Realm.getDefaultInstance();
List<Food> existingItems = realm.copyFromRealm(realm.where(Food.class).findAll());
realm.close();
for (Food item : existingItems) {
if (object.has(item.getKey())) {
JsonElement itemObject = object.get(item.getKey());
if (itemObject.isJsonObject()) {
Food parsedItem = context.deserialize(itemObject.getAsJsonObject(), Food.class);
item.setText(parsedItem.getText());
item.setNotes(parsedItem.getNotes());
item.setValue(parsedItem.getValue());
item.setArticle(parsedItem.getArticle());
item.setCanDrop(parsedItem.getCanDrop());
item.setTarget(parsedItem.getTarget());
} else {
item.setOwned(itemObject.getAsInt());
}
vals.add(item);
object.remove(item.getKey());
}
}
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
Food item;
if (entry.getValue().isJsonObject()) {
item = context.deserialize(entry.getValue(), Food.class);
} else {
item = new Food();
item.setKey(entry.getKey());
if (entry.getValue().isJsonNull()) {
item.setOwned(0);
} else {
item.setOwned(entry.getValue().getAsInt());
}
}
vals.add(item);
}
} else {
for (JsonElement item : json.getAsJsonArray()) {
vals.add(context.deserialize(item.getAsJsonObject(), Food.class));
}
}
return vals;
}
}

View file

@ -1,70 +0,0 @@
package com.habitrpg.android.habitica.utils;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.habitrpg.android.habitica.models.inventory.HatchingPotion;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import io.realm.Realm;
import io.realm.RealmList;
public class HatchingPotionListDeserializer implements JsonDeserializer<List<HatchingPotion>> {
@Override
public List<HatchingPotion> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
RealmList<HatchingPotion> vals = new RealmList<>();
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();
Realm realm = Realm.getDefaultInstance();
List<HatchingPotion> existingItems = realm.copyFromRealm(realm.where(HatchingPotion.class).findAll());
realm.close();
for (HatchingPotion item : existingItems) {
if (object.has(item.getKey())) {
JsonElement itemObject = object.get(item.getKey());
if (itemObject.isJsonObject()) {
HatchingPotion parsedItem = context.deserialize(itemObject.getAsJsonObject(), HatchingPotion.class);
item.setText(parsedItem.getText());
item.setNotes(parsedItem.getNotes());
item.setValue(parsedItem.getValue());
item.setLimited(parsedItem.getLimited());
item.setPremium(parsedItem.getPremium());
} else {
item.setOwned(itemObject.getAsInt());
}
vals.add(item);
object.remove(item.getKey());
}
}
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
HatchingPotion item;
if (entry.getValue().isJsonObject()) {
item = context.deserialize(entry.getValue(), HatchingPotion.class);
} else {
item = new HatchingPotion();
item.setKey(entry.getKey());
if (entry.getValue().isJsonNull()) {
item.setOwned(0);
} else {
item.setOwned(entry.getValue().getAsInt());
}
}
vals.add(item);
}
} else {
for (JsonElement item : json.getAsJsonArray()) {
vals.add(context.deserialize(item.getAsJsonObject(), HatchingPotion.class));
}
}
return vals;
}
}

View file

@ -0,0 +1,24 @@
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.models.user.OwnedItem
import io.realm.RealmList
import java.lang.reflect.Type
class OwnedItemListDeserializer: JsonDeserializer<List<OwnedItem>> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): List<OwnedItem> {
val ownedItems = RealmList<OwnedItem>()
val entrySet = json?.asJsonObject?.entrySet()
if (entrySet != null) {
for (entry in entrySet) {
val item = OwnedItem()
item.key = entry.key
item.numberOwned = entry.value.asInt
ownedItems.add(item)
}
}
return ownedItems
}
}

View file

@ -1,78 +0,0 @@
package com.habitrpg.android.habitica.utils;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.habitrpg.android.habitica.models.inventory.QuestContent;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import io.realm.Realm;
import io.realm.RealmList;
public class QuestListDeserializer implements JsonDeserializer<List<QuestContent>> {
@Override
public List<QuestContent> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
RealmList<QuestContent> vals = new RealmList<>();
if (json.isJsonObject()) {
JsonObject object = json.getAsJsonObject();
Realm realm = Realm.getDefaultInstance();
List<QuestContent> existingItems = realm.copyFromRealm(realm.where(QuestContent.class).findAll());
realm.close();
for (QuestContent item : existingItems) {
if (object.has(item.getKey())) {
JsonElement itemObject = object.get(item.getKey());
if (itemObject.isJsonObject()) {
QuestContent parsedItem = context.deserialize(itemObject.getAsJsonObject(), QuestContent.class);
item.setText(parsedItem.getText());
item.setNotes(parsedItem.getNotes());
item.setValue(parsedItem.getValue());
item.setPrevious(parsedItem.getPrevious());
item.setCanBuy(parsedItem.isCanBuy());
item.setBoss(parsedItem.getBoss());
item.setDrop(parsedItem.getDrop());
item.setCategory(parsedItem.getCategory());
item.setCollect(parsedItem.getCollect());
item.setLvl(parsedItem.getLvl());
} else {
item.setOwned(itemObject.getAsInt());
}
vals.add(item);
object.remove(item.getKey());
}
}
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
QuestContent item;
if (entry.getValue().isJsonObject()) {
item = context.deserialize(entry.getValue(), QuestContent.class);
//Make sure keys are set
item.setBoss(item.getBoss());
item.setDrop(item.getDrop());
} else {
item = new QuestContent();
item.setKey(entry.getKey());
if (entry.getValue().isJsonNull()) {
item.setOwned(0);
} else {
item.setOwned(entry.getValue().getAsInt());
}
}
vals.add(item);
}
} else {
for (JsonElement item : json.getAsJsonArray()) {
vals.add(context.deserialize(item.getAsJsonObject(), QuestContent.class));
}
}
return vals;
}
}

View file

@ -64,7 +64,7 @@
// updateData.put("preferences.timezoneOffset", 2);
// verify(mockedApiClient).updateUser(updateData);
// verify(mockedLocalRepository).saveUser(testUser);
// assertEquals(testUser.getItems(), newUser.getItems());
// assertEquals(testUser.getEquipment(), newUser.getEquipment());
// assertEquals(testUser.getPreferences(), newUser.getPreferences());
// assertEquals(testUser.getFlags(), newUser.getFlags());
// }