mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-18 11:49:01 +00:00
Implement background/customization filtering
This commit is contained in:
parent
ddb1650b01
commit
fffae439a4
5 changed files with 367 additions and 8 deletions
200
Habitica/res/layout/bottom_sheet_backgrounds_filter.xml
Normal file
200
Habitica/res/layout/bottom_sheet_backgrounds_filter.xml
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:paddingStart="@dimen/bottom_sheet_inset"
|
||||
android:paddingEnd="@dimen/bottom_sheet_inset">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="@dimen/spacing_large">
|
||||
|
||||
<TextView
|
||||
style="@style/Headline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/background_filters"
|
||||
android:textColor="@color/text_primary"
|
||||
android:layout_gravity="center_vertical"/>
|
||||
<Space
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content" />
|
||||
<Button
|
||||
android:id="@+id/clear_button"
|
||||
style="@style/Body1_Button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:text="@string/clear"
|
||||
android:textColor="?colorPrimary"
|
||||
android:textSize="16sp"
|
||||
android:gravity="end|center_vertical"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingStart="0dp"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/task_type_title"
|
||||
style="@style/Caption3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/show_me"
|
||||
android:textAllCaps="true"/>
|
||||
<RadioGroup
|
||||
android:id="@+id/show_me_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
<RadioButton
|
||||
android:id="@+id/show_all_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/all"
|
||||
style="@style/TaskFilterRadioButton"
|
||||
android:checked="true"
|
||||
/>
|
||||
<RadioButton
|
||||
android:id="@+id/show_purchased_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/purchased"
|
||||
style="@style/TaskFilterRadioButton"
|
||||
android:layout_marginStart="8dp" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sort_by_title"
|
||||
style="@style/Caption3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sort_by"
|
||||
android:textAllCaps="true"/>
|
||||
<RadioGroup
|
||||
android:id="@+id/sort_by_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
<RadioButton
|
||||
android:id="@+id/newest_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/newest"
|
||||
style="@style/TaskFilterRadioButton"
|
||||
android:checked="true"
|
||||
/>
|
||||
<RadioButton
|
||||
android:id="@+id/oldest_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/oldest"
|
||||
style="@style/TaskFilterRadioButton"
|
||||
android:layout_marginStart="8dp" />
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/month_released_title"
|
||||
style="@style/Caption3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sort_by"
|
||||
android:textAllCaps="true"/>
|
||||
<LinearLayout
|
||||
android:id="@+id/month_released_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="24dp">
|
||||
<CheckBox
|
||||
android:id="@+id/january_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/january"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/febuary_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/febuary"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/march_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/march"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/april_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/april"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/may_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/may"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/june_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/june"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/july_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/july"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/august_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/august"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/september_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/september"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/october_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/october"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/november_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/november"
|
||||
android:checked="true"
|
||||
/>
|
||||
<CheckBox
|
||||
android:id="@+id/december_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/december"
|
||||
android:checked="true"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
9
Habitica/res/menu/menu_list_customizations.xml
Normal file
9
Habitica/res/menu/menu_list_customizations.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="com.habitrpg.android.habitica.ui.activities.MainActivity">
|
||||
<item android:id="@+id/action_filter"
|
||||
android:icon="@drawable/ic_action_filter_list"
|
||||
android:title="@string/filter"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
|
@ -1241,4 +1241,22 @@
|
|||
<string name="armoire_rate_experience_title">20% Experience points</string>
|
||||
<string name="armoire_rate_experience_description">The amount gained varies randomly from 10 to 50</string>
|
||||
<string name="day_start_adjustment">Day Start Adjustment</string>
|
||||
<string name="purchased">Purchased</string>
|
||||
<string name="show_me">Show Me</string>
|
||||
<string name="background_filters">Background Filters</string>
|
||||
<string name="newest">Newest</string>
|
||||
<string name="oldest">Oldest</string>
|
||||
<string name="sort_by">Sort By</string>
|
||||
<string name="january">January</string>
|
||||
<string name="febuary">Febuary</string>
|
||||
<string name="march">March</string>
|
||||
<string name="april">April</string>
|
||||
<string name="may">May</string>
|
||||
<string name="june">June</string>
|
||||
<string name="july">July</string>
|
||||
<string name="august">August</string>
|
||||
<string name="september">September</string>
|
||||
<string name="october">October</string>
|
||||
<string name="november">November</string>
|
||||
<string name="december">December</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.habitrpg.android.habitica.models
|
||||
|
||||
data class CustomizationFilter(
|
||||
var onlyPurchased: Boolean = false,
|
||||
var ascending: Boolean = false,
|
||||
var months: MutableList<String> = mutableListOf()
|
||||
) {
|
||||
val isFiltering: Boolean
|
||||
get() {
|
||||
return onlyPurchased || months.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,22 +2,35 @@ package com.habitrpg.android.habitica.ui.fragments.inventory.customization
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckBox
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.CustomizationRepository
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.databinding.BottomSheetBackgroundsFilterBinding
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.CustomizationFilter
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.user.OwnedCustomization
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.CustomizationRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaBottomSheetDialog
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
import io.reactivex.rxjava3.kotlin.combineLatest
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
import javax.inject.Inject
|
||||
|
||||
class AvatarCustomizationFragment :
|
||||
|
|
@ -44,6 +57,9 @@ class AvatarCustomizationFragment :
|
|||
internal var adapter: CustomizationRecyclerViewAdapter = CustomizationRecyclerViewAdapter()
|
||||
internal var layoutManager: GridLayoutManager = GridLayoutManager(activity, 2)
|
||||
|
||||
private val currentFilter = BehaviorSubject.create<CustomizationFilter>()
|
||||
private val ownedCustomizations = PublishSubject.create<List<OwnedCustomization>>()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
|
|
@ -115,6 +131,7 @@ class AvatarCustomizationFragment :
|
|||
this.loadCustomizations()
|
||||
|
||||
userViewModel.user.observe(viewLifecycleOwner) { updateUser(it) }
|
||||
currentFilter.onNext(CustomizationFilter())
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
@ -122,6 +139,22 @@ class AvatarCustomizationFragment :
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_list_customizations, menu)
|
||||
}
|
||||
|
||||
@Suppress("ReturnCount")
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_filter -> {
|
||||
showFilterDialog()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun injectFragment(component: UserComponent) {
|
||||
component.inject(this)
|
||||
}
|
||||
|
|
@ -129,10 +162,44 @@ class AvatarCustomizationFragment :
|
|||
private fun loadCustomizations() {
|
||||
val type = this.type ?: return
|
||||
compositeSubscription.add(
|
||||
customizationRepository.getCustomizations(type, category, false).subscribe(
|
||||
{
|
||||
adapter.setCustomizations(if (type == "background") { it.reversed() } else { it })
|
||||
},
|
||||
customizationRepository.getCustomizations(type, category, false)
|
||||
.combineLatest(currentFilter.toFlowable(BackpressureStrategy.DROP),
|
||||
ownedCustomizations.toFlowable(BackpressureStrategy.DROP))
|
||||
.subscribe(
|
||||
{ (customizations, filter, ownedCustomizations) ->
|
||||
if (filter.isFiltering) {
|
||||
val displayedCustomizations = mutableListOf<Customization>()
|
||||
for (customization in customizations) {
|
||||
if (filter.onlyPurchased) {
|
||||
if (ownedCustomizations.find { it.key == customization.identifier } == null) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (filter.months.isNotEmpty()) {
|
||||
if (!filter.months.contains(customization.customizationSetName?.substringAfter('.'))) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
displayedCustomizations.add(customization)
|
||||
}
|
||||
adapter.setCustomizations(
|
||||
if (!filter.ascending) {
|
||||
displayedCustomizations.reversed()
|
||||
} else {
|
||||
displayedCustomizations
|
||||
}
|
||||
)
|
||||
} else {
|
||||
adapter.setCustomizations(
|
||||
if (!filter.ascending) {
|
||||
customizations.reversed()
|
||||
} else {
|
||||
customizations
|
||||
}
|
||||
)
|
||||
}
|
||||
adapter.ownedCustomizations = ownedCustomizations.map { it.key + "_" + it.type + "_" + it.category }
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
)
|
||||
|
|
@ -154,9 +221,7 @@ class AvatarCustomizationFragment :
|
|||
fun updateUser(user: User?) {
|
||||
if (user == null) return
|
||||
this.updateActiveCustomization(user)
|
||||
val ownedCustomizations = ArrayList<String>()
|
||||
user.purchased?.customizations?.filter { it.type == this.type && it.purchased }?.mapTo(ownedCustomizations) { it.key + "_" + it.type + "_" + it.category }
|
||||
adapter.updateOwnership(ownedCustomizations)
|
||||
ownedCustomizations.onNext(user.purchased?.customizations?.filter { it.type == this.type && it.purchased })
|
||||
this.adapter.userSize = user.preferences?.size
|
||||
this.adapter.hairColor = user.preferences?.hair?.color
|
||||
this.adapter.gemBalance = user.gemCount
|
||||
|
|
@ -192,7 +257,7 @@ class AvatarCustomizationFragment :
|
|||
|
||||
override fun onRefresh() {
|
||||
compositeSubscription.add(
|
||||
userRepository.retrieveUser(false, true).subscribe(
|
||||
userRepository.retrieveUser(withTasks = false, forced = true).subscribe(
|
||||
{
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
},
|
||||
|
|
@ -200,4 +265,59 @@ class AvatarCustomizationFragment :
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun showFilterDialog() {
|
||||
val filter = currentFilter.value ?: CustomizationFilter()
|
||||
val context = context ?: return
|
||||
val dialog = HabiticaBottomSheetDialog(context)
|
||||
val binding = BottomSheetBackgroundsFilterBinding.inflate(layoutInflater)
|
||||
binding.showMeWrapper.check(if (filter.onlyPurchased) R.id.show_purchased_button else R.id.show_all_button)
|
||||
binding.showMeWrapper.setOnCheckedChangeListener { _, checkedId ->
|
||||
filter.onlyPurchased = checkedId == R.id.show_purchased_button
|
||||
currentFilter.onNext(filter)
|
||||
}
|
||||
binding.clearButton.setOnClickListener {
|
||||
currentFilter.onNext(CustomizationFilter())
|
||||
dialog.dismiss()
|
||||
}
|
||||
if (type == "background") {
|
||||
binding.sortByWrapper.check(if (filter.ascending) R.id.oldest_button else R.id.newest_button)
|
||||
binding.sortByWrapper.setOnCheckedChangeListener { _, checkedId ->
|
||||
filter.ascending = checkedId == R.id.oldest_button
|
||||
currentFilter.onNext(filter)
|
||||
}
|
||||
configureMonthFilterButton(binding.januaryButton, 1, filter)
|
||||
configureMonthFilterButton(binding.febuaryButton, 2, filter)
|
||||
configureMonthFilterButton(binding.marchButton, 3, filter)
|
||||
configureMonthFilterButton(binding.aprilButton, 4, filter)
|
||||
configureMonthFilterButton(binding.mayButton, 5, filter)
|
||||
configureMonthFilterButton(binding.juneButton, 6, filter)
|
||||
configureMonthFilterButton(binding.julyButton, 7, filter)
|
||||
configureMonthFilterButton(binding.augustButton, 8, filter)
|
||||
configureMonthFilterButton(binding.septemberButton, 9, filter)
|
||||
configureMonthFilterButton(binding.octoberButton, 10, filter)
|
||||
configureMonthFilterButton(binding.novemberButton, 11, filter)
|
||||
configureMonthFilterButton(binding.decemberButton, 12, filter)
|
||||
} else {
|
||||
binding.sortByTitle.visibility = View.GONE
|
||||
binding.sortByWrapper.visibility = View.GONE
|
||||
binding.monthReleasedTitle.visibility = View.GONE
|
||||
binding.monthReleasedWrapper.visibility = View.GONE
|
||||
}
|
||||
dialog.setContentView(binding.root)
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun configureMonthFilterButton(button: CheckBox, value: Int, filter: CustomizationFilter) {
|
||||
val identifier = value.toString().padStart(2, '0')
|
||||
button.isChecked = filter.months.contains(identifier)
|
||||
button.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (!isChecked && filter.months.contains(identifier)) {
|
||||
filter.months.remove(identifier)
|
||||
} else if (isChecked && !filter.months.contains(identifier)) {
|
||||
filter.months.add(identifier)
|
||||
}
|
||||
currentFilter.onNext(filter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue