diff --git a/Habitica/build.gradle b/Habitica/build.gradle
index 8ddd9ea96..c17635d69 100644
--- a/Habitica/build.gradle
+++ b/Habitica/build.gradle
@@ -146,7 +146,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 2991
+ versionCode 2994
versionName "3.3"
}
diff --git a/Habitica/res/drawable-hdpi/baseline_error_outline_black_36dp.png b/Habitica/res/drawable-hdpi/baseline_error_outline_black_36dp.png
new file mode 100644
index 000000000..ec4860d02
Binary files /dev/null and b/Habitica/res/drawable-hdpi/baseline_error_outline_black_36dp.png differ
diff --git a/Habitica/res/drawable-mdpi/baseline_error_outline_black_36dp.png b/Habitica/res/drawable-mdpi/baseline_error_outline_black_36dp.png
new file mode 100644
index 000000000..8994911fa
Binary files /dev/null and b/Habitica/res/drawable-mdpi/baseline_error_outline_black_36dp.png differ
diff --git a/Habitica/res/drawable-xhdpi/baseline_error_outline_black_36dp.png b/Habitica/res/drawable-xhdpi/baseline_error_outline_black_36dp.png
new file mode 100644
index 000000000..356880514
Binary files /dev/null and b/Habitica/res/drawable-xhdpi/baseline_error_outline_black_36dp.png differ
diff --git a/Habitica/res/drawable-xxhdpi/baseline_error_outline_black_36dp.png b/Habitica/res/drawable-xxhdpi/baseline_error_outline_black_36dp.png
new file mode 100644
index 000000000..a65cafc82
Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/baseline_error_outline_black_36dp.png differ
diff --git a/Habitica/res/drawable-xxxhdpi/baseline_error_outline_black_36dp.png b/Habitica/res/drawable-xxxhdpi/baseline_error_outline_black_36dp.png
new file mode 100644
index 000000000..0a0d146a4
Binary files /dev/null and b/Habitica/res/drawable-xxxhdpi/baseline_error_outline_black_36dp.png differ
diff --git a/Habitica/res/layout/empty_item.xml b/Habitica/res/layout/empty_item.xml
new file mode 100644
index 000000000..313983fb2
--- /dev/null
+++ b/Habitica/res/layout/empty_item.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/failed_item.xml b/Habitica/res/layout/failed_item.xml
new file mode 100644
index 000000000..9944a2a90
--- /dev/null
+++ b/Habitica/res/layout/failed_item.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/fragment_challengeslist.xml b/Habitica/res/layout/fragment_challengeslist.xml
deleted file mode 100644
index 4f0cc63cb..000000000
--- a/Habitica/res/layout/fragment_challengeslist.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Habitica/res/layout/fragment_items.xml b/Habitica/res/layout/fragment_items.xml
index 34a0fe2e9..6bdd962ee 100644
--- a/Habitica/res/layout/fragment_items.xml
+++ b/Habitica/res/layout/fragment_items.xml
@@ -41,23 +41,4 @@
android:text="@string/open_market"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"/>
-
-
-
-
-
diff --git a/Habitica/res/layout/fragment_recyclerview.xml b/Habitica/res/layout/fragment_recyclerview.xml
index c46cfa83b..4ebfab202 100644
--- a/Habitica/res/layout/fragment_recyclerview.xml
+++ b/Habitica/res/layout/fragment_recyclerview.xml
@@ -1,23 +1,10 @@
-
-
-
-
-
-
-
+ android:layout_height="match_parent"
+ android:scrollbarSize="3dp"
+ android:scrollbarThumbVertical="@color/scrollbarThumb"
+ android:scrollbars="vertical"
+ android:clipToPadding="false"/>
diff --git a/Habitica/res/layout/fragment_recyclerview_stable.xml b/Habitica/res/layout/fragment_recyclerview_stable.xml
deleted file mode 100644
index c46cfa83b..000000000
--- a/Habitica/res/layout/fragment_recyclerview_stable.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/Habitica/res/layout/fragment_refresh_recyclerview.xml b/Habitica/res/layout/fragment_refresh_recyclerview.xml
index 4e9ddf75e..b97735dc9 100644
--- a/Habitica/res/layout/fragment_refresh_recyclerview.xml
+++ b/Habitica/res/layout/fragment_refresh_recyclerview.xml
@@ -1,6 +1,5 @@
-
-
-
-
-
-
-
-
-
diff --git a/Habitica/res/layout/loading_item.xml b/Habitica/res/layout/loading_item.xml
new file mode 100644
index 000000000..87140c59c
--- /dev/null
+++ b/Habitica/res/layout/loading_item.xml
@@ -0,0 +1,11 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml
index fed84e82a..b02d6c5db 100644
--- a/Habitica/res/values/strings.xml
+++ b/Habitica/res/values/strings.xml
@@ -1166,6 +1166,10 @@
You must purchase the previous items in this sequence to unlock
You caused damage to the boss
Style
+ Failed to load
Daily Checkbox
+ Retry
+ Nothing here yet
+ You don\'t have any equipment of this type yet. You can purchase equipment from the market using the gold you earned.
Todo Checkbox
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
index cb366c2bb..8123bd95e 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmInventoryLocalRepository.kt
@@ -87,6 +87,7 @@ class RealmInventoryLocalRepository(realm: Realm) : RealmContentLocalRepository(
"hatchingPotions" -> it.items?.hatchingPotions
"food" -> it.items?.food
"quests" -> it.items?.quests
+ "special" -> it.items?.special?.ownedItems
else -> emptyList()
} ?: emptyList()
if (includeZero) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
index e6d45237a..531671e3c 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt
@@ -30,13 +30,17 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter 0) {
+ notifyItemChanged(0)
+ }
}
var context: Context? = null
var user: User? = null
set(value) {
field = value
- this.notifyDataSetChanged()
+ if (items.size > 0) {
+ this.notifyDataSetChanged()
+ }
}
private var pinnedItemKeys: List = ArrayList()
@@ -49,7 +53,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter 0) {
notifyDataSetChanged()
}
}
@@ -200,12 +204,16 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter) {
this.ownedItems = ownedItems
- this.notifyDataSetChanged()
+ if (items.size > 0) {
+ this.notifyDataSetChanged()
+ }
}
fun setPinnedItemKeys(pinnedItemKeys: List) {
this.pinnedItemKeys = pinnedItemKeys
- this.notifyDataSetChanged()
+ if (items.size > 0) {
+ this.notifyDataSetChanged()
+ }
}
internal class ShopHeaderViewHolder(parent: ViewGroup) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.shop_header)) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
index 1ba31a2c3..6148c7e26 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/StableRecyclerAdapter.kt
@@ -135,6 +135,7 @@ class StableRecyclerAdapter : RecyclerView.Adapter() {
}
override fun getItemViewType(position: Int): Int {
+ if (itemList.size <= position) { return 0 }
val item = itemList[position]
return if (item == "header") {
0
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
index 7b350c6b9..b9b8cf26b 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt
@@ -21,6 +21,9 @@ import io.reactivex.rxjava3.subjects.PublishSubject
class RewardsRecyclerViewAdapter(private var customRewards: List?, private val layoutResource: Int) : BaseRecyclerViewAdapter(), TaskRecyclerViewAdapter {
var user: User? = null
set(value) {
+ if (field?.versionNumber == value?.versionNumber) {
+ return
+ }
field = value
notifyDataSetChanged()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
index 141d147e4..6a018e1b5 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt
@@ -7,12 +7,15 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
+import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.ui.adapter.inventory.EquipmentRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import com.habitrpg.android.habitica.ui.helpers.EmptyItem
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import javax.inject.Inject
@@ -29,6 +32,7 @@ class EquipmentDetailFragment : BaseMainFragment(), SwipeRefr
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding?.recyclerView?.setEmptyView(binding?.emptyView)
binding?.refreshLayout?.setOnRefreshListener(this)
- binding?.emptyTextView?.text = getString(R.string.empty_items, itemTypeText)
+ binding?.recyclerView?.emptyItem = EmptyItem(
+ getString(R.string.empty_items, itemTypeText),
+ getString(R.string.open_market)
+ ) {
+ openMarket()
+ }
val context = activity
@@ -176,7 +180,7 @@ class ItemDialogFragment : BaseDialogFragment(), SwipeRefr
openMarket()
}
- binding?.openEmptyMarketButton?.setOnClickListener { openMarket() }
+ //binding?.openEmptyMarketButton?.setOnClickListener { openMarket() }
this.loadItems()
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
index 5850746b6..6cf61ecb9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemRecyclerFragment.kt
@@ -1,11 +1,9 @@
package com.habitrpg.android.habitica.ui.fragments.inventory.items
-import android.graphics.drawable.BitmapDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.view.Window
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@@ -23,10 +21,9 @@ import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
-import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
+import com.habitrpg.android.habitica.ui.helpers.EmptyItem
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.helpers.loadImage
-import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import com.habitrpg.android.habitica.ui.views.dialogs.OpenedMysteryitemDialog
import javax.inject.Inject
@@ -62,9 +59,15 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding?.recyclerView?.setEmptyView(binding?.emptyView)
binding?.refreshLayout?.setOnRefreshListener(this)
- binding?.emptyTextView?.text = getString(R.string.empty_items, itemTypeText)
+ binding?.recyclerView?.emptyItem = EmptyItem(
+ getString(R.string.empty_items, itemTypeText ?: itemType),
+ null,
+ null,
+ getString(R.string.open_market)
+ ) {
+ openMarket()
+ }
val context = activity
@@ -129,8 +132,6 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL
openMarket()
}
- binding?.openEmptyMarketButton?.setOnClickListener { openMarket() }
-
this.loadItems()
}
@@ -176,20 +177,21 @@ class ItemRecyclerFragment : BaseFragment(), SwipeRefreshL
}
itemType?.let { type ->
compositeSubscription.add(inventoryRepository.getOwnedItems(type)
- .doOnNext { items ->
- adapter?.data = items
+ .map { it.distinctBy { it.key } }
+ .doOnNext { items ->
+ adapter?.data = items
+ }
+ .map { items -> items.mapNotNull { it.key } }
+ .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray()) }
+ .map {
+ val itemMap = mutableMapOf()
+ for (item in it) {
+ itemMap[item.key] = item
}
- .map { items -> items.mapNotNull { it.key } }
- .flatMap { inventoryRepository.getItems(itemClass, it.toTypedArray()) }
- .map {
- val itemMap = mutableMapOf()
- for (item in it) {
- itemMap[item.key] = item
- }
- itemMap
- }
- .subscribe({ items ->
- adapter?.items = items
+ itemMap
+ }
+ .subscribe({ items ->
+ adapter?.items = items
}, RxErrorHandler.handleEmptyError()))
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
index e4fbd27d9..2f6e00f91 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopFragment.kt
@@ -12,6 +12,7 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
+import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
import com.habitrpg.android.habitica.events.GearPurchasedEvent
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
@@ -22,12 +23,13 @@ import com.habitrpg.android.habitica.models.social.Group
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.adapter.inventory.ShopRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
+import com.habitrpg.android.habitica.ui.helpers.RecyclerViewState
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.views.CurrencyViews
import org.greenrobot.eventbus.Subscribe
import javax.inject.Inject
-open class ShopFragment : BaseMainFragment() {
+open class ShopFragment : BaseMainFragment() {
internal val currencyView: CurrencyViews by lazy {
val view = CurrencyViews(context)
@@ -48,10 +50,10 @@ open class ShopFragment : BaseMainFragment() {
private var gearCategories: MutableList? = null
- override var binding: FragmentRecyclerviewBinding? = null
+ override var binding: FragmentRefreshRecyclerviewBinding? = null
- override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRecyclerviewBinding {
- return FragmentRecyclerviewBinding.inflate(inflater, container, false)
+ override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
+ return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@@ -70,7 +72,12 @@ open class ShopFragment : BaseMainFragment() {
super.onViewCreated(view, savedInstanceState)
toolbarAccessoryContainer?.addView(currencyView)
binding?.recyclerView?.setBackgroundResource(R.color.content_background)
-
+ binding?.recyclerView?.onRefresh = {
+ loadShopInventory()
+ }
+ binding?.refreshLayout?.setOnRefreshListener {
+ loadShopInventory()
+ }
adapter = binding?.recyclerView?.adapter as? ShopRecyclerAdapter
if (adapter == null) {
adapter = ShopRecyclerAdapter()
@@ -178,7 +185,12 @@ open class ShopFragment : BaseMainFragment() {
.subscribe({
this.shop = it
this.adapter?.setShop(it)
- }, RxErrorHandler.handleEmptyError()))
+ }, {
+ binding?.recyclerView?.state = RecyclerViewState.FAILED
+ RxErrorHandler.reportError(it)
+ }, {
+ binding?.refreshLayout?.isRefreshing = false
+ }))
compositeSubscription.add(this.inventoryRepository.getOwnedItems()
.subscribe({ adapter?.setOwnedItems(it) }, RxErrorHandler.handleEmptyError()))
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
index e6e9ddfc4..2cece79fa 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableRecyclerFragment.kt
@@ -20,6 +20,7 @@ import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.*
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
+import com.habitrpg.android.habitica.ui.helpers.EmptyItem
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import io.reactivex.rxjava3.core.Flowable
@@ -70,8 +71,8 @@ class StableRecyclerFragment : BaseFragment(
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding?.recyclerView?.setEmptyView(binding?.emptyView)
- binding?.emptyViewTitle?.text = getString(R.string.empty_items, itemTypeText)
+ binding?.recyclerView?.emptyItem = EmptyItem(
+ getString(R.string.empty_items, itemTypeText))
binding?.refreshLayout?.setOnRefreshListener(this)
layoutManager = androidx.recyclerview.widget.GridLayoutManager(activity, 2)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeListFragment.kt
index ae8ea3a96..f4521f6bb 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeListFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeListFragment.kt
@@ -10,7 +10,7 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ChallengeRepository
import com.habitrpg.android.habitica.data.SocialRepository
import com.habitrpg.android.habitica.data.UserRepository
-import com.habitrpg.android.habitica.databinding.FragmentChallengeslistBinding
+import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.social.Challenge
@@ -26,7 +26,7 @@ import javax.inject.Inject
import javax.inject.Named
-class ChallengeListFragment : BaseFragment(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
+class ChallengeListFragment : BaseFragment(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
@Inject
lateinit var challengeRepository: ChallengeRepository
@@ -37,10 +37,10 @@ class ChallengeListFragment : BaseFragment(), and
@field:[Inject Named(AppModule.NAMED_USER_ID)]
lateinit var userId: String
- override var binding: FragmentChallengeslistBinding? = null
+ override var binding: FragmentRefreshRecyclerviewBinding? = null
- override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentChallengeslistBinding {
- return FragmentChallengeslistBinding.inflate(inflater, container, false)
+ override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
+ return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
}
private var challengeAdapter: ChallengesListViewAdapter? = null
@@ -84,7 +84,6 @@ class ChallengeListFragment : BaseFragment(), and
filterGroups?.addAll(it.second)
}, RxErrorHandler.handleEmptyError()))
- binding?.recyclerView?.setEmptyView(binding?.emptyView)
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
challengeAdapter?.updateUnfilteredData(challenges)
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
index ad046884e..f8d087c9f 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt
@@ -29,6 +29,7 @@ import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.adapter.tasks.*
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
+import com.habitrpg.android.habitica.ui.helpers.EmptyItem
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
@@ -288,51 +289,61 @@ open class TaskRecyclerViewFragment : BaseFragment 0) {
+ binding?.recyclerView?.emptyItem = if (taskFilterHelper.howMany(taskType) > 0) {
when (this.taskType) {
Task.TYPE_HABIT -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
- binding?.emptyViewTitle?.setText(R.string.empty_title_habits_filtered)
- binding?.emptyViewDescription?.setText(R.string.empty_description_habits_filtered)
+ EmptyItem(
+ getString(R.string.empty_title_habits_filtered),
+ getString(R.string.empty_description_habits_filtered),
+ R.drawable.icon_habits)
}
Task.TYPE_DAILY -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
- binding?.emptyViewTitle?.setText(R.string.empty_title_dailies_filtered)
- binding?.emptyViewDescription?.setText(R.string.empty_description_dailies_filtered)
+ EmptyItem(
+ getString(R.string.empty_title_dailies_filtered),
+ getString(R.string.empty_description_dailies_filtered),
+ R.drawable.icon_dailies)
}
Task.TYPE_TODO -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
- binding?.emptyViewTitle?.setText(R.string.empty_title_todos_filtered)
- binding?.emptyViewDescription?.setText(R.string.empty_description_todos_filtered)
+ EmptyItem(
+ getString(R.string.empty_title_todos_filtered),
+ getString(R.string.empty_description_todos_filtered),
+ R.drawable.icon_todos)
}
Task.TYPE_REWARD -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
- binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
+ EmptyItem(
+ getString(R.string.empty_title_rewards_filtered),
+ null,
+ R.drawable.icon_rewards)
}
+ else -> EmptyItem("")
}
} else {
when (this.taskType) {
Task.TYPE_HABIT -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
- binding?.emptyViewTitle?.setText(R.string.empty_title_habits)
- binding?.emptyViewDescription?.setText(R.string.empty_description_habits)
+ EmptyItem(
+ getString(R.string.empty_title_habits),
+ getString(R.string.empty_description_habits),
+ R.drawable.icon_habits)
}
Task.TYPE_DAILY -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
- binding?.emptyViewTitle?.setText(R.string.empty_title_dailies)
- binding?.emptyViewDescription?.setText(R.string.empty_description_dailies)
+ EmptyItem(
+ getString(R.string.empty_title_dailies),
+ getString(R.string.empty_description_dailies),
+ R.drawable.icon_dailies)
}
Task.TYPE_TODO -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
- binding?.emptyViewTitle?.setText(R.string.empty_title_todos)
- binding?.emptyViewDescription?.setText(R.string.empty_description_todos)
+ EmptyItem(
+ getString(R.string.empty_title_todos),
+ getString(R.string.empty_description_todos),
+ R.drawable.icon_todos)
}
Task.TYPE_REWARD -> {
- binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
- binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
+ EmptyItem(
+ getString(R.string.empty_title_rewards),
+ null,
+ R.drawable.icon_rewards)
}
+ else -> EmptyItem("")
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt
index b771fa8e5..7a38fa3dc 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/RecyclerViewEmptySupport.kt
@@ -3,22 +3,108 @@ package com.habitrpg.android.habitica.ui.helpers
import android.content.Context
import android.util.AttributeSet
import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AlphaAnimation
+import android.widget.ProgressBar
+import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.EmptyItemBinding
+import com.habitrpg.android.habitica.databinding.FailedItemBinding
+import com.habitrpg.android.habitica.extensions.inflate
+
+data class EmptyItem(
+ var title: String,
+ var text: String? = null,
+ var iconResource: Int? = null,
+ var buttonLabel: String? = null,
+ var onButtonTap: (() -> Unit)? = null
+)
+
+enum class RecyclerViewState {
+ LOADING,
+ EMPTY,
+ DISPLAYING_DATA,
+ FAILED
+}
-//http://stackoverflow.com/a/27801394/1315039
class RecyclerViewEmptySupport : RecyclerView {
- private var emptyView: View? = null
+ var onRefresh: (() -> Unit)? = null
+ var state: RecyclerViewState = RecyclerViewState.LOADING
+ set(value) {
+ field = value
+ when (field) {
+ RecyclerViewState.DISPLAYING_DATA -> updateAdapter(actualAdapter)
+ else -> {
+ updateAdapter(emptyAdapter)
+ emptyAdapter.notifyDataSetChanged()
+ }
+ }
+ }
+
+ private fun updateAdapter(newAdapter: Adapter<*>?) {
+ if (adapter != newAdapter) {
+ super.setAdapter(newAdapter)
+ }
+ }
+
+ var emptyItem: EmptyItem? = null
+ set(value) {
+ field = value
+ emptyAdapter.notifyDataSetChanged()
+ }
+
+ private var actualAdapter: Adapter<*>? = null
+ private val emptyAdapter: Adapter = object : Adapter() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return when (viewType) {
+ 0 -> {
+ val view = parent.inflate(R.layout.loading_item)
+ val animation1 = AlphaAnimation(0.0f, 1.0f)
+ animation1.duration = 300
+ animation1.startOffset = 500
+ animation1.fillAfter = true
+ view.findViewById(R.id.loading_indicator).startAnimation(animation1)
+ object : ViewHolder(view) {}
+ }
+ 1 -> FailedViewHolder(parent.inflate(R.layout.failed_item))
+ else -> EmptyViewHolder(parent.inflate(R.layout.empty_item))
+ }
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ if (holder is EmptyViewHolder) {
+ holder.bind(emptyItem)
+ } else if (holder is FailedViewHolder) {
+ holder.bind(onRefresh)
+ }
+ (holder as? EmptyViewHolder)?.bind(emptyItem)
+ }
+
+ override fun getItemCount(): Int {
+ return 1
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (state) {
+ RecyclerViewState.LOADING -> 0
+ RecyclerViewState.FAILED -> 1
+ else -> 2
+ }
+ }
+ }
+
private val observer = object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
- checkIfEmpty()
+ updateState()
}
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
- checkIfEmpty()
+ updateState()
}
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
- checkIfEmpty()
+ updateState()
}
}
@@ -28,25 +114,59 @@ class RecyclerViewEmptySupport : RecyclerView {
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
- internal fun checkIfEmpty() {
- if (emptyView != null && adapter != null) {
- val emptyViewVisible = adapter?.itemCount == 0
- emptyView?.visibility = if (emptyViewVisible) View.VISIBLE else View.GONE
- visibility = if (emptyViewVisible) View.GONE else View.VISIBLE
+ internal fun updateState(isInitial: Boolean = false) {
+ if (actualAdapter != null && !isInitial) {
+ val emptyViewVisible = actualAdapter?.itemCount == 0
+ if (emptyViewVisible) {
+ state = RecyclerViewState.EMPTY
+ } else {
+ state = RecyclerViewState.DISPLAYING_DATA
+ }
+ } else {
+ state = RecyclerViewState.LOADING
}
}
override fun setAdapter(adapter: Adapter<*>?) {
- val oldAdapter = getAdapter()
+ val oldAdapter = actualAdapter
oldAdapter?.unregisterAdapterDataObserver(observer)
super.setAdapter(adapter)
adapter?.registerAdapterDataObserver(observer)
-
- checkIfEmpty()
- }
-
- fun setEmptyView(emptyView: View?) {
- this.emptyView = emptyView
- checkIfEmpty()
+ actualAdapter = adapter
+ updateState(true)
+ }
+}
+
+class FailedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ private val binding = FailedItemBinding.bind(itemView)
+
+ fun bind(onRefresh: (() -> Unit)?) {
+ if (onRefresh != null) {
+ binding.refreshButton.visibility = View.VISIBLE
+ binding.refreshButton.setOnClickListener { onRefresh() }
+ } else {
+ binding.refreshButton.visibility = View.GONE
+ }
+ }
+}
+
+class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+ private val binding = EmptyItemBinding.bind(itemView)
+
+ fun bind(emptyItem: EmptyItem?) {
+ binding.emptyIconView.setColorFilter(ContextCompat.getColor(itemView.context, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY)
+ emptyItem?.iconResource?.let { binding.emptyIconView.setImageResource(it) }
+ binding.emptyViewTitle.text = emptyItem?.title
+ binding.emptyViewDescription.text = emptyItem?.text
+
+ val buttonLabel =emptyItem?.buttonLabel
+ if (buttonLabel != null) {
+ binding.button.visibility = View.VISIBLE
+ binding.button.text = buttonLabel
+ binding.button.setOnClickListener { emptyItem.onButtonTap?.invoke() }
+ } else {
+ binding.button.visibility = View.GONE
+ }
}
}
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 3e1d2ed9a..216119ab2 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
@@ -9,10 +9,12 @@ import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.view.children
+import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.inflate
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 kotlinx.coroutines.Dispatchers
@@ -289,10 +291,11 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
dialogQueue.removeAt(0)
}
if (dialogQueue.size > 0) {
- if ((dialogQueue[0].context as? Activity)?.isFinishing != true) {
- GlobalScope.launch(context = Dispatchers.Main) {
+ if ((dialogQueue[0].context as? BaseActivity)?.isFinishing != true) {
+ (dialogQueue[0].context as? BaseActivity)?.lifecycleScope?.launch(context = Dispatchers.Main) {
delay(500L)
- if (dialogQueue.size > 0 && (dialogQueue[0].context as? Activity)?.isFinishing == false) {
+ if (dialogQueue.size > 0 && ((dialogQueue[0].context as? Activity)?.isFinishing == false ||
+ ((dialogQueue[0].context as? ContextThemeWrapper)?.baseContext as? Activity)?.isFinishing == false)) {
dialogQueue[0].show()
}
}