Implement more avatar customization options. Fixes #1137

This commit is contained in:
Phillip Thelen 2020-03-20 17:37:29 +01:00
parent 3d9a4eb950
commit dadc3885fa
20 changed files with 363 additions and 144 deletions

View file

@ -155,7 +155,7 @@ android {
multiDexEnabled true
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 2386
versionCode 2388
versionName "2.5"
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/gray_700" />
<corners android:radius="@dimen/rounded_button_radius"/>
<padding android:left="0dip" android:top="0dip" android:right="0dip" android:bottom="0dip" />
<stroke android:color="@color/brand_300" android:width="2dp" />
</shape>

View file

@ -1,37 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<RelativeLayout
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="90dp"
android:minHeight="60dp"
android:minHeight="60dp">
<LinearLayout
android:id="@+id/wrapper"
android:layout_width="76dp"
android:layout_height="wrap_content"
android:background="@drawable/layout_rounded_bg_gray_700"
android:orientation="vertical"
android:clickable="true"
android:background="@drawable/selection_highlight">
<View
android:id="@+id/purchaseOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@android:color/black" />
android:layout_gravity="center">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/imageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitEnd"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
android:layout_height="76dp"
android:layout_gravity="start"
android:scaleType="fitEnd" />
<FrameLayout
android:id="@+id/buy_button"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/layout_rounded_bg_shopitem_price">
<com.habitrpg.android.habitica.ui.views.CurrencyView
android:id="@+id/price_label"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:textColor="@color/gray_300"
tools:text="150"
style="@style/Body1"
app:hasLightBackground="true"
android:textSize="15sp"
android:layout_gravity="center" />
</FrameLayout>
</LinearLayout>
</FrameLayout>

View file

@ -1,21 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="65dp"
android:paddingTop="16dp">
android:paddingTop="16dp"
android:gravity="bottom"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp">
<TextView
android:id="@+id/label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="bottom"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginLeft="@dimen/section_leftright_padding"
android:layout_marginRight="@dimen/section_leftright_padding"/>
android:textAppearance="?android:attr/textAppearanceMedium"/>
<Button
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="40dp"
android:id="@+id/purchaseSetButton"
android:visibility="gone" />
android:visibility="gone"
android:orientation="horizontal"
android:gravity="center"
tools:visibility="visible"
android:paddingStart="@dimen/spacing_medium"
android:paddingEnd="@dimen/spacing_medium"
android:background="@drawable/layout_rounded_bg_gray_700">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/buy_all"
android:textAllCaps="true"
android:textColor="@color/brand_300"
style="@style/Body1"
android:layout_marginEnd="@dimen/spacing_medium"/>
<com.habitrpg.android.habitica.ui.views.CurrencyView
android:id="@+id/set_price_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View file

@ -33,7 +33,8 @@
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="50dp">
android:layout_height="50dp"
android:gravity="center_vertical">
<TextView
android:layout_width="0dp"
@ -52,7 +53,6 @@
android:layout_height="match_parent"
android:id="@+id/avatarSizeSpinner"
android:spinnerMode="dropdown"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:entries="@array/avatar_sizes"/>
</LinearLayout>
@ -66,6 +66,30 @@
android:layout_height="wrap_content"
android:id="@+id/avatarSkinView"
app:equipmentTitle="@string/avatar_skin"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/avatar_extras"
android:textAppearance="?android:attr/textAppearanceMedium"
style="@style/SectionTitle"/>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/CardView.Default">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="?android:listDivider"
android:showDividers="middle">
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/avatar_glasses_view"
app:equipmentTitle="@string/avatar_glasses"/>
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -74,8 +98,8 @@
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/avatar_glasses_view"
app:equipmentTitle="@string/avatar_glasses"/>
android:id="@+id/avatar_accent_view"
app:equipmentTitle="@string/avatar_accent" />
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -86,6 +110,11 @@
android:layout_height="wrap_content"
android:id="@+id/avatar_animal_tail_view"
app:equipmentTitle="@string/animal_tail"/>
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/avatar_headband_view"
app:equipmentTitle="@string/avatar_headband"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
@ -120,11 +149,6 @@
android:layout_height="wrap_content"
android:id="@+id/avatarHairBangsView"
app:equipmentTitle="@string/avatar_bangs" />
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/avatarHairFlowerView"
app:equipmentTitle="@string/avatar_flower" />
<com.habitrpg.android.habitica.ui.views.EquipmentItemRow
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -123,6 +123,9 @@
<action
android:id="@+id/openAvatarDetail"
app:destination="@id/avatarCustomizationFragment" />
<action
android:id="@+id/openAvatarEquipment"
app:destination="@id/avatarEquipmentFragment" />
</fragment>
<fragment
android:id="@+id/itemsFragment"
@ -254,6 +257,17 @@
android:name="category"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/avatarEquipmentFragment"
android:name="com.habitrpg.android.habitica.ui.fragments.inventory.customization.AvatarEquipmentFragment"
android:label="@string/sidebar_avatar" >
<argument
android:name="type"
app:argType="string" />
<argument
android:name="category"
app:argType="string" />
</fragment>
<activity
android:id="@+id/prefsActivity"
android:name="com.habitrpg.android.habitica.ui.activities.PrefsActivity"

View file

@ -44,7 +44,7 @@
<dimen name="grid_item_margin">6dp</dimen>
<dimen name="avatar_small_width">90dp</dimen>
<dimen name="avatar_small_height">90dp</dimen>
<dimen name="customization_width">100dp</dimen>
<dimen name="customization_width">76dp</dimen>
<dimen name="gear_image_size">68dp</dimen>
<dimen name="row_padding">12dp</dimen>
<dimen name="row_title_size">16sp</dimen>

View file

@ -1001,4 +1001,7 @@
<string name="could_not_find_user">Could not find user</string>
<string name="animal_ears">Animal Ears</string>
<string name="animal_tail">Animal Tail</string>
<string name="avatar_headband">Headband</string>
<string name="avatar_accent">Accent</string>
<string name="buy_all">Buy All</string>
</resources>

View file

@ -31,6 +31,7 @@ interface InventoryRepository : BaseRepository {
fun retrieveInAppRewards(): Flowable<List<ShopItem>>
fun getOwnedEquipment(type: String): Flowable<RealmResults<Equipment>>
fun getEquipmentType(type: String, set: String): Flowable<RealmResults<Equipment>>
fun getOwnedItems(itemType: String): Flowable<RealmResults<OwnedItem>>
fun getOwnedItems(): Flowable<Map<String, OwnedItem>>

View file

@ -46,6 +46,10 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie
return localRepository.getOwnedEquipment()
}
override fun getEquipmentType(type: String, set: String): Flowable<RealmResults<Equipment>> {
return localRepository.getEquipmentType(type, set)
}
override fun getOwnedItems(itemType: String): Flowable<RealmResults<OwnedItem>> {
return localRepository.getOwnedItems(itemType, userID)
}

View file

@ -33,6 +33,7 @@ interface InventoryLocalRepository : ContentLocalRepository {
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(userID: String): Flowable<Map<String, OwnedItem>>
fun getEquipmentType(type: String, set: String): Flowable<RealmResults<Equipment>>
fun getEquipment(key: String): Flowable<Equipment>
fun getMounts(type: String, group: String, color: String?): Flowable<RealmResults<Mount>>

View file

@ -70,6 +70,15 @@ class RealmInventoryLocalRepository(realm: Realm, private val context: Context)
.filter { it.isLoaded }
}
override fun getEquipmentType(type: String, set: String): Flowable<RealmResults<Equipment>> {
return realm.where(Equipment::class.java)
.equalTo("type", type)
.equalTo("gearSet", set)
.findAll()
.asFlowable()
.filter { it.isLoaded }
}
override fun getOwnedItems(itemType: String, userID: String): Flowable<RealmResults<OwnedItem>> {
return realm.where(OwnedItem::class.java)
.greaterThan("numberOwned", 0)

View file

@ -24,4 +24,5 @@ open class Equipment : RealmObject() {
var owned: Boolean? = null
var twoHanded = false
var mystery = ""
var gearSet = ""
}

View file

@ -0,0 +1,157 @@
package com.habitrpg.android.habitica.ui.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.os.bundleOf
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.subjects.PublishSubject
import java.util.*
class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<androidx.recyclerview.widget.RecyclerView.ViewHolder>() {
var gemBalance: Int = 0
var equipmentList
: MutableList<Equipment> = ArrayList()
set(value) {
field = value
notifyDataSetChanged()
}
var activeEquipment: String? = null
set(value) {
field = value
this.notifyDataSetChanged()
}
private val selectCustomizationEvents = PublishSubject.create<Equipment>()
private val unlockCustomizationEvents = PublishSubject.create<Equipment>()
private val unlockSetEvents = PublishSubject.create<CustomizationSet>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
val viewID: Int = R.layout.customization_grid_item
val view = LayoutInflater.from(parent.context).inflate(viewID, parent, false)
return EquipmentViewHolder(view)
}
override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
(holder as EquipmentViewHolder).bind(equipmentList[position])
}
override fun getItemCount(): Int {
return equipmentList.size
}
override fun getItemViewType(position: Int): Int {
return if (this.equipmentList[position].javaClass == CustomizationSet::class.java) {
0
} else {
1
}
}
fun setEquipment(newEquipmentList: List<Equipment>) {
this.equipmentList = newEquipmentList.toMutableList()
this.notifyDataSetChanged()
}
fun getSelectCustomizationEvents(): Flowable<Equipment> {
return selectCustomizationEvents.toFlowable(BackpressureStrategy.DROP)
}
fun getUnlockCustomizationEvents(): Flowable<Equipment> {
return unlockCustomizationEvents.toFlowable(BackpressureStrategy.DROP)
}
fun getUnlockSetEvents(): Flowable<CustomizationSet> {
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)
var equipment: Equipment? = null
init {
itemView.setOnClickListener(this)
}
fun bind(equipment: Equipment) {
this.equipment = equipment
DataBindingUtils.loadImage(binding.imageView, "shop_" + this.equipment?.key)
if (equipment.owned == true || equipment.value == 0.0) {
binding.buyButton.visibility = View.GONE
} else {
binding.buyButton.visibility = View.VISIBLE
binding.priceLabel.currency = "gems"
binding.priceLabel.value = if (equipment.gearSet == "animal") {
2.0
} else {
equipment.value
}
}
if (activeEquipment == equipment.key) {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700_brand_border)
} else {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700)
}
}
override fun onClick(v: View) {
if (equipment?.owned != true && (equipment?.value ?: 0.0) > 0.0) {
val dialogContent = LayoutInflater.from(itemView.context).inflate(R.layout.dialog_purchase_customization, null) as LinearLayout
val imageView = dialogContent.findViewById<SimpleDraweeView>(R.id.imageView)
DataBindingUtils.loadImage(imageView, "shop_" + this.equipment?.key)
val priceLabel = dialogContent.findViewById<TextView>(R.id.priceLabel)
priceLabel.text = if (equipment?.gearSet == "animal") {
2.0
} else {
equipment?.value ?: 0
}.toString()
(dialogContent.findViewById<View>(R.id.gem_icon) as? ImageView)?.setImageBitmap(HabiticaIconsHelper.imageOfGem())
val dialog = HabiticaAlertDialog(itemView.context)
dialog.addButton(R.string.purchase_button, true) { _, _ ->
if (equipment?.value ?: 0.0 > gemBalance) {
MainNavigationController.navigate(R.id.gemPurchaseActivity, bundleOf(Pair("openSubscription", false)))
return@addButton
}
equipment?.let {
unlockCustomizationEvents.onNext(it)
}
}
dialog.setTitle(R.string.purchase_customization)
dialog.setAdditionalContentView(dialogContent)
dialog.addButton(R.string.reward_dialog_dismiss, false)
dialog.show()
return
}
if (equipment?.key == activeEquipment) {
return
}
equipment?.let {
selectCustomizationEvents.onNext(it)
}
}
}
}

View file

@ -4,11 +4,14 @@ import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.core.content.ContextCompat
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.os.bundleOf
import com.facebook.drawee.view.SimpleDraweeView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
import com.habitrpg.android.habitica.databinding.CustomizationSectionHeaderBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.inventory.Customization
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
@ -125,35 +128,33 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
internal inner class CustomizationViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val cardView: androidx.cardview.widget.CardView by bindView(itemView, R.id.card_view)
private val linearLayout: RelativeLayout by bindView(itemView, R.id.linearLayout)
private val imageView: SimpleDraweeView by bindView(itemView, R.id.imageView)
private val purchaseOverlay: View by bindView(itemView, R.id.purchaseOverlay)
private val binding = CustomizationGridItemBinding.bind(itemView)
var customization: Customization? = null
init {
linearLayout.setOnClickListener(this)
itemView.setOnClickListener(this)
}
fun bind(customization: Customization) {
this.customization = customization
if (customization.customizationSet?.contains("timeTravel") == true) {
DataBindingUtils.loadImage(this.imageView, customization.getImageName(userSize, hairColor), imageFormat = "gif")
DataBindingUtils.loadImage(binding.imageView, customization.getImageName(userSize, hairColor), imageFormat = "gif")
} else {
DataBindingUtils.loadImage(this.imageView, customization.getImageName(userSize, hairColor))
DataBindingUtils.loadImage(binding.imageView, customization.getImageName(userSize, hairColor))
}
cardView.setCardBackgroundColor(ContextCompat.getColor(itemView.context, android.R.color.white))
if (customization.isUsable) {
imageView.alpha = 1.0f
purchaseOverlay.alpha = 0.0f
if (customization.identifier == activeCustomization) {
cardView.setCardBackgroundColor(ContextCompat.getColor(itemView.context, R.color.brand_500))
}
binding.buyButton.visibility = View.GONE
} else {
imageView.alpha = 0.3f
purchaseOverlay.alpha = 0.8f
binding.buyButton.visibility = View.VISIBLE
binding.priceLabel.currency = "gems"
binding.priceLabel.value = customization.price.toDouble()
}
if (activeCustomization == customization.identifier) {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700_brand_border)
} else {
binding.wrapper.background = itemView.context.getDrawable(R.drawable.layout_rounded_bg_gray_700)
}
}
@ -209,23 +210,24 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
internal inner class SectionViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val binding = CustomizationSectionHeaderBinding.bind(itemView)
private val label: TextView by bindView(itemView, R.id.label)
private val purchaseSetButton: Button by bindView(itemView, R.id.purchaseSetButton)
var context: Context = itemView.context
private var set: CustomizationSet? = null
init {
purchaseSetButton.setOnClickListener(this)
binding.purchaseSetButton.setOnClickListener(this)
}
fun bind(set: CustomizationSet) {
this.set = set
this.label.text = set.text
if (set.hasPurchasable && !set.identifier.contains("timeTravel")) {
this.purchaseSetButton.visibility = View.VISIBLE
this.purchaseSetButton.text = context.getString(R.string.purchase_set_button, set.price)
binding.purchaseSetButton.visibility = View.VISIBLE
binding.setPriceLabel.value = set.price.toDouble()
binding.setPriceLabel.currency = "gems"
} else {
this.purchaseSetButton.visibility = View.GONE
binding.purchaseSetButton.visibility = View.GONE
}
}

