diff --git a/Habitica/build.gradle b/Habitica/build.gradle index c194341a3..1ae5deb61 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -166,7 +166,7 @@ android { buildConfigField "String", "TESTING_LEVEL", "\"production\"" resConfigs 'en', 'bg', 'de', 'en-rGB', 'es', 'fr', 'hr-rHR', 'in', 'it', 'iw', 'ja', 'ko', 'lt', 'nl', 'pl', 'pt-rBR', 'pt-rPT', 'ru', 'tr', 'zh', 'zh-rTW' - versionCode 3321 + versionCode 3501 versionName "3.6" targetSdkVersion 32 diff --git a/Habitica/res/drawable/purchase_dialog_spacing.xml b/Habitica/res/drawable/purchase_dialog_spacing.xml new file mode 100644 index 000000000..1135937b0 --- /dev/null +++ b/Habitica/res/drawable/purchase_dialog_spacing.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/Habitica/res/layout/dialog_purchase_customizationset.xml b/Habitica/res/layout/dialog_purchase_customizationset.xml new file mode 100644 index 000000000..05c3ab5aa --- /dev/null +++ b/Habitica/res/layout/dialog_purchase_customizationset.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/Habitica/res/layout/purchase_dialog_background.xml b/Habitica/res/layout/purchase_dialog_background.xml index 987007542..19961e095 100644 --- a/Habitica/res/layout/purchase_dialog_background.xml +++ b/Habitica/res/layout/purchase_dialog_background.xml @@ -9,8 +9,8 @@ tools:parentTag="LinearLayout" tools:orientation="vertical"> - diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt index 7f8be410a..92b2ceaaf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/UserRepository.kt @@ -6,7 +6,6 @@ import com.habitrpg.android.habitica.models.QuestAchievement import com.habitrpg.android.habitica.models.Skill import com.habitrpg.android.habitica.models.TeamPlan import com.habitrpg.android.habitica.models.inventory.Customization -import com.habitrpg.android.habitica.models.inventory.CustomizationSet import com.habitrpg.android.habitica.models.responses.SkillResponse import com.habitrpg.android.habitica.models.responses.UnlockResponse import com.habitrpg.android.habitica.models.responses.VerifyUsernameResponse @@ -51,9 +50,8 @@ interface UserRepository : BaseRepository { fun changeClass(selectedClass: String): Flowable - fun unlockPath(user: User?, unlockPath: String?, type: String?, price: Int): Flowable + fun unlockPath(user: User?, path: String, price: Int): Flowable fun unlockPath(user: User?, customization: Customization): Flowable - fun unlockPath(set: CustomizationSet): Flowable fun runCron(tasks: MutableList) fun runCron() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt index 7236c7e0e..a6b70ed67 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt @@ -14,7 +14,6 @@ import com.habitrpg.android.habitica.models.QuestAchievement import com.habitrpg.android.habitica.models.Skill import com.habitrpg.android.habitica.models.TeamPlan import com.habitrpg.android.habitica.models.inventory.Customization -import com.habitrpg.android.habitica.models.inventory.CustomizationSet import com.habitrpg.android.habitica.models.responses.SkillResponse import com.habitrpg.android.habitica.models.responses.TaskDirection import com.habitrpg.android.habitica.models.responses.UnlockResponse @@ -162,11 +161,7 @@ class UserRepositoryImpl( override fun changeClass(selectedClass: String): Flowable = apiClient.changeClass(selectedClass) .flatMap { retrieveUser(false) } - override fun unlockPath(user: User?, unlockPath: String?, type: String?, price: Int): Flowable { - var path = unlockPath ?: return Flowable.empty() - if (path.last() == '.' && type == "background") { - path += user?.preferences?.background - } + override fun unlockPath(user: User?, path: String, price: Int): Flowable { return zipWithLiveUser(apiClient.unlockPath(path)) { unlockResponse, copiedUser -> val user = localRepository.getUnmanagedCopy(copiedUser) user.preferences = unlockResponse.preferences @@ -179,32 +174,11 @@ class UserRepositoryImpl( } override fun unlockPath(user: User?, customization: Customization): Flowable { - return unlockPath(user, customization.path, customization.type, customization.price ?: 0) - } - - override fun unlockPath(set: CustomizationSet): Flowable { - var path = "" - for (customization in set.customizations) { - path = path + "," + customization.path + var path = customization.path + if (path.last() == '.' && customization.type == "background") { + path += user?.preferences?.background } - if (path.isEmpty()) { - return Flowable.just(null) - } - path = path.substring(1) - return Flowable.zip( - apiClient.unlockPath(path), - localRepository.getUser(userID).firstElement().toFlowable() - .map { localRepository.getUnmanagedCopy(it) } - .skipNull(), - { unlockResponse, copiedUser -> - copiedUser.preferences = unlockResponse.preferences - copiedUser.purchased = unlockResponse.purchased - copiedUser.items = unlockResponse.items - copiedUser.balance = copiedUser.balance - set.price / 4.0 - localRepository.saveUser(copiedUser, false) - unlockResponse - } - ) + return unlockPath(user, path, customization.price ?: 0) } override fun runCron() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt index 200044c00..94fa0bcd3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/shops/ShopItem.kt @@ -6,8 +6,10 @@ import com.google.gson.annotations.SerializedName import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.models.BaseObject import com.habitrpg.android.habitica.models.inventory.Customization +import com.habitrpg.android.habitica.models.inventory.CustomizationSet import com.habitrpg.android.habitica.models.inventory.ItemEvent import com.habitrpg.android.habitica.models.user.User +import io.realm.RealmList import io.realm.RealmObject import io.realm.annotations.PrimaryKey @@ -48,6 +50,8 @@ open class ShopItem : RealmObject(), BaseObject { var level: Int? = null var event: ItemEvent? = null + var setImageNames = RealmList() + val isTypeItem: Boolean get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType || "debuffPotion" == purchaseType || "fortify" == purchaseType @@ -164,5 +168,27 @@ open class ShopItem : RealmObject(), BaseObject { item.imageName = customization.getImageName(userSize, hairColor) return item } + + fun fromCustomizationSet(set: CustomizationSet, userSize: String?, hairColor: String?): ShopItem { + val item = ShopItem() + var path = "" + for (customization in set.customizations) { + path = path + "," + customization.path + } + if (path.isEmpty()) { + item.path = path + } else { + item.path = path.substring(1) + } + item.text = set.text + item.key = set.identifier ?: "" + item.currency = "gems" + item.value = set.price + item.purchaseType = "customizationSet" + set.customizations.forEach { + item.setImageNames.add(it.getIconName(userSize, hairColor)) + } + return item + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt index 5fc97bd88..e5dfea384 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationEquipmentRecyclerViewAdapter.kt @@ -37,7 +37,6 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R private val selectCustomizationEvents = PublishSubject.create() private val unlockCustomizationEvents = PublishSubject.create() - private val unlockSetEvents = PublishSubject.create() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder { val viewID: Int = R.layout.customization_grid_item @@ -81,10 +80,6 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R return unlockCustomizationEvents.toFlowable(BackpressureStrategy.DROP) } - fun getUnlockSetEvents(): Flowable { - return unlockSetEvents.toFlowable(BackpressureStrategy.DROP) - } - internal inner class EquipmentViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { private val binding = CustomizationGridItemBinding.bind(itemView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt index cca9f40b7..d01ce9997 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/CustomizationRecyclerViewAdapter.kt @@ -4,10 +4,7 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.TextView import androidx.core.content.ContextCompat -import androidx.core.os.bundleOf import coil.load import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R @@ -48,14 +45,6 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler var ownedCustomizations: List = listOf() private val selectCustomizationEvents = PublishSubject.create() - private val unlockCustomizationEvents = PublishSubject.create() - private val unlockSetEvents = PublishSubject.create() - - fun updateOwnership(ownedCustomizations: List) { - this.ownedCustomizations = ownedCustomizations - setCustomizations(unsortedCustomizations) - notifyDataSetChanged() - } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder { return if (viewType == 0) { @@ -132,6 +121,7 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler customizationList.add(set) } customizationList.add(customization) + lastSet.customizations.add(customization) if (customization.isUsable(ownedCustomizations.contains(customization.id)) && lastSet.hasPurchasable) { lastSet.hasPurchasable = false } @@ -143,14 +133,6 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler return selectCustomizationEvents.toFlowable(BackpressureStrategy.DROP) } - fun getUnlockCustomizationEvents(): Flowable { - return unlockCustomizationEvents.toFlowable(BackpressureStrategy.DROP) - } - - fun getUnlockSetEvents(): Flowable { - return unlockSetEvents.toFlowable(BackpressureStrategy.DROP) - } - internal inner class CustomizationViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener { private val binding = CustomizationGridItemBinding.bind(itemView) @@ -248,36 +230,10 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler } override fun onClick(v: View) { - val dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_purchase_customization, null) as LinearLayout - - val priceLabel = dialogContent.findViewById(R.id.priceLabel) - priceLabel.text = set?.price.toString() - - val dialog = HabiticaAlertDialog(context) - dialog.addButton(R.string.purchase_button, true) { _, _ -> - if (set?.price ?: 0 > gemBalance) { - MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false))) - return@addButton - } - set?.customizations = ArrayList() - customizationList - .filter { Customization::class.java.isAssignableFrom(it.javaClass) } - .map { it as Customization } - .filter { it.customizationSet != null && it.customizationSet == set?.identifier } - .forEach { set?.customizations?.add(it) } - if (additionalSetItems.isNotEmpty()) { - additionalSetItems - .filter { !it.isUsable(ownedCustomizations.contains(it.id)) && it.customizationSet == set?.identifier } - .forEach { set?.customizations?.add(it) } - } - set?.let { - unlockSetEvents.onNext(it) - } + set?.let { + val dialog = PurchaseDialog(itemView.context, HabiticaBaseApplication.userComponent, ShopItem.fromCustomizationSet(it, userSize, hairColor)) + dialog.show() } - dialog.setTitle(context.getString(R.string.purchase_set_title, set?.text)) - dialog.setAdditionalContentView(dialogContent) - dialog.addButton(R.string.reward_dialog_dismiss, false) - dialog.show() } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt index 4189b6ee4..78e3f7c46 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt @@ -86,24 +86,6 @@ class AvatarCustomizationFragment : } .subscribe({ }, RxErrorHandler.handleEmptyError()) ) - compositeSubscription.add( - adapter.getUnlockCustomizationEvents() - .flatMap { customization -> - userRepository.unlockPath(userViewModel.user.value, customization) - } - .flatMap { userRepository.retrieveUser(withTasks = false, forced = true) } - .flatMap { inventoryRepository.retrieveInAppRewards() } - .subscribe({ }, RxErrorHandler.handleEmptyError()) - ) - compositeSubscription.add( - adapter.getUnlockSetEvents() - .flatMap { set -> - userRepository.unlockPath(set) - } - .flatMap { userRepository.retrieveUser(withTasks = false, forced = true) } - .flatMap { inventoryRepository.retrieveInAppRewards() } - .subscribe({ }, RxErrorHandler.handleEmptyError()) - ) return super.onCreateView(inflater, container, savedInstanceState) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt index b0e711b5f..2d3636ff3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarEquipmentFragment.kt @@ -65,13 +65,6 @@ class AvatarEquipmentFragment : } .subscribe({ }, RxErrorHandler.handleEmptyError()) ) - compositeSubscription.add( - adapter.getUnlockSetEvents() - .flatMap { set -> - userRepository.unlockPath(set) - } - .subscribe({ }, RxErrorHandler.handleEmptyError()) - ) return super.onCreateView(inflater, container, savedInstanceState) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt index 2de98b3fc..4f6f81151 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/dialogs/HabiticaAlertDialog.kt @@ -11,7 +11,6 @@ import android.view.ViewGroup import android.view.WindowManager import android.view.animation.AccelerateInterpolator import android.widget.Button -import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.RelativeLayout import android.widget.TextView @@ -26,10 +25,10 @@ import com.habitrpg.android.habitica.extensions.layoutInflater import com.habitrpg.android.habitica.ui.activities.BaseActivity import com.habitrpg.android.habitica.ui.views.login.LockableScrollView import com.plattysoft.leonids.ParticleSystem -import java.lang.ref.WeakReference import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.lang.ref.WeakReference open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.HabiticaAlertDialogTheme) { @@ -44,7 +43,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style. private val dialogContainer: LinearLayout private var titleTextView: TextView private var messageTextView: TextView - internal var contentView: FrameLayout + internal var contentView: ViewGroup private var scrollingSeparator: View internal var scrollView: LockableScrollView protected var buttonsWrapper: LinearLayout diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt index cf52fd1ba..db42164ee 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt @@ -105,8 +105,11 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop checkGearClass() } "gems" == shopItem.purchaseType -> contentView = PurchaseDialogGemsContent(context) - "background" == shopItem.purchaseType -> contentView = PurchaseDialogBackgroundContent(context) + "background" == shopItem.purchaseType -> { + contentView = PurchaseDialogBackgroundContent(context) + } "customization" == shopItem.purchaseType -> contentView = PurchaseDialogCustomizationContent(context) + "customizationSet" == shopItem.purchaseType -> contentView = PurchaseDialogCustomizationSetContent(context) else -> contentView = PurchaseDialogBaseContent(context) } @@ -283,6 +286,10 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop checkGearClass() } setLimitedTextView() + + if (additionalContentView is PurchaseDialogBackgroundContent) { + (additionalContentView as PurchaseDialogBackgroundContent).setAvatar(userRepository.getUnmanagedCopy(user)) + } } override fun dismiss() { @@ -354,10 +361,8 @@ class PurchaseDialog(context: Context, component: UserComponent?, val item: Shop observable = inventoryRepository.purchaseQuest(shopItem.key).cast(Any::class.java) } else if (shopItem.purchaseType == "debuffPotion") { observable = userRepository.useSkill(shopItem.key, null).cast(Any::class.java) - } else if (shopItem.purchaseType == "customization" || shopItem.purchaseType == "background") { - observable = userRepository.unlockPath(user, item.path, item.purchaseType, - item.value - ).cast(Any::class.java) + } else if (shopItem.purchaseType == "customization" || shopItem.purchaseType == "background" || shopItem.purchaseType == "customizationSet") { + observable = userRepository.unlockPath(user, item.path ?: "", item.value).cast(Any::class.java) } else if (shopItem.purchaseType == "debuffPotion") { observable = userRepository.useSkill(shopItem.key, null).cast(Any::class.java) } else if (shopItem.purchaseType == "card") { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogBackgroundContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogBackgroundContent.kt index 22346a7cc..388e84e5e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogBackgroundContent.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogBackgroundContent.kt @@ -5,17 +5,22 @@ import android.widget.ImageView import android.widget.TextView import com.habitrpg.android.habitica.databinding.PurchaseDialogBackgroundBinding import com.habitrpg.android.habitica.extensions.layoutInflater +import com.habitrpg.android.habitica.models.Avatar import com.habitrpg.android.habitica.models.shops.ShopItem class PurchaseDialogBackgroundContent(context: Context) : PurchaseDialogContent(context) { val binding = PurchaseDialogBackgroundBinding.inflate(context.layoutInflater, this) override val imageView: ImageView - get() = binding.imageView + get() = ImageView(context) override val titleTextView: TextView get() = binding.titleTextView override fun setItem(item: ShopItem) { - super.setItem(item) + binding.titleTextView.text = item.text binding.notesTextView.text = item.notes } + + fun setAvatar(avatar: Avatar) { + binding.avatarView.setAvatar(avatar) + } } \ No newline at end of file diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogCustomizationSetContent.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogCustomizationSetContent.kt new file mode 100644 index 000000000..a79d830a2 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialogCustomizationSetContent.kt @@ -0,0 +1,32 @@ +package com.habitrpg.android.habitica.ui.views.shops + +import android.content.Context +import android.widget.ImageView +import android.widget.TextView +import com.google.android.flexbox.FlexboxLayout +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.databinding.DialogPurchaseCustomizationsetBinding +import com.habitrpg.android.habitica.extensions.dpToPx +import com.habitrpg.android.habitica.extensions.layoutInflater +import com.habitrpg.android.habitica.models.shops.ShopItem +import com.habitrpg.android.habitica.ui.helpers.loadImage + +class PurchaseDialogCustomizationSetContent(context: Context) : PurchaseDialogContent(context) { + val binding = DialogPurchaseCustomizationsetBinding.inflate(context.layoutInflater, this) + override val imageView: ImageView + get() = ImageView(context) + override val titleTextView: TextView + get() = binding.titleTextView + + override fun setItem(item: ShopItem) { + titleTextView.text = item.text + binding.imageViewWrapper.removeAllViews() + item.setImageNames.forEach { + val imageView = ImageView(context) + imageView.setBackgroundResource(R.drawable.layout_rounded_bg_window) + imageView.loadImage(it) + imageView.layoutParams = FlexboxLayout.LayoutParams(76.dpToPx(context), 76.dpToPx(context)) + binding.imageViewWrapper.addView(imageView) + } + } +} \ No newline at end of file