From dfbcdc712565b8a625cb1d83f9b362f0b96e7544 Mon Sep 17 00:00:00 2001 From: Hafiz Date: Mon, 14 Feb 2022 05:51:43 -0500 Subject: [PATCH] Ability to use special items from Items added --- Habitica/res/values/strings.xml | 1 + .../implementation/ContentRepositoryImpl.kt | 1 - .../RealmContentLocalRepository.kt | 1 + .../habitica/models/inventory/SpecialItem.kt | 1 + .../habitica/models/user/SpecialItems.kt | 15 ++ .../adapter/inventory/ItemRecyclerAdapter.kt | 29 +++- .../inventory/items/ItemRecyclerFragment.kt | 154 +++++++++++++++--- .../habitica/utils/ContentDeserializer.kt | 23 ++- 8 files changed, 181 insertions(+), 44 deletions(-) diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 1047ee33d..807820cdb 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -466,6 +466,7 @@ Each month, subscribers will receive a mystery item. This is usually released about one week before the end of the month. Open> Open + Use Item on party member> You earned a %1$s as a reward for your devotion to improving your life. Next prize in %1$d Check-Ins pending approval diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt index fd4c8cb9e..2868d580b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt @@ -24,7 +24,6 @@ class ContentRepositoryImpl(localRepository: T, apiC return if (forced || now - this.lastContentSync > 300000) { lastContentSync = now apiClient.content.doOnNext { - it.special = RealmList() it.special.add(mysteryItem) localRepository.saveContent(it) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmContentLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmContentLocalRepository.kt index da558dc96..042f35b2f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmContentLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmContentLocalRepository.kt @@ -28,6 +28,7 @@ open class RealmContentLocalRepository(realm: Realm) : RealmBaseLocalRepository( realm1.insertOrUpdate(contentResult.mounts) realm1.insertOrUpdate(contentResult.spells) + realm1.insertOrUpdate(contentResult.special) realm1.insertOrUpdate(contentResult.appearances) realm1.insertOrUpdate(contentResult.backgrounds) realm1.insertOrUpdate(contentResult.faq) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/SpecialItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/SpecialItem.kt index 061955b30..521388af6 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/SpecialItem.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/inventory/SpecialItem.kt @@ -15,6 +15,7 @@ open class SpecialItem : RealmObject(), Item { internal var notes: String = "" override var value: Int = 0 override var event: ItemEvent? = null + var target: String? = null var isMysteryItem: Boolean = false companion object { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/SpecialItems.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/SpecialItems.kt index 681e158eb..b83d50627 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/SpecialItems.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/SpecialItems.kt @@ -17,4 +17,19 @@ open class SpecialItems : RealmObject(), BaseObject { get() { return seafoam > 0 || shinySeed > 0 || snowball > 0 || spookySparkles > 0 } + + fun getSpecialItemCount(key: String): Int{ + var inventoryPresent: OwnedItem = ownedItems?.filter { it.key == "inventory_present" }?.single() ?: OwnedItem() + + var count = 0 + when(key){ + "seafoam" -> count = seafoam + "shinySeed" -> count = shinySeed + "snowball" -> count = snowball + "spookySparkles" -> count = spookySparkles + "inventory_present" -> count = inventoryPresent.numberOwned + } + return count + + } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt index 6a15c43d1..167d20edd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ItemRecyclerAdapter.kt @@ -13,6 +13,7 @@ import com.habitrpg.android.habitica.models.Skill import com.habitrpg.android.habitica.models.inventory.* import com.habitrpg.android.habitica.models.user.OwnedItem import com.habitrpg.android.habitica.models.user.OwnedPet +import com.habitrpg.android.habitica.models.user.SpecialItems import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu @@ -38,6 +39,11 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter() private val questInvitationEvents = PublishSubject.create() @@ -45,6 +51,8 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter() private val hatchPetSubject = PublishSubject.create>() private val feedPetSubject = PublishSubject.create() + private val useSpecialSubject = PublishSubject.create() + fun getSellItemFlowable(): Flowable { return sellItemEvents.toFlowable(BackpressureStrategy.DROP) @@ -60,6 +68,7 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter = startHatchingSubject.toFlowable(BackpressureStrategy.DROP) val hatchPetEvents: Flowable> = hatchPetSubject.toFlowable(BackpressureStrategy.DROP) val feedPetEvents: Flowable = feedPetSubject.toFlowable(BackpressureStrategy.DROP) + val useSpecialEvents: Flowable = useSpecialSubject.toFlowable(BackpressureStrategy.DROP) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { return ItemViewHolder(ItemItemBinding.inflate(context.layoutInflater, parent, false)) @@ -111,14 +120,15 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter "Egg" @@ -161,7 +171,10 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter 0) { menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.open))) + } else if (ownedItem?.numberOwned ?: 0 > 0){ + menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.use_item))) } + } menu.setSelectionRunnable { index -> item?.let { selectedItem -> @@ -181,7 +194,7 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter openMysteryItemEvents.onNext(selectedItem) + is SpecialItem -> useSpecialSubject.onNext(selectedItem) } } } @@ -209,16 +222,16 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter, ownedItems: MutableList){ + fun setSpecialItems(skillItems: List){ val transformationItems: MutableList = mutableListOf() for (item in skillItems){ val ownedTransformationItem = OwnedItem() ownedTransformationItem.key = item.key - ownedTransformationItem.itemType = item.habitClass - ownedTransformationItem.numberOwned = 5 //Test + ownedTransformationItem.itemType = "special" + ownedTransformationItem.numberOwned = specialItems?.getSpecialItemCount(item.key) ?: 0 transformationItems.add(ownedTransformationItem) } - data = ownedItems + transformationItems + data = transformationItems notifyDataSetChanged() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt index f7f083dcb..64509ed91 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt @@ -1,9 +1,15 @@ package com.habitrpg.android.habitica.ui.fragments.inventory.items +import android.app.Activity +import android.content.Intent +import android.graphics.drawable.BitmapDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import androidx.lifecycle.lifecycleScope import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent @@ -16,18 +22,28 @@ import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.interactors.HatchPetUseCase +import com.habitrpg.android.habitica.models.Skill import com.habitrpg.android.habitica.models.inventory.* +import com.habitrpg.android.habitica.models.responses.SkillResponse import com.habitrpg.android.habitica.models.user.OwnedItem import com.habitrpg.android.habitica.models.user.OwnedPet import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.ui.activities.BaseActivity import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity +import com.habitrpg.android.habitica.ui.activities.SkillTasksActivity import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter import com.habitrpg.android.habitica.ui.fragments.BaseFragment import com.habitrpg.android.habitica.ui.helpers.EmptyItem import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator import com.habitrpg.android.habitica.ui.helpers.loadImage +import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar import com.habitrpg.android.habitica.ui.views.dialogs.OpenedMysteryitemDialog +import io.reactivex.rxjava3.core.Flowable +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.util.ArrayList import javax.inject.Inject class ItemRecyclerFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { @@ -46,6 +62,7 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL var transformationItems: MutableList = mutableListOf() var itemTypeText: String? = null var user: User? = null + private var selectedSpecialItem: SpecialItem? = null internal var layoutManager: androidx.recyclerview.widget.LinearLayoutManager? = null override var binding: FragmentItemsBinding? = null @@ -91,7 +108,8 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL adapter = ItemRecyclerAdapter(context) } binding?.recyclerView?.adapter = adapter - + adapter?.specialItems = this.user?.items?.special + adapter?.useSpecialEvents?.subscribeWithErrorHandler { onSpecialItemSelected(it) }?.let { compositeSubscription.add(it) } adapter?.let { adapter -> compositeSubscription.add( adapter.getSellItemFlowable() @@ -204,35 +222,72 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL else -> Egg::class.java } itemType?.let { type -> - compositeSubscription.add( - inventoryRepository.getOwnedItems(type) - .map { it.distinctBy { it.key } } - .doOnNext { items -> - adapter?.data = items - if (itemType == "special") { - transformationItems = items.toMutableList() - userRepository.getTransformationItems() - .subscribe { skillItems -> adapter?.setSpecialItems(skillItems, transformationItems) } + if (type != "special"){ + compositeSubscription.add( + inventoryRepository.getOwnedItems(type) + .map { it.distinctBy { it.key } } + .doOnNext { items -> + adapter?.data = items +// if (itemType == "special") { +// transformationItems = items.toMutableList() +// userRepository.getTransformationItems() //Get from RealmInventoryLocalRepository +// .subscribe { skillItems -> adapter?.setSpecialItems(skillItems, transformationItems) } +// } } - } - .map { items -> items.mapNotNull { it.key } } - .flatMap { - inventoryRepository.getItems(itemClass, it.toTypedArray()) - } - .map { - val itemMap = mutableMapOf() - for (item in it) { - itemMap[item.key] = item + .map { items -> items.mapNotNull { it.key } } + .flatMap { + inventoryRepository.getItems(itemClass, it.toTypedArray()) } - itemMap + .map { + val itemMap = mutableMapOf() + for (item in it) { + itemMap[item.key] = item + } + itemMap + } + .subscribe( + { items -> + adapter?.items = items + }, + RxErrorHandler.handleEmptyError() + ) + ) + } else { + val specialItems = user?.items?.special + val ownedItems = ArrayList() + if (specialItems != null) { + ownedItems.add("inventory_present") + if (specialItems.snowball > 0) { + ownedItems.add("snowball") } - .subscribe( - { items -> - adapter?.items = items - }, - RxErrorHandler.handleEmptyError() - ) - ) + if (specialItems.shinySeed > 0) { + ownedItems.add("shinySeed") + } + if (specialItems.seafoam > 0) { + ownedItems.add("seafoam") + } + if (specialItems.spookySparkles > 0) { + ownedItems.add("spookySparkles") + } + } + if (ownedItems.size == 0) { + ownedItems.add("") + } + compositeSubscription.add( + inventoryRepository.getItems(SpecialItem::class.java, ownedItems.toTypedArray()) + .map { it.distinctBy { it.key } } + .doOnNext { items -> + adapter?.setSpecialItems(items) + val itemMap = mutableMapOf() + for (item in items) { + itemMap[item.key] = item + } + adapter?.items = itemMap + } + .subscribe() + ) + } + } compositeSubscription.add(inventoryRepository.getPets().subscribe({ adapter?.setExistingPets(it) }, RxErrorHandler.handleEmptyError())) @@ -251,6 +306,51 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL MainNavigationController.navigate(R.id.marketFragment) } + private fun onSpecialItemSelected(specialItem: SpecialItem) { + selectedSpecialItem = specialItem + val intent = Intent(activity, SkillMemberActivity::class.java) + memberSelectionResult.launch(intent) + + } + + private val memberSelectionResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == Activity.RESULT_OK) { + useSpecialItem(selectedSpecialItem, it.data?.getStringExtra("member_id")) + } + } + + private fun useSpecialItem(specialItem: SpecialItem?, memberID: String? = null) { + if (specialItem == null || memberID == null) { + return + } + + val observable: Flowable = userRepository.useSkill(specialItem.key, specialItem.target, memberID) + + compositeSubscription.add( + observable.subscribe( + { skillResponse -> this.displaySkillResult(specialItem) }, + RxErrorHandler.handleEmptyError() + ) + ) + } + + private fun displaySkillResult(specialItem: SpecialItem?) { + if (!isAdded) return + + val activity = activity as? MainActivity + activity?.let { + HabiticaSnackbar.showSnackbar( + it.snackbarContainer, + context?.getString(R.string.used_skill_without_mana, specialItem?.text), + HabiticaSnackbar.SnackbarDisplayType.BLUE + ) + } + + compositeSubscription.add( + userRepository.retrieveUser(false).subscribe({ }, RxErrorHandler.handleEmptyError()) + ) + } + companion object { private const val ITEM_TYPE_KEY = "CLASS_TYPE_KEY" diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt index 815dc0ed2..b7176b24f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/ContentDeserializer.kt @@ -11,14 +11,7 @@ import com.habitrpg.android.habitica.models.ContentGear import com.habitrpg.android.habitica.models.ContentResult import com.habitrpg.android.habitica.models.FAQArticle import com.habitrpg.android.habitica.models.Skill -import com.habitrpg.android.habitica.models.inventory.Customization -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.Mount -import com.habitrpg.android.habitica.models.inventory.Pet -import com.habitrpg.android.habitica.models.inventory.QuestContent +import com.habitrpg.android.habitica.models.inventory.* import io.realm.RealmList import java.lang.reflect.Type @@ -101,6 +94,20 @@ class ContentDeserializer : JsonDeserializer { result.spells.add(skill) } } + + if (obj.has("special")) {//ToDo add special + for ((classname, value) in obj.getAsJsonObject("special").entrySet()) { + val skillItem = value.asJsonObject + val special = SpecialItem() + special.key = skillItem.get("key").asString + special.text = skillItem.get("text").asString + special.notes = skillItem.get("notes").asString + special.target = skillItem.get("target").asString + special.value = skillItem.get("value").asInt + result.special.add(special) + } + } + result.appearances = context.deserialize(obj.get("appearances"), object : TypeToken>() {}.type) result.backgrounds = context.deserialize(obj.get("backgrounds"), object : TypeToken>() {}.type) val noBackground = Customization()