View file

@ -83,8 +83,7 @@ class AvatarCustomizationFragment : BaseMainFragment() {
}
}
setGridSpanCount(view.width)
val layoutManager = GridLayoutManager(activity, 2)
val layoutManager = GridLayoutManager(activity, 4)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter.getItemViewType(position) == 0) {
@ -94,6 +93,7 @@ class AvatarCustomizationFragment : BaseMainFragment() {
}
}
}
setGridSpanCount(view.width)
recyclerView.layoutManager = layoutManager
recyclerView.addItemDecoration(MarginDecoration(context))

View file

@ -12,7 +12,7 @@ import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.responses.UnlockResponse
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.CustomizationRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.adapter.CustomizationEquipmentRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
@ -30,7 +30,7 @@ class AvatarEquipmentFragment : BaseMainFragment() {
var category: String? = null
private var activeEquipment: String? = null
internal var adapter: CustomizationRecyclerViewAdapter = CustomizationRecyclerViewAdapter()
internal var adapter: CustomizationEquipmentRecyclerViewAdapter = CustomizationEquipmentRecyclerViewAdapter()
internal var layoutManager: GridLayoutManager = GridLayoutManager(activity, 2)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -39,18 +39,13 @@ class AvatarEquipmentFragment : BaseMainFragment() {
val view = inflater.inflate(R.layout.fragment_recyclerview, container, false)
compositeSubscription.add(adapter.getSelectCustomizationEvents()
.flatMap { customization ->
userRepository.useCustomization(user, customization.type ?: "", customization.category, customization.identifier ?: "")
.flatMap { equipment ->
inventoryRepository.equip(user, if (user?.preferences?.costume == true) "costume" else "equipped", equipment.key ?: "")
}
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getUnlockCustomizationEvents()
.flatMap<UnlockResponse> { customization ->
val user = this.user
if (user != null) {
userRepository.unlockPath(user, customization)
} else {
Flowable.empty()
}
.flatMap<UnlockResponse> {
Flowable.empty()
}
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(adapter.getUnlockSetEvents()
@ -69,7 +64,7 @@ class AvatarEquipmentFragment : BaseMainFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let {
val args = AvatarCustomizationFragmentArgs.fromBundle(it)
val args = AvatarEquipmentFragmentArgs.fromBundle(it)
type = args.type
if (args.category.isNotEmpty()) {
category = args.category
@ -77,7 +72,7 @@ class AvatarEquipmentFragment : BaseMainFragment() {
}
setGridSpanCount(view.width)
val layoutManager = GridLayoutManager(activity, 2)
val layoutManager = GridLayoutManager(activity, 4)
layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (adapter.getItemViewType(position) == 0) {
@ -105,6 +100,9 @@ class AvatarEquipmentFragment : BaseMainFragment() {
private fun loadEquipment() {
val type = this.type ?: return
inventoryRepository.getEquipmentType(type, category ?: "").subscribe(Consumer {
adapter.setEquipment(it)
}, RxErrorHandler.handleEmptyError())
}
private fun setGridSpanCount(width: Int) {
@ -118,15 +116,6 @@ class AvatarEquipmentFragment : BaseMainFragment() {
fun updateUser(user: User) {
this.updateActiveCustomization(user)
if (adapter.customizationList.size != 0) {
val ownedCustomizations = ArrayList<String>()
user.purchased?.customizations?.filter { it.type == this.type }?.mapTo(ownedCustomizations) { it.id }
adapter.updateOwnership(ownedCustomizations)
} else {
this.loadEquipment()
}
this.adapter.userSize = this.user?.preferences?.size
this.adapter.hairColor = this.user?.preferences?.hair?.color
this.adapter.gemBalance = user.gemCount
adapter.notifyDataSetChanged()
}
@ -135,25 +124,16 @@ class AvatarEquipmentFragment : BaseMainFragment() {
if (this.type == null || user.preferences == null) {
return
}
val prefs = this.user?.preferences
val outfit = if (user.preferences?.costume == true) this.user?.items?.gear?.costume else this.user?.items?.gear?.equipped
val activeEquipment = when (this.type) {
"skin" -> prefs?.skin
"shirt" -> prefs?.shirt
"background" -> prefs?.background
"hair" -> when (this.category) {
"bangs" -> prefs?.hair?.bangs.toString()
"base" -> prefs?.hair?.base.toString()
"color" -> prefs?.hair?.color
"flower" -> prefs?.hair?.flower.toString()
"beard" -> prefs?.hair?.beard.toString()
"mustache" -> prefs?.hair?.mustache.toString()
else -> ""
}
"headAccessory" -> outfit?.headAccessory
"back" -> outfit?.back
"eyewear" -> outfit?.eyeWear
else -> ""
}
if (activeEquipment != null) {
this.activeEquipment = activeEquipment
this.adapter.activeCustomization = activeEquipment
this.adapter.activeEquipment = activeEquipment
}
}
}

View file

@ -32,13 +32,14 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis
binding.avatarShirtView.setOnClickListener { displayCustomizationFragment("shirt", null) }
binding.avatarSkinView.setOnClickListener { displayCustomizationFragment("skin", null) }
binding.avatarChairView.setOnClickListener { displayCustomizationFragment("chair", null) }
binding.avatarGlassesView.setOnClickListener { displayCustomizationFragment("eyewear", null) }
binding.avatarAnimalEarsView.setOnClickListener { displayCustomizationFragment("animal_ears", null) }
binding.avatarAnimalTailView.setOnClickListener { displayCustomizationFragment("animal_tails", null) }
binding.avatarGlassesView.setOnClickListener { displayEquipmentFragment("eyewear", "glasses") }
binding.avatarAnimalEarsView.setOnClickListener { displayEquipmentFragment("headAccessory", "animal") }
binding.avatarAnimalTailView.setOnClickListener { displayEquipmentFragment("back", "animal") }
binding.avatarHeadbandView.setOnClickListener { displayEquipmentFragment("headAccessory", "headband") }
binding.avatarHairColorView.setOnClickListener { displayCustomizationFragment("hair", "color") }
binding.avatarHairBangsView.setOnClickListener { displayCustomizationFragment("hair", "bangs") }
binding.avatarHairBaseView.setOnClickListener { displayCustomizationFragment("hair", "base") }
binding.avatarHairFlowerView.setOnClickListener { displayCustomizationFragment("hair", "flower") }
binding.avatarAccentView.setOnClickListener { displayCustomizationFragment("hair", "flower") }
binding.avatarHairBeardView.setOnClickListener { displayCustomizationFragment("hair", "beard") }
binding.avatarHairMustacheView.setOnClickListener { displayCustomizationFragment("hair", "mustache") }
binding.avatarBackgroundView.setOnClickListener { displayCustomizationFragment("background", null) }
@ -56,6 +57,10 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarDetail(type, category ?: ""))
}
private fun displayEquipmentFragment(type: String, category: String?) {
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarEquipment(type, category ?: ""))
}
fun updateUser(user: User) {
this.setSize(user.preferences?.size)
setCustomizations(user)
@ -63,32 +68,20 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis
private fun setCustomizations(user: User) {
binding.avatarShirtView.customizationIdentifier = user.preferences?.size + "_shirt_" + user.preferences?.shirt
binding.avatarShirtView.equipmentName = user.preferences?.shirt
binding.avatarSkinView.customizationIdentifier = "skin_" + user.preferences?.skin
binding.avatarSkinView.equipmentName = user.preferences?.skin
val chair = user.preferences?.chair
binding.avatarChairView.customizationIdentifier = if (chair?.startsWith("handleless") == true) "chair_$chair" else chair
binding.avatarChairView.equipmentName = chair?.removePrefix("chair_")
binding.avatarGlassesView.customizationIdentifier = "shop_" + user.equipped?.eyeWear
binding.avatarGlassesView.equipmentName = user.equipped?.eyeWear
binding.avatarAnimalEarsView.customizationIdentifier = "shop_" + user.equipped?.headAccessory
binding.avatarAnimalEarsView.equipmentName = user.equipped?.headAccessory
binding.avatarAnimalTailView.customizationIdentifier = "shop_" + user.equipped?.back
binding.avatarAnimalTailView.equipmentName = user.equipped?.back
binding.avatarGlassesView.equipmentIdentifier = user.equipped?.eyeWear
binding.avatarAnimalEarsView.equipmentIdentifier = user.equipped?.headAccessory
binding.avatarHeadbandView.equipmentIdentifier = user.equipped?.headAccessory
binding.avatarAnimalTailView.equipmentIdentifier = user.equipped?.back
binding.avatarHairColorView.customizationIdentifier = if (user.preferences?.hair?.color != null && user.preferences?.hair?.color != "") "hair_bangs_1_" + user.preferences?.hair?.color else ""
binding.avatarHairColorView.equipmentName = user.preferences?.hair?.color
binding.avatarHairBangsView.customizationIdentifier = if (user.preferences?.hair?.bangs != null && user.preferences?.hair?.bangs != 0) "hair_bangs_" + user.preferences?.hair?.bangs + "_" + user.preferences?.hair?.color else ""
binding.avatarHairBangsView.equipmentName = user.preferences?.hair?.bangs.toString()
binding.avatarHairBaseView.customizationIdentifier = if (user.preferences?.hair?.base != null && user.preferences?.hair?.base != 0) "hair_base_" + user.preferences?.hair?.base + "_" + user.preferences?.hair?.color else ""
binding.avatarHairBaseView.equipmentName = user.preferences?.hair?.base.toString()
binding.avatarHairFlowerView.customizationIdentifier = if (user.preferences?.hair?.flower != null && user.preferences?.hair?.flower != 0) "hair_flower_" + user.preferences?.hair?.flower else ""
binding.avatarHairFlowerView.equipmentName = user.preferences?.hair?.bangs.toString()
binding.avatarAccentView.customizationIdentifier = if (user.preferences?.hair?.flower != null && user.preferences?.hair?.flower != 0) "hair_flower_" + user.preferences?.hair?.flower else ""
binding.avatarHairBeardView.customizationIdentifier = if (user.preferences?.hair?.beard != null && user.preferences?.hair?.beard != 0) "hair_beard_" + user.preferences?.hair?.beard + "_" + user.preferences?.hair?.color else ""
binding.avatarHairBeardView.equipmentName = user.preferences?.hair?.beard.toString()
binding.avatarHairMustacheView.customizationIdentifier = if (user.preferences?.hair?.mustache != null && user.preferences?.hair?.mustache != 0) "hair_mustache_" + user.preferences?.hair?.mustache + "_" + user.preferences?.hair?.color else ""
binding.avatarHairMustacheView.equipmentName = user.preferences?.hair?.mustache.toString()
binding.avatarBackgroundView.customizationIdentifier = "background_" + user.preferences?.background
binding.avatarBackgroundView.equipmentName = user.preferences?.background
}
private fun setSize(size: String?) {

View file

@ -5,40 +5,37 @@ import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.ItemImageRowBinding
import com.habitrpg.android.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import kotlinx.android.synthetic.main.item_image_row.view.*
class EquipmentItemRow(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
class EquipmentItemRow(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
private val binding: ItemImageRowBinding = ItemImageRowBinding.inflate(context.layoutInflater, this)
var equipmentIdentifier: String? = null
set(value) {
field = value
val imageName = if (equipmentIdentifier?.isNotEmpty() == true && equipmentIdentifier?.endsWith("base_0") == false) "shop_$equipmentIdentifier" else "head_0"
DataBindingUtils.loadImage(imageView, imageName)
DataBindingUtils.loadImage(binding.imageView, imageName)
}
var customizationIdentifier: String? = null
set(value) {
field = value
val imageName = if (customizationIdentifier?.isNotEmpty() == true) customizationIdentifier else "head_0"
DataBindingUtils.loadImage(imageView, imageName)
}
var equipmentName: String? = ""
set(value) {
field = value
valueTextView.text = equipmentName
DataBindingUtils.loadImage(binding.imageView, imageName)
}
init {
View.inflate(context, R.layout.item_image_row, this)
isClickable = true
val attributes = context?.theme?.obtainStyledAttributes(
val attributes = context.theme?.obtainStyledAttributes(
attrs,
R.styleable.EquipmentItemRow,
0, 0)
titleTextView.text = attributes?.getString(R.styleable.EquipmentItemRow_equipmentTitle)
binding.titleTextView.text = attributes?.getString(R.styleable.EquipmentItemRow_equipmentTitle)
binding.valueTextView.visibility = View.GONE
}
}

View file

@ -44,6 +44,7 @@ public class EquipmentListDeserializer implements JsonDeserializer<List<Equipmen
item.set_int(parsedItem.get_int());
item.setTwoHanded(parsedItem.getTwoHanded());
item.setMystery(parsedItem.getMystery());
item.setGearSet(parsedItem.getGearSet());
} else {
item.setOwned(itemObject.getAsBoolean());
}