Archived
CanteenPageFragment.kt now displays the count of offers that have been hidden due to allergen and dietary preferences
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
Reference in New Issue
Block a user