CanteenPageFragment.kt now displays the count of offers that have been hidden due to allergen and dietary preferences

This commit is contained in:
denizk0461
2023-05-14 19:49:09 +02:00
parent d4e64018d2
commit 14e379eebf
6 changed files with 129 additions and 33 deletions
@@ -2,6 +2,7 @@ package com.denizk0461.weserplaner.adapter
import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.DiffUtil
@@ -29,8 +30,16 @@ class CanteenOfferItemAdapter(
private val displayColours: Boolean,
) : RecyclerView.Adapter<CanteenOfferItemAdapter.OfferViewHolder>() {
/**
* Offers to display.
*/
private val offers: MutableList<CanteenOfferGroup> = mutableListOf()
/**
* Difference between filtered list and the list fetched from the website.
*/
private var difference: Int = 0
/**
* View holder class for parent class
*
@@ -43,7 +52,7 @@ class CanteenOfferItemAdapter(
ItemCanteenBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
false,
)
)
}
@@ -52,9 +61,36 @@ class CanteenOfferItemAdapter(
override fun getItemCount(): Int = offers.size
override fun onBindViewHolder(holder: OfferViewHolder, position: Int) {
// Retrieve item for current position
val currentItem = offers[position]
// Context for easier reference
val context = holder.binding.root.context
holder.binding.canteenCard.visibility = if (currentItem.date == "ALL\$HIDDEN") {
View.GONE
} else {
View.VISIBLE
}
// Display
if (position == (offers.size - 1) && difference > 0) {
holder.binding.textDifference.apply {
visibility = View.VISIBLE
text = if (difference == 1) {
context.getString(R.string.canteen_item_difference_text_one)
} else {
context.getString(R.string.canteen_item_difference_text_other, difference)
}
}
} else {
holder.binding.textDifference.apply {
visibility = View.GONE
text = ""
}
}
/*
* Children of the line container MUST be removed before any are added, as otherwise, during
* recycling, lines will be duplicated when they are scrolled off the visible screen area
@@ -69,34 +105,36 @@ class CanteenOfferItemAdapter(
// Set the text to "No offers" (localised)
holder.binding.textCategory.text =
holder.binding.root.context.getString(R.string.canteen_no_offer_header)
context.getString(R.string.canteen_no_offer_header)
// Inflate a single line without binding it to the line container
val line = ItemCanteenLineBinding.inflate(
LayoutInflater.from(holder.binding.root.context),
LayoutInflater.from(context),
)
// Set content to tell the user that no offers are available (localised)
line.textContent.text =
holder.binding.root.context.getString(R.string.canteen_no_offer_desc)
context.getString(R.string.canteen_no_offer_desc)
// Inflate a new icon holder
val img = ItemIconBinding.inflate(LayoutInflater.from(holder.binding.root.context))
val img = ItemIconBinding.inflate(LayoutInflater.from(context))
// Retrieve data for the dietary preference
val (_, drawableId, colourId) = DietaryPreferences.getData(DietaryPreferences.ERROR.ordinal)
val (_, drawableId, colourId) = DietaryPreferences.getData(
DietaryPreferences.ERROR.ordinal,
)
// Set a cross icon
img.imageView.setImageDrawable(
AppCompatResources.getDrawable(
holder.binding.root.context,
context,
drawableId,
)
)
// Set tint of the icon; themed to the preference, if the user has the option enabled
img.imageView.imageTintList = ColorStateList.valueOf(
holder.binding.root.context.theme.getThemedColor(
context.theme.getThemedColor(
if (displayColours) {
colourId
} else {
@@ -119,7 +157,7 @@ class CanteenOfferItemAdapter(
currentItem.offers.forEach { offer ->
// Inflate the new line without binding it to the line container
val line = ItemCanteenLineBinding.inflate(
LayoutInflater.from(holder.binding.root.context),
LayoutInflater.from(context),
)
// Set text values
@@ -144,7 +182,7 @@ class CanteenOfferItemAdapter(
// Iterate through all icons that need to be displayed
indices.forEach { index ->
// Inflate a new icon holder
val img = ItemIconBinding.inflate(LayoutInflater.from(holder.binding.root.context))
val img = ItemIconBinding.inflate(LayoutInflater.from(context))
// Retrieve data for the dietary preference
val (_, drawableId, colourId) = DietaryPreferences.getData(index)
@@ -152,14 +190,14 @@ class CanteenOfferItemAdapter(
// Set the appropriate icon
img.imageView.setImageDrawable(
AppCompatResources.getDrawable(
holder.binding.root.context,
context,
drawableId,
)
)
// Set tint of the icon
img.imageView.imageTintList = ColorStateList.valueOf(
holder.binding.root.context.theme.getThemedColor(
context.theme.getThemedColor(
if (displayColours) {
colourId
} else {
@@ -193,7 +231,9 @@ class CanteenOfferItemAdapter(
*
* @param newData new dataset to be displayed
*/
fun setNewData(newData: List<CanteenOfferGroup>) {
fun setNewData(newData: List<CanteenOfferGroup>, difference: Int) {
this.difference = difference
// Calculate the difference between the old list and the new list
val diffResult = DiffUtil.calculateDiff(AppDiffUtilCallback(offers, newData))
@@ -205,6 +245,9 @@ class CanteenOfferItemAdapter(
// Tell the DiffUtil which items have changed between the two lists
diffResult.dispatchUpdatesTo(this)
// Force an update on the last item to display how many items have been hidden
notifyItemChanged(offers.size - 1)
}
/**
@@ -263,7 +263,7 @@ class AppRepository(app: Application) {
defaultValue = AllergenPreferences.TEMPLATE,
)
/**
* This value determines how many allergens the user wants to have displayed or hidden.
* This value determines how many allergens the user wants to have hidden.
*/
fun getPreferenceAllergenConfigCount(): Int = getPreferenceAllergenConfig().run {
if (isBlank()) 0 else split(",").count()
@@ -84,36 +84,75 @@ class CanteenPageFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListen
// Observe offers and re-set them if they change
viewModel.getOffersByDay(currentDay).observe(viewLifecycleOwner) { offers ->
// Refresh temporarily stored offer list
offerList.clear()
offerList.addAll(offers)
recyclerViewAdapter.setNewData(offers.filterElements().groupElements().distinct())
// Set new data
recyclerViewAdapter.setData(offers)
}
}
// Retrieve parent fragment to access its functions
val navHostFragment = activity?.supportFragmentManager?.fragments?.get(0) as? NavHostFragment
val parent = navHostFragment?.childFragmentManager?.primaryNavigationFragment as? CanteenFragment
// Set up FAB behaviour for parent fragment's FAB if activity is not null
activity?.let { activity ->
// Retrieve parent fragment to access its functions
val navHostFragment = activity
.supportFragmentManager
.fragments[0] as NavHostFragment
val parentFragment = navHostFragment
.childFragmentManager
.primaryNavigationFragment as CanteenFragment
// Set up scroll change listener to shrink and extend FAB accordingly
binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
// Calculate the vertical scroll difference
val dy = scrollY - oldScrollY
if (dy > 0) {
// If scrolling down, shrink the FAB
parent?.shrinkFab()
} else if (dy < 0) {
// If scrolling up, extend the FAB
parent?.extendFab()
// Set up scroll change listener to shrink and extend FAB accordingly
binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
// Calculate the vertical scroll difference
val dy = scrollY - oldScrollY
if (dy > 0) {
// If scrolling down, shrink the FAB
parentFragment.shrinkFab()
} else if (dy < 0) {
// If scrolling up, extend the FAB
parentFragment.extendFab()
}
}
}
// Observe dietary preference updates and re-set the items if a change is detected
/*
* Observe dietary preference updates and re-set the items if a change is detected to force
* an update.
*/
viewModel.dietaryPreferencesUpdate.observe(viewLifecycleOwner) {
// Re-set the list of items to the recycler view, forcing an update
recyclerViewAdapter.setNewData(offerList.filterElements().groupElements().distinct())
// Re-set data
recyclerViewAdapter.setData(offerList)
}
}
private fun CanteenOfferItemAdapter.setData(offers: List<CanteenOffer>) {
/*
* Filter items that contain user-selected allergens or user-excluded dietary
* preferences.
*/
val filteredOffers = offers.filterElements()
// Put elements into groups
val groupedOffers = filteredOffers.groupElements().distinct()
val difference = offers.size - filteredOffers.size
val newOffers = if (groupedOffers.isEmpty() && difference > 0) {
listOf(CanteenOfferGroup(
"ALL\$HIDDEN", 0, "", "", listOf(),
))
} else groupedOffers
// Set new items into the adapter
setNewData(
newOffers,
// Difference is calculated from the unfiltered and filtered data sets
difference,
)
}
/**
* Groups [CanteenOffer] elements by their categories into [CanteenOfferGroup] elements and
* filters for the user's dietary preferences.
@@ -130,7 +169,7 @@ class CanteenPageFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListen
category,
canteen,
filter {
it.category == category && it.date == date && it.canteen == canteen
it.category == category && it.canteen == canteen
}.map {
// Map the individual items to their respective groups
CanteenOfferGroupElement(
+9 -1
View File
@@ -4,9 +4,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:paddingVertical="4dp">
android:paddingVertical="4dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:id="@+id/canteen_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.Material3.CardView.Outlined">
@@ -45,4 +47,10 @@
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/text_difference"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</androidx.appcompat.widget.LinearLayoutCompat>
+3
View File
@@ -24,6 +24,9 @@
<string name="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string>
<string name="canteen_fetch_error">Mensaplan konnte nicht heruntergeladen werden!</string>
<string name="canteen_item_difference_text_one">Ein Angebot aufgrund deiner Präferenzen versteckt.</string>
<string name="canteen_item_difference_text_other">%d Angebote aufgrund deiner Präferenzen versteckt.</string>
<string name="event_empty_fetch_button">Lade deinen Stundenplan von Stud.IP herunter</string>
<!-- plurals string resource cannot be used here because Android doesn't treat 0 differently from 2 -->
+3
View File
@@ -25,6 +25,9 @@
<string name="fetch_error_webpage_snack">You must navigate to your schedule!</string>
<string name="canteen_fetch_error">Couldn\'t retrieve the canteen plan!</string>
<string name="canteen_item_difference_text_one">One offer hidden due to your preferences.</string>
<string name="canteen_item_difference_text_other">%d offers hidden due to your preferences.</string>
<string name="event_empty_fetch_button">Download your schedule from Stud.IP</string>
<!-- plurals resource cannot be used here; view German translation for details -->