Fix stable display

This commit is contained in:
Phillip Thelen 2022-08-31 11:33:56 +02:00
parent 327f76db72
commit ee9081b679
17 changed files with 109 additions and 83 deletions

View file

@ -95,17 +95,17 @@ open class HabiticaTestCase : TestCase() {
content = loadJsonFile("content", ContentResult::class.java)
every { inventoryRepository.getPets() } returns Flowable.just(content.pets)
every { inventoryRepository.getMounts() } returns Flowable.just(content.mounts)
every { inventoryRepository.getItems(Food::class.java) } returns Flowable.just(content.food)
every { inventoryRepository.getItems(Egg::class.java) } returns Flowable.just(content.eggs)
every { inventoryRepository.getItems(HatchingPotion::class.java) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItems(QuestContent::class.java) } returns Flowable.just(content.quests)
every { inventoryRepository.getItemsFlowable(Food::class.java) } returns Flowable.just(content.food)
every { inventoryRepository.getItemsFlowable(Egg::class.java) } returns Flowable.just(content.eggs)
every { inventoryRepository.getItemsFlowable(HatchingPotion::class.java) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItemsFlowable(QuestContent::class.java) } returns Flowable.just(content.quests)
every { inventoryRepository.getItems(Food::class.java, any()) } returns Flowable.just(content.food)
every { inventoryRepository.getItems(Egg::class.java, any()) } answers {
every { inventoryRepository.getItemsFlowable(Food::class.java, any()) } returns Flowable.just(content.food)
every { inventoryRepository.getItemsFlowable(Egg::class.java, any()) } answers {
Flowable.just(content.eggs)
}
every { inventoryRepository.getItems(HatchingPotion::class.java, any()) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItems(QuestContent::class.java, any()) } returns Flowable.just(content.quests)
every { inventoryRepository.getItemsFlowable(HatchingPotion::class.java, any()) } returns Flowable.just(content.hatchingPotions)
every { inventoryRepository.getItemsFlowable(QuestContent::class.java, any()) } returns Flowable.just(content.quests)
}
internal fun <T> loadJsonFile(s: String, type: Type): T {

View file

@ -115,7 +115,7 @@ internal class ItemRecyclerFragmentTest : FragmentTestCase<ItemRecyclerFragment,
items = (items + items).sortedBy { it.key }
Flowable.just(items)
}
every { inventoryRepository.getItems(Food::class.java, any()) } answers {
every { inventoryRepository.getItemsFlowable(Food::class.java, any()) } answers {
Flowable.just((content.eggs + content.eggs).sortedBy { it.key })
}
fragment.itemType = "food"

View file

@ -86,8 +86,9 @@ interface InventoryRepository : BaseRepository {
fun purchaseItem(purchaseType: String, key: String, purchaseQuantity: Int): Flowable<Void>
fun togglePinnedItem(item: ShopItem): Flowable<List<ShopItem>>
fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>>
fun getItems(itemClass: Class<out Item>): Flowable<out List<Item>>
fun getItemsFlowable(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>>
fun getItemsFlowable(itemClass: Class<out Item>): Flowable<out List<Item>>
fun getItems(itemClass: Class<out Item>): Flow<List<Item>>
fun getLatestMysteryItem(): Flowable<Equipment>
fun getItem(type: String, key: String): Flowable<Item>
fun getAvailableLimitedItems(): Flowable<List<Item>>

View file

@ -75,11 +75,15 @@ class InventoryRepositoryImpl(
return localRepository.getOwnedItems(userID, includeZero)
}
override fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>> {
return localRepository.getItems(itemClass, keys)
override fun getItemsFlowable(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>> {
return localRepository.getItemsFlowable(itemClass, keys)
}
override fun getItems(itemClass: Class<out Item>): Flowable<out List<Item>> {
override fun getItemsFlowable(itemClass: Class<out Item>): Flowable<out List<Item>> {
return localRepository.getItemsFlowable(itemClass)
}
override fun getItems(itemClass: Class<out Item>): Flow<List<Item>> {
return localRepository.getItems(itemClass)
}

View file

@ -35,8 +35,8 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getOwnedEquipment(type: String): Flowable<out List<Equipment>>
fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>>
fun getItems(itemClass: Class<out Item>): Flowable<out List<Item>>
fun getItemsFlowable(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>>
fun getItemsFlowable(itemClass: Class<out Item>): Flowable<out List<Item>>
fun getOwnedItems(itemType: String, userID: String, includeZero: Boolean): Flow<List<OwnedItem>>
fun getOwnedItems(userID: String, includeZero: Boolean): Flowable<Map<String, OwnedItem>>
fun getEquipmentType(type: String, set: String): Flowable<out List<Equipment>>
@ -66,4 +66,5 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun save(items: Items, userID: String)
fun getLiveObject(obj: OwnedItem): OwnedItem?
fun getItems(itemClass: Class<out Item>): Flow<List<Item>>
}

View file

@ -120,18 +120,23 @@ class RealmInventoryLocalRepository(realm: Realm) : RealmContentLocalRepository(
}
}
override fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>> {
override fun getItemsFlowable(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>> {
return realm.where(itemClass).`in`("key", keys).findAll().toFlow()
.filter { it.isLoaded }
}
override fun getItems(itemClass: Class<out Item>): Flowable<out List<Item>> {
override fun getItemsFlowable(itemClass: Class<out Item>): Flowable<out List<Item>> {
return RxJavaBridge.toV3Flowable(
realm.where(itemClass).findAll().asFlowable()
.filter { it.isLoaded }
)
}
override fun getItems(itemClass: Class<out Item>): Flow<List<Item>> {
return realm.where(itemClass).findAll().toFlow()
.filter { it.isLoaded }
}
override fun getOwnedItems(userID: String, includeZero: Boolean): Flowable<Map<String, OwnedItem>> {
return queryUserFlowable(userID).map {
val items = HashMap<String, OwnedItem>()

View file

@ -13,9 +13,9 @@ import com.habitrpg.android.habitica.models.tasks.RemindersItem
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.receivers.NotificationPublisher
import com.habitrpg.android.habitica.receivers.TaskReceiver
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.shared.habitica.HLogger
import com.habitrpg.shared.habitica.LogLevel
import com.habitrpg.shared.habitica.models.tasks.TaskType
import kotlinx.coroutines.flow.firstOrNull
import java.time.ZoneId
import java.time.ZonedDateTime
@ -77,12 +77,12 @@ class TaskAlarmManager(
private fun setTimeForDailyReminder(remindersItem: RemindersItem?, task: Task): RemindersItem? {
val newTime = (remindersItem?.let { task.getNextReminderOccurrence(it) } ?: return null)
remindersItem.time = newTime.withZoneSameLocal(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
remindersItem.time = newTime.withZoneSameLocal(ZoneId.systemDefault())
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
return remindersItem
}
/**
* If reminderItem time is before now, a new reminder will not be created until the reminder passes.
* The exception to this is if the task & reminder was newly created for the same time,
@ -91,11 +91,15 @@ class TaskAlarmManager(
*/
private fun setAlarmForRemindersItem(reminderItemTask: Task, remindersItem: RemindersItem?) {
val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant()
if (remindersItem == null || (remindersItem.getLocalZonedDateTimeInstant()?.isBefore(now) == true && reminderItemTask.nextDue?.firstOrNull() != null)) {
val zonedTime = remindersItem?.getLocalZonedDateTimeInstant()
if (remindersItem == null
|| (reminderItemTask.type == TaskType.DAILY && zonedTime?.isBefore(now) == true && reminderItemTask.nextDue?.firstOrNull() != null)
|| (reminderItemTask.type == TaskType.TODO && zonedTime?.isBefore(now) == true)
) {
return
}
val time = Date.from(remindersItem.getLocalZonedDateTimeInstant())
val time = Date.from(zonedTime)
val cal = Calendar.getInstance()
cal.time = time

View file

@ -20,10 +20,10 @@ import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.OwnedMount
import com.habitrpg.android.habitica.ui.fragments.inventory.stable.StableFragmentDirections
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.android.habitica.ui.viewHolders.MountViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.views.PixelArtView
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
@ -204,8 +204,8 @@ class StableRecyclerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
this.animal = item
val context = itemView.context
val egg = eggs[item.animal]
if (egg != null) {
titleView.text = if (item.type == "drop" || itemType == "mounts") egg.mountText else egg.text
titleView.text = if (egg != null) {
if (item.type == "drop" || itemType == "mounts") egg.mountText else egg.text
} else item.animal
ownedTextView.visibility = View.VISIBLE

View file

@ -296,7 +296,7 @@ class ItemDialogFragment : BaseDialogFragment<FragmentItemsDialogBinding>() {
adapter?.data = filteredItems
}
.map { items -> items.mapNotNull { it.key } }
.map { inventoryRepository.getItems(itemClass, it.toTypedArray()).firstOrNull() }
.map { inventoryRepository.getItemsFlowable(itemClass, it.toTypedArray()).firstOrNull() }
.collect {
val itemMap = mutableMapOf<String, Item>()
for (item in it ?: emptyList()) {

View file

@ -289,7 +289,7 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
}
.map { items -> items.mapNotNull { it.key } }
.map {
inventoryRepository.getItems(itemClass, it.toTypedArray()).firstOrNull()
inventoryRepository.getItemsFlowable(itemClass, it.toTypedArray()).firstOrNull()
}
.collect {
val itemMap = mutableMapOf<String, Item>()

View file

@ -112,10 +112,10 @@ class PetDetailRecyclerFragment :
binding?.recyclerView?.layoutManager = layoutManager
adapter.animalIngredientsRetriever = { animal, callback ->
lifecycleScope.launch {
val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(animal.animal))
val egg = inventoryRepository.getItemsFlowable(Egg::class.java, arrayOf(animal.animal))
.firstOrNull()?.firstOrNull() as? Egg
val potion =
inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(animal.color))
inventoryRepository.getItemsFlowable(HatchingPotion::class.java, arrayOf(animal.color))
.firstOrNull()?.firstOrNull() as? HatchingPotion
callback(Pair(egg, potion))
}

View file

@ -19,12 +19,12 @@ import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
import com.habitrpg.common.habitica.helpers.EmptyItem
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.viewmodels.StableViewModel
import com.habitrpg.android.habitica.ui.viewmodels.StableViewModelFactory
import com.habitrpg.common.habitica.helpers.EmptyItem
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -107,8 +107,8 @@ class StableRecyclerFragment :
adapter = StableRecyclerAdapter()
adapter?.animalIngredientsRetriever = { animal, callback ->
lifecycleScope.launch {
val egg = inventoryRepository.getItems(Egg::class.java, arrayOf(animal.animal)).firstOrNull()?.firstOrNull() as? Egg
val potion = inventoryRepository.getItems(HatchingPotion::class.java, arrayOf(animal.color)).firstOrNull()?.firstOrNull() as? HatchingPotion
val egg = inventoryRepository.getItemsFlowable(Egg::class.java, arrayOf(animal.animal)).firstOrNull()?.firstOrNull() as? Egg
val potion = inventoryRepository.getItemsFlowable(HatchingPotion::class.java, arrayOf(animal.color)).firstOrNull()?.firstOrNull() as? HatchingPotion
callback(Pair(egg, potion))
}
}

View file

@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.asFlow
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
@ -52,6 +53,8 @@ import com.habitrpg.shared.habitica.models.tasks.TaskType
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.util.Date
import java.util.concurrent.TimeUnit
@ -149,6 +152,14 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
recyclerAdapter?.canScoreTasks = canScoreTaks
updateTaskSubscription(it)
}
lifecycleScope.launch {
viewModel.userViewModel.user.asFlow()
.map { it?.preferences?.tasks?.mirrorGroupTasks }
.distinctUntilChanged()
.collect {
updateTaskSubscription(viewModel.ownerID.value)
}
}
}
private fun handleTaskResult(result: TaskScoringResult, value: Int) {
@ -237,7 +248,9 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
return if (recyclerAdapter?.getItemViewType(viewHolder.bindingAdapterPosition) ?: 0 != 0) {
return if ((recyclerAdapter?.getItemViewType(viewHolder.bindingAdapterPosition)
?: 0) != 0
) {
makeFlag(ItemTouchHelper.ACTION_STATE_IDLE, 0)
} else {
makeFlag(

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@ -36,8 +37,15 @@ class StableViewModel(private val application: Application?, private val itemTyp
private val _items: MutableLiveData<List<Any>> = MutableLiveData()
val items: LiveData<List<Any>> = _items
private val _eggs: MutableLiveData<Map<String, Egg>> = MutableLiveData()
val eggs: LiveData<Map<String, Egg>> = _eggs
val eggs: LiveData<Map<String, Egg>> = inventoryRepository.getItems(Egg::class.java)
.map {
val eggMap = mutableMapOf<String, Egg>()
it.forEach { egg ->
eggMap[egg.key] = egg as Egg
}
eggMap
}
.asLiveData()
private val _ownedItems: MutableLiveData<Map<String, OwnedItem>> = MutableLiveData()
val ownedItems: LiveData<Map<String, OwnedItem>> = _ownedItems
private val _mounts: MutableLiveData<List<Mount>> = MutableLiveData()
@ -71,22 +79,9 @@ class StableViewModel(private val application: Application?, private val itemTyp
_items.value = mapAnimals(animals, it)
}
disposable.add(
inventoryRepository.getItems(Egg::class.java)
.map {
val eggMap = mutableMapOf<String, Egg>()
it.forEach { egg ->
eggMap[egg.key] = egg as Egg
}
eggMap
}
.subscribe(
{
_eggs.value = it
},
RxErrorHandler.handleEmptyError()
)
)
viewModelScope.launch {
}
disposable.add(inventoryRepository.getOwnedItems(true).subscribe({ _ownedItems.value = it }, RxErrorHandler.handleEmptyError()))
_mounts.value = if ("pets" == itemType) {
inventoryRepository.getMounts().firstOrNull() ?: emptyList()

View file

@ -5,16 +5,16 @@ import com.habitrpg.shared.habitica.HParcelize
import com.habitrpg.shared.habitica.models.AvatarStats
@HParcelize
data class TaskScoringResult(var hasDied: Boolean,
var drop: TaskDirectionDataDrop?,
var experienceDelta: Double,
var healthDelta: Double,
var goldDelta: Double,
var manaDelta: Double,
var hasLeveledUp: Boolean,
var level: Int,
var questDamage: Double?,
var questItemsFound: Int?): HParcelable {
data class TaskScoringResult(var hasDied: Boolean = false,
var drop: TaskDirectionDataDrop? = null,
var experienceDelta: Double = 0.0,
var healthDelta: Double = 0.0,
var goldDelta: Double = 0.0,
var manaDelta: Double = 0.0,
var hasLeveledUp: Boolean = false,
var level: Int = 0,
var questDamage: Double? = null,
var questItemsFound: Int? = null): HParcelable {
constructor(data: TaskDirectionData, stats: AvatarStats?) : this(
data.hp <= 0.0,

View file

@ -1,2 +1,2 @@
NAME=4.0.1
CODE=4460
CODE=4470

View file

@ -33,7 +33,7 @@ open class Task constructor(): Parcelable, BaseTask {
get() = Attribute.from(attributeValue)
set(value) { attributeValue = value?.value }
var attributeValue: String? = Attribute.STRENGTH.value
var value: Double = 0.0
var value: Double? = 0.0
var dateCreated: Date? = null
var position: Int = 0
// Habits
@ -86,39 +86,42 @@ open class Task constructor(): Parcelable, BaseTask {
val extraLightTaskColor: Int
get() {
val value = value ?: 0.0
return when {
this.value < -20 -> return R.color.watch_maroon_200
this.value < -10 -> return R.color.watch_red_200
this.value < -1 -> return R.color.watch_orange_200
this.value < 1 -> return R.color.watch_yellow_200
this.value < 5 -> return R.color.watch_green_200
this.value < 10 -> return R.color.watch_teal_200
value < -20 -> return R.color.watch_maroon_200
value < -10 -> return R.color.watch_red_200
value < -1 -> return R.color.watch_orange_200
value < 1 -> return R.color.watch_yellow_200
value < 5 -> return R.color.watch_green_200
value < 10 -> return R.color.watch_teal_200
else -> R.color.watch_blue_200
}
}
val lightTaskColor: Int
get() {
val value = value ?: 0.0
return when {
this.value < -20 -> return R.color.watch_maroon_100
this.value < -10 -> return R.color.watch_red_100
this.value < -1 -> return R.color.watch_orange_100
this.value < 1 -> return R.color.watch_yellow_100
this.value < 5 -> return R.color.watch_green_100
this.value < 10 -> return R.color.watch_teal_100
value < -20 -> return R.color.watch_maroon_100
value < -10 -> return R.color.watch_red_100
value < -1 -> return R.color.watch_orange_100
value < 1 -> return R.color.watch_yellow_100
value < 5 -> return R.color.watch_green_100
value < 10 -> return R.color.watch_teal_100
else -> R.color.watch_blue_100
}
}
val mediumTaskColor: Int
get() {
val value = value ?: 0.0
return when {
this.value < -20 -> return R.color.watch_maroon_10
this.value < -10 -> return R.color.watch_red_10
this.value < -1 -> return R.color.watch_orange_10
this.value < 1 -> return R.color.watch_yellow_10
this.value < 5 -> return R.color.watch_green_10
this.value < 10 -> return R.color.watch_teal_10
value < -20 -> return R.color.watch_maroon_10
value < -10 -> return R.color.watch_red_10
value < -1 -> return R.color.watch_orange_10
value < 1 -> return R.color.watch_yellow_10
value < 5 -> return R.color.watch_green_10
value < 10 -> return R.color.watch_teal_10
else -> R.color.watch_blue_10
}
}
@ -190,7 +193,7 @@ open class Task constructor(): Parcelable, BaseTask {
dest.writeString(this.notes)
dest.writeString(this.attribute?.value)
dest.writeString(this.type?.value)
dest.writeDouble(this.value)
this.value?.let { dest.writeDouble(it) }
dest.writeLong(this.dateCreated?.time ?: -1)
dest.writeInt(this.position)
dest.writeValue(this.up)