diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index cc5e25dea..211854692 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -122,8 +122,9 @@ dependencies {
implementation 'com.google.firebase:firebase-config-ktx'
implementation 'com.google.firebase:firebase-perf-ktx'
implementation 'com.google.android.gms:play-services-ads:20.6.0'
- implementation 'com.google.android.gms:play-services-auth:20.1.0'
+ implementation 'com.google.android.gms:play-services-auth:20.2.0'
implementation 'com.nex3z:flow-layout:1.2.2'
+ implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation 'androidx.core:core-ktx:1.7.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
diff --git a/Habitica/res/layout/customization_grid_background_item.xml b/Habitica/res/layout/customization_grid_background_item.xml
index 1e4c6878b..620778f8f 100644
--- a/Habitica/res/layout/customization_grid_background_item.xml
+++ b/Habitica/res/layout/customization_grid_background_item.xml
@@ -8,12 +8,16 @@
android:background="@drawable/layout_rounded_bg_window"
android:orientation="vertical"
android:layout_gravity="center">
-
+ android:layout_height="76dp">
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/customization_grid_item.xml b/Habitica/res/layout/customization_grid_item.xml
index 3adbc453e..2d35f366a 100644
--- a/Habitica/res/layout/customization_grid_item.xml
+++ b/Habitica/res/layout/customization_grid_item.xml
@@ -8,12 +8,16 @@
android:background="@drawable/layout_rounded_bg_window"
android:orientation="vertical"
android:layout_gravity="center">
+
+ android:scaleType="fitCenter" />
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/customization_section_header.xml b/Habitica/res/layout/customization_section_header.xml
index e2fa4b35d..2a87a9365 100644
--- a/Habitica/res/layout/customization_section_header.xml
+++ b/Habitica/res/layout/customization_section_header.xml
@@ -13,7 +13,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
tools:text="Test header"
- style="@style/Overline"
+ style="@style/Caption2"
+ android:gravity="center"
android:textColor="@color/text_ternary"/>
-
-
-
-
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_refresh_recyclerview.xml b/Habitica/res/layout/fragment_refresh_recyclerview.xml
index b97735dc9..850efe905 100644
--- a/Habitica/res/layout/fragment_refresh_recyclerview.xml
+++ b/Habitica/res/layout/fragment_refresh_recyclerview.xml
@@ -11,11 +11,12 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior">
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index 683a09184..5432f0f7b 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -1260,4 +1260,5 @@
November
December
Adjust when your day switches over past the default time of midnight.
+ Buy Set
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 c69c95aca..a12e2f7a3 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
@@ -1,7 +1,6 @@
package com.habitrpg.android.habitica.ui.adapter
import android.content.Context
-import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -13,6 +12,7 @@ import androidx.core.os.bundleOf
import coil.load
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
+import com.habitrpg.android.habitica.databinding.CustomizationSectionFooterBinding
import com.habitrpg.android.habitica.databinding.CustomizationSectionHeaderBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.inventory.Customization
@@ -61,6 +61,10 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.customization_section_header, parent, false)
SectionViewHolder(view)
+ } else if (viewType == 1) {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(R.layout.customization_section_footer, parent, false)
+ SectionFooterViewHolder(view)
} else {
val viewID: Int = if (customizationType == "background") {
R.layout.customization_grid_background_item
@@ -78,8 +82,10 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
position: Int
) {
val obj = customizationList[position]
- if (obj.javaClass == CustomizationSet::class.java) {
+ if (getItemViewType(position) == 0) {
(holder as SectionViewHolder).bind(obj as CustomizationSet)
+ } else if (getItemViewType(position) == 1) {
+ (holder as SectionFooterViewHolder).bind(obj as CustomizationSet)
} else {
(holder as CustomizationViewHolder).bind(customizationList[position] as Customization)
}
@@ -91,10 +97,13 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
override fun getItemViewType(position: Int): Int {
if (customizationList.size <= position) return 0
- return if (this.customizationList[position].javaClass == CustomizationSet::class.java) {
+ return if (this.customizationList[position] is CustomizationSet &&
+ (position < customizationList.size && customizationList[position+1] is CustomizationSet)) {
+ 1
+ } else if (this.customizationList[position] is CustomizationSet) {
0
} else {
- 1
+ 2
}
}
@@ -110,17 +119,20 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
}
}
if (customization.customizationSet != null && customization.customizationSet != lastSet.identifier) {
+ if (lastSet.hasPurchasable) {
+ customizationList.add(lastSet)
+ }
val set = CustomizationSet()
set.identifier = customization.customizationSet
set.text = customization.customizationSetName
set.price = customization.setPrice ?: 0
- set.hasPurchasable = !customization.isUsable(ownedCustomizations.contains(customization.id))
+ set.hasPurchasable = true
lastSet = set
customizationList.add(set)
}
customizationList.add(customization)
- if (!customization.isUsable(ownedCustomizations.contains(customization.id)) && !lastSet.hasPurchasable) {
- lastSet.hasPurchasable = true
+ if (customization.isUsable(ownedCustomizations.contains(customization.id)) && lastSet.hasPurchasable) {
+ lastSet.hasPurchasable = false
}
}
this.notifyDataSetChanged()
@@ -156,13 +168,6 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
DataBindingUtils.loadImage(binding.imageView, customization.getIconName(userSize, hairColor))
}
- if (customization.type == "background") {
- val params = (binding.imageView.layoutParams as? LinearLayout.LayoutParams)?.apply {
- gravity = Gravity.CENTER
- }
- binding.imageView.layoutParams = params
- }
-
if (customization.isUsable(ownedCustomizations.contains(customization.id))) {
binding.buyButton.visibility = View.GONE
} else {
@@ -232,9 +237,18 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
}
}
- internal inner class SectionViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
+ internal inner class SectionViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView) {
private val binding = CustomizationSectionHeaderBinding.bind(itemView)
+
+ fun bind(set: CustomizationSet) {
+ binding.label.text = set.text
+ }
+ }
+
+ internal inner class SectionFooterViewHolder(itemView: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(itemView), View.OnClickListener {
+
+ private val binding = CustomizationSectionFooterBinding.bind(itemView)
var context: Context = itemView.context
private var set: CustomizationSet? = null
@@ -244,7 +258,6 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
fun bind(set: CustomizationSet) {
this.set = set
- binding.label.text = set.text
if (set.hasPurchasable && set.identifier?.contains("timeTravel") != true) {
binding.purchaseSetButton.visibility = View.VISIBLE
binding.setPriceLabel.value = set.price.toDouble()
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
index 8af1419f3..298048cf2 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt
@@ -9,6 +9,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
+import androidx.core.view.setPadding
import androidx.viewbinding.ViewBinding
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout
@@ -17,6 +18,7 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.extensions.getThemeColor
+import com.habitrpg.android.habitica.extensions.setScaledPadding
import com.habitrpg.android.habitica.helpers.SoundManager
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper
@@ -85,6 +87,18 @@ abstract class BaseMainFragment : BaseFragment() {
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
activity?.toolbar?.let { ToolbarColorHelper.colorizeToolbar(it, activity, null) }
+ updateToolbarInteractivity()
+ }
+
+ var isTitleInteractive = false
+
+ open fun updateToolbarInteractivity() {
+ activity?.binding?.toolbarTitle?.background?.alpha = if (isTitleInteractive) 255 else 0
+ if (isTitleInteractive) {
+ activity?.binding?.toolbarTitle?.setScaledPadding(context, 16, 1, 16, 1)
+ } else {
+ activity?.binding?.toolbarTitle?.setPadding(0)
+ }
}
private fun updateTabLayoutVisibility() {
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 f49198736..4189b6ee4 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
@@ -10,8 +10,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import androidx.core.content.ContextCompat
-import androidx.recyclerview.widget.GridLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.google.android.flexbox.AlignItems
+import com.google.android.flexbox.FlexDirection.ROW
+import com.google.android.flexbox.FlexboxLayoutManager
+import com.google.android.flexbox.JustifyContent
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.CustomizationRepository
@@ -60,7 +63,7 @@ class AvatarCustomizationFragment :
private var activeCustomization: String? = null
internal var adapter: CustomizationRecyclerViewAdapter = CustomizationRecyclerViewAdapter()
- internal var layoutManager: GridLayoutManager = GridLayoutManager(activity, 2)
+ internal var layoutManager: FlexboxLayoutManager = FlexboxLayoutManager(activity, ROW)
private val currentFilter = BehaviorSubject.create()
private val ownedCustomizations = PublishSubject.create>()
@@ -116,16 +119,8 @@ class AvatarCustomizationFragment :
}
adapter.customizationType = type
binding?.refreshLayout?.setOnRefreshListener(this)
- val layoutManager = GridLayoutManager(activity, 4)
- layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
- override fun getSpanSize(position: Int): Int {
- return if (adapter.getItemViewType(position) == 0) {
- layoutManager.spanCount
- } else {
- 1
- }
- }
- }
+ layoutManager.justifyContent = JustifyContent.CENTER
+ layoutManager.alignItems = AlignItems.FLEX_START
setGridSpanCount(view.width)
binding?.recyclerView?.layoutManager = layoutManager
@@ -145,6 +140,7 @@ class AvatarCustomizationFragment :
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
+ super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_list_customizations, menu)
filterMenuItem = menu.findItem(R.id.action_filter)
@@ -192,6 +188,7 @@ class AvatarCustomizationFragment :
ownedCustomizations.toFlowable(BackpressureStrategy.DROP))
.subscribe(
{ (customizations, filter, ownedCustomizations) ->
+ adapter.ownedCustomizations = ownedCustomizations.map { it.key + "_" + it.type + "_" + it.category }
if (filter.isFiltering) {
val displayedCustomizations = mutableListOf()
for (customization in customizations) {
@@ -223,7 +220,6 @@ class AvatarCustomizationFragment :
}
)
}
- adapter.ownedCustomizations = ownedCustomizations.map { it.key + "_" + it.type + "_" + it.category }
},
RxErrorHandler.handleEmptyError()
)
@@ -239,8 +235,9 @@ class AvatarCustomizationFragment :
var spanCount = (width / itemWidth).toInt()
if (spanCount == 0) {
spanCount = 1
+ } else if (type == "backgrounds") {
+ spanCount = 3
}
- layoutManager.spanCount = spanCount
}
fun updateUser(user: User?) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
index 4843bcb2b..2df51c4c4 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/SectionViewHolder.kt
@@ -29,7 +29,6 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
constructor(parent: ViewGroup) : this(parent.inflate(R.layout.customization_section_header))
init {
- itemView.findViewById(R.id.purchaseSetButton)?.visibility = View.GONE
selectionSpinner?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
spinnerSelectionChanged?.invoke()