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.content.res.ColorStateList
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
@@ -29,8 +30,16 @@ class CanteenOfferItemAdapter(
private val displayColours: Boolean, private val displayColours: Boolean,
) : RecyclerView.Adapter<CanteenOfferItemAdapter.OfferViewHolder>() { ) : RecyclerView.Adapter<CanteenOfferItemAdapter.OfferViewHolder>() {
/**
* Offers to display.
*/
private val offers: MutableList<CanteenOfferGroup> = mutableListOf() 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 * View holder class for parent class
* *
@@ -43,7 +52,7 @@ class CanteenOfferItemAdapter(
ItemCanteenBinding.inflate( ItemCanteenBinding.inflate(
LayoutInflater.from(parent.context), LayoutInflater.from(parent.context),
parent, parent,
false false,
) )
) )
} }
@@ -52,9 +61,36 @@ class CanteenOfferItemAdapter(
override fun getItemCount(): Int = offers.size override fun getItemCount(): Int = offers.size
override fun onBindViewHolder(holder: OfferViewHolder, position: Int) { override fun onBindViewHolder(holder: OfferViewHolder, position: Int) {
// Retrieve item for current position // Retrieve item for current position
val currentItem = offers[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 * 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 * 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) // Set the text to "No offers" (localised)
holder.binding.textCategory.text = 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 // Inflate a single line without binding it to the line container
val line = ItemCanteenLineBinding.inflate( 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) // Set content to tell the user that no offers are available (localised)
line.textContent.text = 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 // 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 // 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 // Set a cross icon
img.imageView.setImageDrawable( img.imageView.setImageDrawable(
AppCompatResources.getDrawable( AppCompatResources.getDrawable(
holder.binding.root.context, context,
drawableId, drawableId,
) )
) )
// Set tint of the icon; themed to the preference, if the user has the option enabled // Set tint of the icon; themed to the preference, if the user has the option enabled
img.imageView.imageTintList = ColorStateList.valueOf( img.imageView.imageTintList = ColorStateList.valueOf(
holder.binding.root.context.theme.getThemedColor( context.theme.getThemedColor(
if (displayColours) { if (displayColours) {
colourId colourId
} else { } else {
@@ -119,7 +157,7 @@ class CanteenOfferItemAdapter(
currentItem.offers.forEach { offer -> currentItem.offers.forEach { offer ->
// Inflate the new line without binding it to the line container // Inflate the new line without binding it to the line container
val line = ItemCanteenLineBinding.inflate( val line = ItemCanteenLineBinding.inflate(
LayoutInflater.from(holder.binding.root.context), LayoutInflater.from(context),
) )
// Set text values // Set text values
@@ -144,7 +182,7 @@ class CanteenOfferItemAdapter(
// Iterate through all icons that need to be displayed // Iterate through all icons that need to be displayed
indices.forEach { index -> indices.forEach { index ->
// Inflate a new icon holder // 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 // Retrieve data for the dietary preference
val (_, drawableId, colourId) = DietaryPreferences.getData(index) val (_, drawableId, colourId) = DietaryPreferences.getData(index)
@@ -152,14 +190,14 @@ class CanteenOfferItemAdapter(
// Set the appropriate icon // Set the appropriate icon
img.imageView.setImageDrawable( img.imageView.setImageDrawable(
AppCompatResources.getDrawable( AppCompatResources.getDrawable(
holder.binding.root.context, context,
drawableId, drawableId,
) )
) )
// Set tint of the icon // Set tint of the icon
img.imageView.imageTintList = ColorStateList.valueOf( img.imageView.imageTintList = ColorStateList.valueOf(
holder.binding.root.context.theme.getThemedColor( context.theme.getThemedColor(
if (displayColours) { if (displayColours) {
colourId colourId
} else { } else {
@@ -193,7 +231,9 @@ class CanteenOfferItemAdapter(
* *
* @param newData new dataset to be displayed * @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 // Calculate the difference between the old list and the new list
val diffResult = DiffUtil.calculateDiff(AppDiffUtilCallback(offers, newData)) val diffResult = DiffUtil.calculateDiff(AppDiffUtilCallback(offers, newData))
@@ -205,6 +245,9 @@ class CanteenOfferItemAdapter(
// Tell the DiffUtil which items have changed between the two lists // Tell the DiffUtil which items have changed between the two lists
diffResult.dispatchUpdatesTo(this) 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, 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 { fun getPreferenceAllergenConfigCount(): Int = getPreferenceAllergenConfig().run {
if (isBlank()) 0 else split(",").count() if (isBlank()) 0 else split(",").count()
@@ -84,36 +84,75 @@ class CanteenPageFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListen
// Observe offers and re-set them if they change // Observe offers and re-set them if they change
viewModel.getOffersByDay(currentDay).observe(viewLifecycleOwner) { offers -> viewModel.getOffersByDay(currentDay).observe(viewLifecycleOwner) { offers ->
// Refresh temporarily stored offer list
offerList.clear() offerList.clear()
offerList.addAll(offers) offerList.addAll(offers)
recyclerViewAdapter.setNewData(offers.filterElements().groupElements().distinct())
// Set new data
recyclerViewAdapter.setData(offers)
} }
} }
// Retrieve parent fragment to access its functions // Set up FAB behaviour for parent fragment's FAB if activity is not null
val navHostFragment = activity?.supportFragmentManager?.fragments?.get(0) as? NavHostFragment activity?.let { activity ->
val parent = navHostFragment?.childFragmentManager?.primaryNavigationFragment as? CanteenFragment // 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 // Set up scroll change listener to shrink and extend FAB accordingly
binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY -> binding.recyclerView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
// Calculate the vertical scroll difference // Calculate the vertical scroll difference
val dy = scrollY - oldScrollY val dy = scrollY - oldScrollY
if (dy > 0) { if (dy > 0) {
// If scrolling down, shrink the FAB // If scrolling down, shrink the FAB
parent?.shrinkFab() parentFragment.shrinkFab()
} else if (dy < 0) { } else if (dy < 0) {
// If scrolling up, extend the FAB // If scrolling up, extend the FAB
parent?.extendFab() 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) { viewModel.dietaryPreferencesUpdate.observe(viewLifecycleOwner) {
// Re-set the list of items to the recycler view, forcing an update // Re-set data
recyclerViewAdapter.setNewData(offerList.filterElements().groupElements().distinct()) 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 * Groups [CanteenOffer] elements by their categories into [CanteenOfferGroup] elements and
* filters for the user's dietary preferences. * filters for the user's dietary preferences.
@@ -130,7 +169,7 @@ class CanteenPageFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListen
category, category,
canteen, canteen,
filter { filter {
it.category == category && it.date == date && it.canteen == canteen it.category == category && it.canteen == canteen
}.map { }.map {
// Map the individual items to their respective groups // Map the individual items to their respective groups
CanteenOfferGroupElement( CanteenOfferGroupElement(
+9 -1
View File
@@ -4,9 +4,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingHorizontal="8dp" android:paddingHorizontal="8dp"
android:paddingVertical="4dp"> android:paddingVertical="4dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:id="@+id/canteen_card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/Widget.Material3.CardView.Outlined"> style="@style/Widget.Material3.CardView.Outlined">
@@ -45,4 +47,10 @@
</com.google.android.material.card.MaterialCardView> </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> </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="fetch_error_webpage_snack">Du musst deinen Stundenplan öffnen!</string>
<string name="canteen_fetch_error">Mensaplan konnte nicht heruntergeladen werden!</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> <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 --> <!-- 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="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_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> <string name="event_empty_fetch_button">Download your schedule from Stud.IP</string>
<!-- plurals resource cannot be used here; view German translation for details --> <!-- plurals resource cannot be used here; view German translation for details -->