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()