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.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(
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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 -->
|
||||||
|
|||||||
@@ -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 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user