From ca89e68a91ac69236c9df8a095b550f8b391a497 Mon Sep 17 00:00:00 2001 From: denizk0461 Date: Sun, 23 Apr 2023 20:33:17 +0200 Subject: [PATCH] Implemented allergen functionality by showing the user a bottom sheet --- .../com/denizk0461/studip/data/StwParser.kt | 60 ++++++++++++++++--- .../com/denizk0461/studip/db/AppDatabase.kt | 2 +- .../studip/fragment/CanteenFragment.kt | 8 ++- .../com/denizk0461/studip/model/Allergens.kt | 58 ++++++++++++++++++ .../denizk0461/studip/model/CanteenOffer.kt | 2 + .../studip/model/CanteenOfferGroupElement.kt | 2 + .../com/denizk0461/studip/model/OfferItem.kt | 2 + .../denizk0461/studip/sheet/AllergenSheet.kt | 39 +++++++++++- app/src/main/res/values-de/strings.xml | 46 ++++++++++++++ app/src/main/res/values/strings.xml | 46 ++++++++++++++ 10 files changed, 251 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/denizk0461/studip/model/Allergens.kt diff --git a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt index eb40c1e..fd8a5f9 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt @@ -156,14 +156,17 @@ class StwParser { isGame = prefs.isDietaryPreferenceMet(imageLinkPrefGame), ).deconstruct() + val filteredText = tableRows[1].getFilteredText() + // Save the item to persistent storage repo.insert( OfferItem( itemId, categoryId, - title = tableRows[1].getFilteredText(), + title = filteredText.first, price = tableRows.getTextOrEmpty(2), - prefString, + dietaryPreferences = prefString, + allergens = filteredText.second, ) ) @@ -193,28 +196,67 @@ class StwParser { /** * Processes certain character references into human-readable characters. Since the fetched HTML * contains unresolved symbols such as &, they need to be replaced with their counterpart - * (in this case, &). + * (in this case, &). This method also parses allergens and additives, since they are listed on + * the website of the Studierendenwerk, but they are invisible, rendering them pointless to the + * website user. * - * @return the filtered string + * @return the filtered string and a string describing allergens and additives */ - private fun Element.getFilteredText(): String { + private fun Element.getFilteredText(): Pair { // Retrieve the element's inner HTML and replace faulty characters var text = html() .replace("&", "&") .replace(">", ">") .replace("<", "<") + // All allergens will be collected here + var allergens: String = "" + + /* + * Used to determine whether any allergens have already been found in the item. Should this + * be the case, a delimiter is added. + */ + var hasAllergens: Boolean = false + + /* + * Allergens are stored in super tags. Example: + * 4, a1, a4, c, g + * This is the start index of the opening tag (inclusive). + */ + var indexSupOpen = 0 + + // This is the end index of the closing tag (exclusive) + var indexSupClose = 0 + /* * The Studierendenwerk's website lists allergens, but they are invisible, rendering them * pointless to the website user. As of now, this information is discarded in the app. * TODO implement allergen functionality */ -// while (text.contains("")) { -// text = text.substring(0 until text.indexOf("")) + text.substring(text.indexOf("")+6 until text.length) -// } + while (text.contains("")) { + // Add a delimiter if allergens have already been added + if (hasAllergens) allergens += "," + + // Set indices for the tags + indexSupOpen = text.indexOf("") + indexSupClose = text.indexOf("") + + // Add allergens + allergens += text.substring(indexSupOpen + 5 until indexSupClose) + + // Remove allergens from the title of the offer + text = text.substring(0 until indexSupOpen) + + text.substring((indexSupClose + 6) until text.length) + + /* + * Make sure that the delimiter will be added if more than one instance of allergens is + * found. + */ + hasAllergens = true + } // Return the stripped and updated HTML string - return text + return Pair(text, allergens) } /** diff --git a/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt b/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt index 40fe84e..4605b8b 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt @@ -17,7 +17,7 @@ import com.denizk0461.studip.model.* OfferCategory::class, OfferItem::class ], - version = 10, + version = 11, ) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt index 53734f8..051300f 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt @@ -1,7 +1,6 @@ package com.denizk0461.studip.fragment import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -130,6 +129,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { viewModel.fetchOffers(onRefreshUpdate = { status -> // TODO refresh updates }, onFinish = { + createTabLayoutMediator() // binding.swipeRefreshLayout.isRefreshing = false // createTabLayoutMediator() }) @@ -138,16 +138,17 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { /** * Create and attach the object mediating the tabs for the view pager. + * TODO this is crash-prone */ private fun createTabLayoutMediator() { // Fetch all dates from the database val dates = viewModel.getDates() + if (dates.isNotEmpty()) { - Log.d("eek!7", "dates: ${dates}, viewpager pages: ${binding.viewPager.adapter?.itemCount}") TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> - Log.d("eek!9", position.toString()) tab.text = dates[position].date }.attach() + } } @@ -264,6 +265,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { it.title, it.price, it.dietaryPreferences, + it.allergens ) } ) diff --git a/app/src/main/java/com/denizk0461/studip/model/Allergens.kt b/app/src/main/java/com/denizk0461/studip/model/Allergens.kt new file mode 100644 index 0000000..e1ef3d1 --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/model/Allergens.kt @@ -0,0 +1,58 @@ +package com.denizk0461.studip.model + +import com.denizk0461.studip.R + +object Allergens { + /** + * Converts an allergen into the string ID for its localised full-text version. Elements are + * derived from the website of the Studierendenwerk Bremen: + * https://www.stw-bremen.de/de/mensa/allergene + * + * @param value allergen that will be converted + * @return string resource ID for the allergen + */ + fun getStringRes(value: String): Int = when (value) { + // Additives + "1" -> R.string.allergen_dye + "2" -> R.string.allergen_preservatives + "3" -> R.string.allergen_antioxidants + "4" -> R.string.allergen_phosphates + "5" -> R.string.allergen_sweeteners + "6" -> R.string.allergen_phenylalanine + "7" -> R.string.allergen_sulphured + "8" -> R.string.allergen_blackened + "9" -> R.string.allergen_waxed + "10" -> R.string.allergen_alcohol + "11" -> R.string.allergen_flavour_enhancers + "12" -> R.string.allergen_caffeine + "13" -> R.string.allergen_rennet + // Allergens + "a1" -> R.string.allergen_wheat + "a2" -> R.string.allergen_rye + "a3" -> R.string.allergen_barley + "a4" -> R.string.allergen_oat + "a5" -> R.string.allergen_spelt + "a6" -> R.string.allergen_kamut + "b" -> R.string.allergen_crustaceans + "c" -> R.string.allergen_eggs + "d" -> R.string.allergen_fish + "e" -> R.string.allergen_peanuts + "f" -> R.string.allergen_soy + "g" -> R.string.allergen_dairy + "h1" -> R.string.allergen_almonds + "h2" -> R.string.allergen_hazelnuts + "h3" -> R.string.allergen_walnuts + "h4" -> R.string.allergen_cashew_nuts + "h5" -> R.string.allergen_pecans + "h6" -> R.string.allergen_brazil_nuts + "h7" -> R.string.allergen_pistachios + "h8" -> R.string.allergen_macadamia + "i" -> R.string.allergen_celery + "j" -> R.string.allergen_mustard + "k" -> R.string.allergen_sulphides + "l" -> R.string.allergen_lupins + "m" -> R.string.allergen_sesame + "n" -> R.string.allergen_molluscs + else -> 0 // shouldn't occur + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt b/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt index 7daf957..57d1d09 100644 --- a/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt +++ b/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt @@ -19,6 +19,7 @@ import androidx.room.ColumnInfo * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see * DietaryPrefObject.kt + * @param allergens allergens and additives that are present in the item */ data class CanteenOffer( val itemId: Int, @@ -31,4 +32,5 @@ data class CanteenOffer( val title: String, val price: String, @ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String, + val allergens: String, ) diff --git a/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt b/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt index 81dd943..e1e18fe 100644 --- a/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt +++ b/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt @@ -9,9 +9,11 @@ package com.denizk0461.studip.model * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see * DietaryPrefObject.kt + * @param allergens allergens and additives that are present in the item */ data class CanteenOfferGroupElement( val title: String, val price: String, val dietaryPreferences: String, + val allergens: String, ) diff --git a/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt b/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt index 38ae4d3..5b12364 100644 --- a/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt +++ b/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt @@ -14,6 +14,7 @@ import androidx.room.PrimaryKey * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see * DietaryPrefObject.kt + * @param allergens allergens and additives that are present in the item */ @Entity( tableName = "offer_item", @@ -32,4 +33,5 @@ data class OfferItem( val title: String, val price: String, @ColumnInfo(name = "dietary_preferences") val dietaryPreferences: String, + val allergens: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/sheet/AllergenSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/AllergenSheet.kt index cc507df..b715fc5 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/AllergenSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/AllergenSheet.kt @@ -5,6 +5,7 @@ import android.view.View import com.denizk0461.studip.R import com.denizk0461.studip.data.viewBinding import com.denizk0461.studip.databinding.SheetAllergenBinding +import com.denizk0461.studip.model.Allergens import com.denizk0461.studip.model.CanteenOfferGroupElement class AllergenSheet(private val offer: CanteenOfferGroupElement) : AppSheet(R.layout.sheet_allergen) { @@ -16,7 +17,43 @@ class AllergenSheet(private val offer: CanteenOfferGroupElement) : AppSheet(R.la binding.apply { title.text = offer.title - textContent.text = "hello there" + textContent.text = offer.allergens.compileAllergenString() } } + + /** + * Retrieves localised strings for all allergens present in a given offer and compiles them into + * a human-readable string, if any allergens are present. + * + * @return a string of all allergens + */ + private fun String.compileAllergenString(): String { + + // Let the user know if no allergens are present + if (this.isBlank()) return getString(R.string.allergens_none) + + // Stores all localised allergens + var allergenString = "" + + // Used to insert a delimiter if more than one allergen is found + var isFirst = true + + // Split all allergens to iterate through them + val allergens = this.replace(" ", "").split(",") + + // Iterate through all allergens + allergens.forEach { allergen -> + // Insert a delimiter if more than one allergen is found + if (!isFirst) allergenString += ", " + + // Add a localised string for the allergen + allergenString += getString(Allergens.getStringRes(allergen)) + + // Set when the first allergen has been processed + isFirst = false + } + + // Insert the allergens into a template string + return getString(R.string.allergens_contains, allergenString) + } } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5c62ef3..1d7fef7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -34,6 +34,52 @@ Vegetarisch Wild + + Farbstoffe + Konservierungsstoffe + Antioxidationsmittel + Phosphate + Süßungsmittel + Phenylalaninquelle + geschwärzt + geschwefelt + gewachst + Alkohol + Geschmacksverstärker + Koffein + tierisches Lab + + + Weizen + Roggen + Gerste + Hafer + Dinkel + Kamut + Krebstiere + Eier + Fisch + Erdnüsse + Soja + Milch & Laktose + Mandeln + Haselnüsse + Walnüsse + Cashew-Kerne + Pecannüsse + Paranüsse + Pistazien + Macadamianüsse + Sellerie + Senf + Schwefeldioxide & Sulfide von mehr als 10mg/kg or 10mg/l + Lupinen + Sesam + Weichtiere + + Enthält weder Allergene noch Zusatzstoffe. + Enthält: %s. + Stundenplan Mensen Einstellungen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0558cb8..5d69ce3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,6 +34,52 @@ Vegetarian Game + + dye + preservatives + antioxidants + phosphates + sweeteners + a source of phenylalanine + blackened + sulphured + waxed + alcohol + flavour enhancers + caffeine + animal rennet + + + wheat + rye + barley + oats + spelt + kamut + crustaceans + eggs + fish + peanuts + soy + dairy & lactose + almonds + hazelnuts + walnuts + cashew nuts + pecans + brazil nuts + pistachios + macadamia nuts + celery + mustard + sulphur oxide & sulphide concentrated above 10mg/kg or 10mg/l + lupins + sesame + molluscs + + Contains no allergens or additives. + Contains: %s. + Schedule Canteens Settings