From 96682e106f3d24ef9daec8298b40113fef452b38 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Thu, 16 Jul 2020 08:38:49 +0200 Subject: [PATCH] Improve stable overview and add new dialog for unhatched --- .../res/layout/dialog_pet_suggest_hatch.xml | 64 ++++++++++++++++++ Habitica/res/values/strings.xml | 10 ++- .../habitica/extensions/Animal-Extensions.kt | 1 + .../inventory/PetDetailRecyclerAdapter.kt | 32 ++++++++- .../inventory/StableRecyclerAdapter.kt | 53 ++++++++++++--- .../stable/PetDetailRecyclerFragment.kt | 9 +++ .../stable/StableRecyclerFragment.kt | 13 ++-- .../ui/views/dialogs/PetSuggestHatchDialog.kt | 67 +++++++++++++++++++ 8 files changed, 230 insertions(+), 19 deletions(-) create mode 100644 Habitica/res/layout/dialog_pet_suggest_hatch.xml create mode 100644 Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt diff --git a/Habitica/res/layout/dialog_pet_suggest_hatch.xml b/Habitica/res/layout/dialog_pet_suggest_hatch.xml new file mode 100644 index 000000000..57ebae355 --- /dev/null +++ b/Habitica/res/layout/dialog_pet_suggest_hatch.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index f9d45dcce..7e60c15a6 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -1027,8 +1027,8 @@ %d%% Complete Create a Task Complete a Task - Hatch a Pet - Feed a Pet + Hatch a new pet + Feed a pet Purchase Equipment Add a task for something you would like to accomplish this week Check off any of your tasks to earn rewards @@ -1058,4 +1058,10 @@ You already have everything you need for all %s pets. Are you sure you want to purchase %d %ss? Equip View Onboarding Tasks + You\'ll need a %s Egg and %s Potion to hatch this pet + You can use a %s Egg and a %s Potion to hatch this pet + Hatch Pet + Unhatched Pet + Magic Potions + Magic Potion diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt index f8cb2c6bc..102b47cc1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/extensions/Animal-Extensions.kt @@ -14,6 +14,7 @@ fun Animal.getTranslatedType(c: Context?): String { "quest" -> c?.getString(R.string.quest).toString() "wacky" -> c?.getString(R.string.wacky).toString() "special" -> c?.getString(R.string.special).toString() + "premium" -> c?.getString(R.string.magic_potion).toString() else -> { type } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt index b917f88c7..b66dd1c0b 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt @@ -10,16 +10,19 @@ import android.widget.TextView import com.facebook.drawee.view.SimpleDraweeView import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.events.commands.FeedCommand +import com.habitrpg.android.habitica.extensions.addCloseButton import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.helpers.RxErrorHandler -import com.habitrpg.android.habitica.models.inventory.Mount -import com.habitrpg.android.habitica.models.inventory.Pet +import com.habitrpg.android.habitica.models.inventory.* +import com.habitrpg.android.habitica.models.user.OwnedItem import com.habitrpg.android.habitica.models.user.OwnedMount import com.habitrpg.android.habitica.models.user.OwnedPet import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils import com.habitrpg.android.habitica.ui.helpers.bindView import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem +import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.android.habitica.ui.views.dialogs.PetSuggestHatchDialog import io.reactivex.BackpressureStrategy import io.reactivex.Flowable import io.reactivex.Observable @@ -38,12 +41,15 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B private var existingMounts: RealmResults? = null private var ownedPets: Map? = null private var ownedMounts: Map? = null + private var ownedItems: Map? = null private val equipEvents = PublishSubject.create() fun getEquipFlowable(): Flowable { return equipEvents.toFlowable(BackpressureStrategy.DROP) } + var animalIngredientsRetriever: ((Animal) -> Pair)? = null + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PetViewHolder { return PetViewHolder(parent.inflate(R.layout.pet_detail_item)) } @@ -68,6 +74,12 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B this.ownedPets = ownedPets notifyDataSetChanged() } + + fun setOwnedItems(ownedItems: Map) { + this.ownedItems = ownedItems + notifyDataSetChanged() + } + inner class PetViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { var animal: Pet? = null var ownedPet: OwnedPet? = null @@ -88,6 +100,11 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B return false } + private val canHatch: Boolean + get() { + return ownedItems?.get(animal?.animal + "-eggs") != null && ownedItems?.get(animal?.color + "-hatchingPotions") != null + } + init { itemView.setOnClickListener(this) } @@ -126,6 +143,7 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B override fun onClick(v: View) { if (!this.isOwned) { + showRequirementsDialog() return } val context = context ?: return @@ -148,5 +166,15 @@ class PetDetailRecyclerAdapter(data: OrderedRealmCollection?, autoUpdate: B } menu.show() } + + private fun showRequirementsDialog() { + val context = context ?: return + val dialog = PetSuggestHatchDialog(context) + animal?.let { + val ingredients = animalIngredientsRetriever?.invoke(it) + dialog.configure(it, ingredients?.first, ingredients?.second, canHatch) + } + dialog.show() + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt index 4245d0ed5..7d6bdcca0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt @@ -10,6 +10,7 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.GridLayoutManager import com.facebook.drawee.view.SimpleDraweeView import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.events.commands.FeedCommand import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.RxErrorHandler @@ -18,11 +19,17 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmentDirections import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu +import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder import com.habitrpg.android.habitica.ui.views.NPCBannerView +import io.reactivex.BackpressureStrategy +import io.reactivex.Flowable import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Consumer +import io.reactivex.subjects.PublishSubject +import org.greenrobot.eventbus.EventBus class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter() { @@ -30,6 +37,11 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< var itemType: String? = null var context: Context? = null var activity: MainActivity? = null + private val equipEvents = PublishSubject.create() + + fun getEquipFlowable(): Flowable { + return equipEvents.toFlowable(BackpressureStrategy.DROP) + } private var itemList: List = ArrayList() @@ -120,7 +132,8 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< fun bind(item: Animal) { this.animal = item - titleView.text = if (item.type == "special") { + val isIndividualAnimal = item.type == "special" || animal?.type == "wacky" + titleView.text = if (isIndividualAnimal) { item.text } else { item.animal @@ -131,27 +144,33 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< this.ownedTextView.alpha = 1.0f val imageName = if (itemType == "pets") { - "Pet_Egg_" + item.animal + if (isIndividualAnimal) { + "social_Pet-" + animal?.key + } else { + "Pet_Egg_" + item.animal + } } else { "Mount_Icon_" + item.key } context?.let { - - var owned = item.numberOwned - var totalNum = item.totalNumber - + val owned = item.numberOwned + val totalNum = item.totalNumber this.ownedTextView.text = context?.getString(R.string.pet_ownership_fraction, owned, totalNum) this.ownedTextView.background = context?.getDrawable(R.drawable.layout_rounded_bg_shopitem_price) this.ownedTextView.setTextColor(ContextCompat.getColor(it, R.color.black) ) - ownedTextView.visibility = if (animal?.type == "special") View.GONE else View.VISIBLE + ownedTextView.visibility = if (isIndividualAnimal) View.GONE else View.VISIBLE imageView.background = null - - DataBindingUtils.loadImage(imageName) { - val drawable = BitmapDrawable(context?.resources, it) + val numberOwned = item.numberOwned == 0 + DataBindingUtils.loadImage(imageName) {bitmap -> + val drawable = if (isIndividualAnimal) { + BitmapDrawable(context?.resources, if (numberOwned) bitmap.extractAlpha() else bitmap) + } else { + BitmapDrawable(context?.resources, bitmap) + } Observable.just(drawable) .observeOn(AndroidSchedulers.mainThread()) .subscribe(Consumer { @@ -175,6 +194,20 @@ class StableRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter< override fun onClick(v: View) { val animal = this.animal if (animal != null) { + if (animal.type == "special" || animal.type == "wacky") { + if (animal.numberOwned == 0) return + val context = context ?: return + val menu = BottomSheetMenu(context) + menu.setTitle(animal.text) + menu.addMenuItem(BottomSheetMenuItem(itemView.resources.getString(R.string.equip))) + menu.setSelectionRunnable { + animal.let { + equipEvents.onNext(it.key) + } + } + menu.show() + return + } val color = if (animal.type == "special") animal.color else null if (animal.numberOwned > 0) { if (itemType == "pets") { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt index 55e96dc9c..f2d6fc3fd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/PetDetailRecyclerFragment.kt @@ -9,9 +9,12 @@ import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.InventoryRepository import com.habitrpg.android.habitica.events.commands.FeedCommand import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Egg +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.user.Items +import com.habitrpg.android.habitica.models.user.OwnedItem import com.habitrpg.android.habitica.models.user.OwnedMount import com.habitrpg.android.habitica.models.user.OwnedPet import com.habitrpg.android.habitica.ui.adapter.inventory.PetDetailRecyclerAdapter @@ -83,6 +86,11 @@ class PetDetailRecyclerFragment : BaseMainFragment() { compositeSubscription.add(adapter.getEquipFlowable() .flatMap { key -> inventoryRepository.equip(user, "pet", key) } .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) + adapter.animalIngredientsRetriever = { + val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(it.animal), null).firstElement().blockingGet().firstOrNull() + val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(it.color), null).firstElement().blockingGet().firstOrNull() + Pair(egg as? Egg, potion as? HatchingPotion) + } view.post { setGridSpanCount(view.width) } } @@ -122,6 +130,7 @@ class PetDetailRecyclerFragment : BaseMainFragment() { return@map mountMap } .subscribe(Consumer { adapter.setOwnedMounts(it) }, RxErrorHandler.handleEmptyError())) + compositeSubscription.add(inventoryRepository.getOwnedItems().subscribe(Consumer { adapter.setOwnedItems(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(inventoryRepository.getPets(animalType, animalGroup, animalColor).firstElement().subscribe(Consumer> { adapter.updateData(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(inventoryRepository.getMounts(animalType, animalGroup, animalColor).subscribe(Consumer> { adapter.setExistingMounts(it) }, RxErrorHandler.handleEmptyError())) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt index b82bf9c92..147a49006 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt @@ -12,10 +12,7 @@ import com.habitrpg.android.habitica.extensions.getTranslatedType import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.inventory.Animal -import com.habitrpg.android.habitica.models.user.OwnedMount -import com.habitrpg.android.habitica.models.user.OwnedObject -import com.habitrpg.android.habitica.models.user.OwnedPet -import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.models.user.* import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter import com.habitrpg.android.habitica.ui.fragments.BaseFragment @@ -91,6 +88,12 @@ class StableRecyclerFragment : BaseFragment() { adapter?.context = context recyclerView?.adapter = adapter recyclerView?.itemAnimator = SafeDefaultItemAnimator() + + adapter?.let { + compositeSubscription.add(it.getEquipFlowable() + .flatMap { key -> inventoryRepository.equip(user, if (itemType == "pets") "pet" else "mount", key) } + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) + } } this.loadItems() @@ -146,7 +149,7 @@ class StableRecyclerFragment : BaseFragment() { var lastAnimal: Animal = unsortedAnimals[0] ?: return items var lastSectionTitle = "" for (animal in unsortedAnimals) { - val identifier = if (animal.animal.isNotEmpty() && animal.type != "special") animal.animal else animal.key + val identifier = if (animal.animal.isNotEmpty() && (animal.type != "special" && animal.type != "wacky")) animal.animal else animal.key val lastIdentifier = if (lastAnimal.animal.isNotEmpty()) lastAnimal.animal else lastAnimal.key if (identifier != lastIdentifier || animal === unsortedAnimals[unsortedAnimals.size - 1]) { if (!((lastAnimal.type == "premium" || lastAnimal.type == "special") && lastAnimal.numberOwned == 0)) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt new file mode 100644 index 000000000..f8641c21e --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/PetSuggestHatchDialog.kt @@ -0,0 +1,67 @@ +package com.habitrpg.android.habitica.ui.views.dialogs + +import android.content.Context +import android.graphics.drawable.BitmapDrawable +import android.view.LayoutInflater +import com.facebook.drawee.view.SimpleDraweeView +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.DialogPetSuggestHatchBinding +import com.habitrpg.android.habitica.helpers.MainNavigationController +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Animal +import com.habitrpg.android.habitica.models.inventory.Egg +import com.habitrpg.android.habitica.models.inventory.HatchingPotion +import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Consumer + +class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) { + + + private lateinit var binding: DialogPetSuggestHatchBinding + + init { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater + inflater?.let { binding = DialogPetSuggestHatchBinding.inflate(it) } + setAdditionalContentView(binding.root) + } + + fun configure(pet: Animal, egg: Egg?, potion: HatchingPotion?, canHatch: Boolean) { + DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${pet.animal}") + DataBindingUtils.loadImage(binding.hatchingPotionView, "Pet_HatchingPotion_${pet.color}") + binding.petTitleView.text = pet.text + + + if (canHatch) { + binding.descriptionView.text = context.getString(R.string.can_hatch_pet, + egg?.text ?: pet.animal.capitalize(), + potion?.text ?: pet.color.capitalize()) + addButton(R.string.hatch_pet, true, false) { _, _ -> + val thisPotion = potion ?: return@addButton + val thisEgg = egg ?: return@addButton + (getActivity() as? MainActivity)?.hatchPet(thisPotion, thisEgg) + } + setTitle(R.string.hatch_pet_title) + } else { + binding.descriptionView.text = context.getString(R.string.suggest_pet_hatch, + egg?.text ?: pet.animal.capitalize(), + potion?.text ?: pet.color.capitalize()) + setTitle(R.string.unhatched_pet) + } + + addButton(R.string.close, !canHatch) + + val imageName = "social_Pet-${pet.animal}-${pet.color}" + DataBindingUtils.loadImage(imageName) { + val resources = context.resources ?: return@loadImage + val drawable = BitmapDrawable(resources, it.extractAlpha()) + Observable.just(drawable) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Consumer { + binding.petView.background = drawable + }, RxErrorHandler.handleEmptyError()) + } + } +}