From 2bf340bdffbf1c4d49fe56672c68531d2e9e0266 Mon Sep 17 00:00:00 2001 From: denizk0461 Date: Sat, 29 Apr 2023 10:56:52 +0200 Subject: [PATCH] TabLayoutMediator has been re-instantiated, hopefully in a form that is less crash-prone. Crashes will also be handled now in StwParser.kt --- .../studip/activity/FetcherActivity.kt | 15 +-- .../java/com/denizk0461/studip/data/Misc.kt | 12 ++ .../com/denizk0461/studip/data/StwParser.kt | 117 +++++++++++------ .../java/com/denizk0461/studip/db/AppDAO.kt | 24 +--- .../com/denizk0461/studip/db/AppRepository.kt | 16 +-- .../studip/fragment/CanteenFragment.kt | 118 +++++------------- .../studip/model/DietaryPreferences.kt | 2 + .../denizk0461/studip/sheet/DevCodeSheet.kt | 11 +- .../studip/viewmodel/CanteenViewModel.kt | 43 +++---- app/src/main/res/layout/fragment_canteen.xml | 8 ++ app/src/main/res/layout/fragment_settings.xml | 2 +- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 3 +- 13 files changed, 158 insertions(+), 214 deletions(-) diff --git a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt index b48c279..9e17573 100644 --- a/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt +++ b/app/src/main/java/com/denizk0461/studip/activity/FetcherActivity.kt @@ -8,10 +8,9 @@ import android.webkit.WebView import android.webkit.WebViewClient import androidx.lifecycle.ViewModelProvider import com.denizk0461.studip.R -import com.denizk0461.studip.data.getThemedColor +import com.denizk0461.studip.data.showErrorSnackBar import com.denizk0461.studip.databinding.ActivityFetcherBinding import com.denizk0461.studip.viewmodel.FetcherViewModel -import com.google.android.material.snackbar.Snackbar import java.io.IOException import java.net.URLDecoder @@ -90,18 +89,8 @@ class FetcherActivity : Activity() { // finish() // } } catch (e: IOException) { - // Let the user know that an error occurred - Snackbar - .make( - binding.rootView, - getString(R.string.fetch_error_snack), - Snackbar.LENGTH_SHORT - ) - // Set colours to signify an error - .setBackgroundTint(theme.getThemedColor(R.attr.colorErrorContainer)) - .setTextColor(theme.getThemedColor(R.attr.colorOnErrorContainer)) - .show() + theme.showErrorSnackBar(binding.rootView, getString(R.string.fetch_error_snack)) } } } diff --git a/app/src/main/java/com/denizk0461/studip/data/Misc.kt b/app/src/main/java/com/denizk0461/studip/data/Misc.kt index 092ce7e..613aabd 100644 --- a/app/src/main/java/com/denizk0461/studip/data/Misc.kt +++ b/app/src/main/java/com/denizk0461/studip/data/Misc.kt @@ -5,7 +5,10 @@ import android.content.res.Resources import android.util.TypedValue import android.widget.Toast import androidx.annotation.AttrRes +import androidx.coordinatorlayout.widget.CoordinatorLayout +import com.denizk0461.studip.R import com.denizk0461.studip.exception.AcademicQuarterNotApplicableException +import com.google.android.material.snackbar.Snackbar import kotlin.jvm.Throws /** @@ -42,6 +45,15 @@ fun showToast(context: Context, text: String) { Toast.makeText(context, text, Toast.LENGTH_SHORT).show() } +fun Resources.Theme.showErrorSnackBar(view: CoordinatorLayout, text: String) { + Snackbar + .make(view, text, Snackbar.LENGTH_SHORT) + // Set colours to signify an error + .setBackgroundTint(getThemedColor(R.attr.colorErrorContainer)) + .setTextColor(getThemedColor(R.attr.colorOnErrorContainer)) + .show() +} + /** * Provides a conversion method between a timestamp ending in a full hour, and one with an academic * quarter applied. 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 6fedc78..7510730 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt @@ -3,9 +3,12 @@ package com.denizk0461.studip.data import android.app.Application import com.denizk0461.studip.db.AppRepository import com.denizk0461.studip.model.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.jsoup.Jsoup import org.jsoup.nodes.Element import org.jsoup.select.Elements +import kotlin.jvm.Throws /** * Parser class used for fetching and collecting canteen offers from the website of the @@ -36,11 +39,14 @@ class StwParser(application: Application) { /** * Parses through a list of canteen plans and saves them to persistent storage. * - * @param onRefreshUpdate action call for when an update on the status of the fetch is - * available - * @param onFinish action call for when the fetch has finished + * @param onFinish action call for when the fetch has finished + * @param onError action call for when an error has occurred */ - fun parse(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) { + suspend fun parse( + canteen: Int, + onFinish: () -> Unit, + onError: () -> Unit + ) { /* * Reset primary key values. This needs to be done in case this method is called multiple * times within the app's lifespan @@ -65,30 +71,47 @@ class StwParser(application: Application) { else -> urlUniMensa } - // Delete all previous entries and start afresh - repo.nukeOffers() + /* + * Handle the fetch operation in an encompassing try-catch to prevent the app from crashing. + * User will be notified if an error occurs. + */ + try { - // Fetch offers from the chosen canteen - val (dates, canteens, categories, items) = parseFromPage(link) + // Fetch offers from the chosen canteen + val (dates, canteens, categories, items) = parseFromPage(link) - // Save everything all at once into the database - with (repo) { - insertDates(dates) - insertCanteens(canteens) - insertCategories(categories) - insertItems(items) + // Delete all previous entries and start afresh + repo.nukeOffers() + + // Save everything all at once into the database + with(repo) { + insertDates(dates) + insertCanteens(canteens) + insertCategories(categories) + insertItems(items) + } + + // Call action to let the user know the fetch has finished + onFinish() + + } catch (e: RuntimeException) { + /* + * Call action to let the user know an error occurred. Do this on the main thread to + * access UI. + */ + withContext(Dispatchers.Main) { + onError() + } } - // TODO implement onRefresh(Int) - - // Action call once all fetching activities have finished - onFinish() } /** * Fetch and parse the plan of a specific canteen. * - * @param url link to the canteen to be scraped. Must be a subpage of stw-bremen.de + * @param url link to the canteen to be scraped. Must be a subpage of stw-bremen.de + * @throws RuntimeException if something goes wrong during the fetch */ + @Throws(RuntimeException::class) private fun parseFromPage(url: String): StwResults { val dates = mutableListOf() @@ -189,25 +212,34 @@ class StwParser(application: Application) { // Retrieve the parent element holding the item's title and price val tableRows = element.getElementsByTag("td") - // Retrieve the dietary preferences the item meets - val prefs = element - .getElementsByClass("field field-name-field-food-types")[0] + /* + * Retrieve the dietary preferences the item meets and parse them into a + * string that can be inserted into the database. + */ + val prefs = try { + with(element + .getElementsByClass("field field-name-field-food-types")[0] + ) { + DietaryPreferences.Object( + isFair = isDietaryPreferenceMet(imageLinkPrefFair), + isFish = isDietaryPreferenceMet(imageLinkPrefFish), + isPoultry = isDietaryPreferenceMet(imageLinkPrefPoultry), + isLamb = isDietaryPreferenceMet(imageLinkPrefLamb), + isVital = isDietaryPreferenceMet(imageLinkPrefVital), + isBeef = isDietaryPreferenceMet(imageLinkPrefBeef), + isPork = isDietaryPreferenceMet(imageLinkPrefPork), + isVegan = isDietaryPreferenceMet(imageLinkPrefVegan), + isVegetarian = isDietaryPreferenceMet(imageLinkPrefVegetarian), + isGame = isDietaryPreferenceMet(imageLinkPrefGame), + ) + } + } catch (e: IndexOutOfBoundsException) { + DietaryPreferences.NONE_MET + } - // Parse the preferences into a string for the database - val prefString = DietaryPreferences.Object( - isFair = prefs.isDietaryPreferenceMet(imageLinkPrefFair), - isFish = prefs.isDietaryPreferenceMet(imageLinkPrefFish), - isPoultry = prefs.isDietaryPreferenceMet(imageLinkPrefPoultry), - isLamb = prefs.isDietaryPreferenceMet(imageLinkPrefLamb), - isVital = prefs.isDietaryPreferenceMet(imageLinkPrefVital), - isBeef = prefs.isDietaryPreferenceMet(imageLinkPrefBeef), - isPork = prefs.isDietaryPreferenceMet(imageLinkPrefPork), - isVegan = prefs.isDietaryPreferenceMet(imageLinkPrefVegan), - isVegetarian = prefs.isDietaryPreferenceMet(imageLinkPrefVegetarian), - isGame = prefs.isDietaryPreferenceMet(imageLinkPrefGame), - ).deconstruct() - - val filteredText = tableRows[1].getFilteredText() + val filteredText = getOrElse(orElse = Pair("", "")) { + tableRows[1].getFilteredText() + } // Save the item to its list items.add( @@ -216,7 +248,7 @@ class StwParser(application: Application) { categoryId, title = filteredText.first, price = tableRows.getTextOrEmpty(2), - dietaryPreferences = prefString, + dietaryPreferences = prefs.deconstruct(), allergens = filteredText.second, ) ) @@ -300,7 +332,6 @@ class StwParser(application: Application) { /* * 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("")) { @@ -367,6 +398,12 @@ class StwParser(application: Application) { else -> "00" // shouldn't occur } + private fun getOrElse(orElse: T, action: () -> T): T = try { + action() + } catch (e: Exception) { + orElse + } + /** * Retrieves a text and adds
tags with line breaks. * @@ -407,7 +444,7 @@ class StwParser(application: Application) { private val urlGW2 = "https://www.stw-bremen.de/de/cafeteria/gw2" // private val urlGraz = "" // No link since there are only snacks on offer that are not listed online private val urlHSBNeustadt = "https://www.stw-bremen.de/de/mensa/neustadtswall" - private val urlHSBWerder = "https://www.stw-bremen.de/de/mensa/werderstra%C3%9Fe" + private val urlHSBWerder = "https://www.stw-bremen.de/de/mensa/werderstraße" private val urlHSBAirport = "https://www.stw-bremen.de/de/mensa/airport" private val urlHfK = "https://www.stw-bremen.de/de/mensa/interimsmensa-hfk" private val urlMensaBHV = "https://www.stw-bremen.de/de/mensa/bremerhaven" diff --git a/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt b/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt index e780226..6b07fc3 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppDAO.kt @@ -67,20 +67,6 @@ interface AppDAO { /* --- canteen offers --- */ - /** - * Retrieves all canteen offers. Objects will be joined through their primary/foreign keys from - * instances of OfferDate.kt, OfferCanteen.kt, OfferCategory.kt, and OfferItem.kt. - * - * @return all canteen offers exposed through a LiveData object - */ - @get:Query( - "SELECT * FROM offer_item " + - "JOIN offer_category ON offer_item.categoryId = offer_category.id " + - "JOIN offer_canteen ON offer_category.canteenId = offer_canteen.id " + - "JOIN offer_date ON offer_category.dateId = offer_date.id" - ) - val allOffers: LiveData> - /** * Retrieves all canteen offers that match given dietary preferences. Objects will be joined * through their primary/foreign keys from instances of OfferDate.kt, OfferCanteen.kt, @@ -113,21 +99,13 @@ interface AppDAO { ) fun getOffersByDay(day: Int): LiveData> - /** - * Retrieves the amount of dates represented in the offers stored locally. Can be observed. - * - * @return date count as LiveData - */ - @Query("SELECT COUNT(*) FROM offer_date") - fun getDateCount(): LiveData - /** * Retrieves all canteen offer date objects. * * @return a list of instances of canteen offer dates */ @Query("SELECT * FROM offer_date ORDER BY id") - fun getDates(): List + fun getDates(): LiveData> /** * Deletes all dates from the database. diff --git a/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt b/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt index 8da43fc..373c8b9 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt @@ -78,13 +78,6 @@ class AppRepository(app: Application) { */ fun getCanteenOpeningHours(): String = dao.getCanteenOpeningHours() - /** - * Retrieves all canteen offers. - * - * @return all canteen offers exposed through a LiveData object - */ - val allOffers: LiveData> = dao.allOffers - /** * Retrieves all canteen offers that match given day. * @@ -92,13 +85,6 @@ class AppRepository(app: Application) { */ fun getOffersByDay(day: Int): LiveData> = dao.getOffersByDay(day) - /** - * Retrieves the amount of dates represented in the offers stored locally. Can be observed. - * - * @return date count as LiveData - */ - fun getDateCount(): LiveData = dao.getDateCount() - /** * Updates a schedule element. * @@ -122,7 +108,7 @@ class AppRepository(app: Application) { * * @return a list of instances of canteen offer dates */ - fun getDates(): List = dao.getDates() + fun getDates(): LiveData> = dao.getDates() /** * Retrieves the regex string used to determine the user's dietary preferences, or, if no 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 fdd81ec..0381efa 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt @@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentActivity import androidx.fragment.app.viewModels import com.denizk0461.studip.R import com.denizk0461.studip.adapter.CanteenOfferPageAdapter +import com.denizk0461.studip.data.showErrorSnackBar import com.denizk0461.studip.databinding.FragmentCanteenBinding import com.denizk0461.studip.model.* import com.denizk0461.studip.sheet.TextSheet @@ -37,17 +38,10 @@ class CanteenFragment : AppFragment() { // View model reference for providing access to the database private val viewModel: CanteenViewModel by viewModels() - // Elements in the canteen plan - private var elements: List = listOf() - - // Count of days displayed in the canteen plan - private var dateSize: Int = 0 - - // Used if no preference has been set or none can be found - private val emptyPreferenceRegex: String = ".........." - private var openingHours = "" + private var dates: List = listOf() + // Instantiate the view binding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentCanteenBinding.inflate(inflater, container, false) @@ -78,10 +72,10 @@ class CanteenFragment : AppFragment() { binding.buttonCanteenPicker.text = getCurrentlySelectedCanteenName() // Display to the user that the canteen plan will be refreshed - binding.swipeRefreshLayout.isRefreshing = true +// binding.swipeRefreshLayout.isRefreshing = true // Refresh the canteen menu for the newly selected canteen -// refresh() + refresh() true } inflate(R.menu.menu_canteens) @@ -92,6 +86,7 @@ class CanteenFragment : AppFragment() { // Set currently selected canteen to the button binding.buttonCanteenPicker.text = getCurrentlySelectedCanteenName() + // Set up button to display info (most likely opening hours) binding.buttonInfo.setOnClickListener { openBottomSheet( TextSheet( @@ -104,7 +99,6 @@ class CanteenFragment : AppFragment() { ) } - // Assign a preference value to every button to filter for dietary preferences val chipMap = mapOf( binding.chipPrefFair to DietaryPreferences.WELFARE, @@ -134,8 +128,7 @@ class CanteenFragment : AppFragment() { * it removes the view before immediately adding it back, this method causes a * flicker. * TODO implement a more efficient / better-looking method - * TODO order the chips alphabetically - * TODO the refreshing ability stopped working + * TODO order the chips alphabetically? */ val index = binding.chipsPreference.indexOfChild(buttonView) binding.chipsPreference.removeView(buttonView) @@ -144,43 +137,25 @@ class CanteenFragment : AppFragment() { } // Set up the view pager's adapter - viewPagerAdapter = CanteenOfferPageAdapter( - activity as FragmentActivity, - ) + viewPagerAdapter = CanteenOfferPageAdapter(activity as FragmentActivity) - viewModel.getDateCount().observe(viewLifecycleOwner) { dateCount -> - viewPagerAdapter.itemCount = dateCount - binding.swipeRefreshLayout.isRefreshing = false + // Date count is observed to correctly set the amount of pages and the corresponding tabs + viewModel.getDates().observe(viewLifecycleOwner) { newDates -> + + // Retrieve the dates individually and store them for the tab mediator to use + dates = newDates.map { it.date } + + // Let the adapter know of the new amount of dates (pages) to display + viewPagerAdapter.itemCount = dates.size } // Assign the adapter to the view pager binding.viewPager.adapter = viewPagerAdapter - // Set up LiveData observer to refresh the view on update -// viewModel.allOffers.observe(viewLifecycleOwner) { offers -> -// // Update the element list stored in this fragment -// elements = offers -// -// // Group elements by category -// val groupedElements = offers.groupElements().distinct() -// -// // Find all dates for which items are available -// val newDates = groupedElements.map { it.date }.distinct() -// -// // Update the date count stored in this fragment -// dateSize = newDates.size -// -// // Update the item list in the view pager's adapter -//// viewPagerAdapter.setNewItems(groupedElements, dateSize) -// -// // Set the text for the opening hours dialogue -// openingHours = viewModel.getCanteenOpeningHours() -// -// binding.swipeRefreshLayout.isRefreshing = false -// -// // Create and attach the tab layout for the ViewPager -// createTabLayoutMediator(newDates) -// } + // Set up TabLayoutMediator to populate tabs + TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> + tab.text = dates[position] + }.attach() // Set up functions for when the user swipes to refresh the view binding.swipeRefreshLayout.setOnRefreshListener { @@ -188,23 +163,6 @@ class CanteenFragment : AppFragment() { } } - /** - * Create and attach the object mediating the tabs for the view pager. - * - * TODO this is error-prone, likes to crash with an IndexOutOfBoundsException, and I have no - * fucking idea why - * - * @param dates titles for the individual tabs - */ - private fun createTabLayoutMediator(dates: List) { - // Only attach the mediator if the list has items, otherwise an error would occur - if (dates.isNotEmpty()) { - TabLayoutMediator(binding.dayTabLayout, binding.viewPager) { tab, position -> - tab.text = dates[position] - }.attach() - } - } - // Invalidate the view binding override fun onDestroyView() { super.onDestroyView() @@ -223,7 +181,6 @@ class CanteenFragment : AppFragment() { // Update the view viewModel.registerDietaryPreferencesUpdate() -// viewPagerAdapter.setNewItems(elements.groupElements().distinct(), dateSize) } /** @@ -258,38 +215,21 @@ class CanteenFragment : AppFragment() { /** * Downloads the canteen offers and refreshes them in the app. - * TODO handle SocketTimeoutException */ private fun refresh() { // Retrieve new offers from the website(s) - viewModel.fetchOffers(viewModel.preferenceCanteen, onRefreshUpdate = { status -> - // TODO refresh updates - }, onFinish = { + viewModel.fetchOffers(viewModel.preferenceCanteen, onFinish = { /* * Tell the swipe refresh layout to stop refreshing. - * TODO if an exception is raised, this will not be fired. This must be changed */ -// binding.swipeRefreshLayout.isRefreshing = false -// createTabLayoutMediator() + binding.swipeRefreshLayout.isRefreshing = false + + }, onError = { + context?.theme?.showErrorSnackBar( + binding.snackbarContainer, + getString(R.string.canteen_fetch_error) + ) + binding.swipeRefreshLayout.isRefreshing = false }) } - - /** - * Executed when an item has been clicked. - * - * @param offer item that has been clicked - */ -// override fun onClick(offer: CanteenOfferGroupElement, category: String) { -// openBottomSheet(AllergenSheet(offer, category)) -// } -// -// /** -// * Executed when an item has been long-pressed. -// * -// * @param offer item that has been long-pressed -// * @return whether the long press was successful -// */ -// override fun onLongClick(offer: CanteenOfferGroupElement): Boolean { -// return false -// } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt b/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt index db65055..4d34e1a 100644 --- a/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt +++ b/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt @@ -139,6 +139,8 @@ enum class DietaryPreferences(val value: String) { const val TEMPLATE_EMPTY: String = ".........." + val NONE_MET: Object = construct(TEMPLATE_EMPTY) + /** * Constructs a DietaryPrefObject from a regex string. String must be 10 characters long. * A char equal to C_TRUE is treated as a true boolean value. Any other char is treated as a diff --git a/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt index c10626f..7d9c917 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/DevCodeSheet.kt @@ -44,11 +44,11 @@ class DevCodeSheet( * * About the inks; they are safe (which is suspicious of me to say), just encoded so * people can't just look at the file and figure out all the codes! Of course, they're - * easily decoded, but who would go for that? You would have to be a pretty big nerd to - * do that. Then again, I was the one who encoded all of these links in the first place, - * so I suppose the joke's on me... + * easily decoded, but who would go for that? You would have to have a lot of time to + * waste to do that. Then again, I was the one who encoded all of these links in the + * first place, so I suppose the joke's on me... * - * If you're reading this – why? + * If you're reading this, I ask you – why? */ when (binding.input.text.toString().uppercase()) { "U1RST0JF".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS90S2k5Wi1mNnFYNA==".d64) @@ -59,6 +59,9 @@ class DevCodeSheet( "SE9ORVNU".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS9DSEg0OE5LUEFraw==".d64) "QkJVT0s/".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS90Rm1PMm1TY0tHNA==".d64) "SUhUU0Mq".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS85bXZ4SVdhWHZuWQ==".d64) + "SU5TQU5F".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS90NE9kYTlUZFYwbw==".d64) + "X0RSV05f".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS9SUVpUUy1CWjhtcw==".d64) + "TUFSR0Uh".d64 -> launchLink("aHR0cHM6Ly95b3V0dS5iZS8tbHFxRHZXRjQ1dw==".d64) "NEVENT" -> { // nuke events nukeEvents() showToast(context, "Nuked all events") diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt index 0d870f4..f87f301 100644 --- a/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt @@ -2,8 +2,11 @@ package com.denizk0461.studip.viewmodel import android.app.Application import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope import com.denizk0461.studip.data.StwParser import com.denizk0461.studip.model.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * View model for [com.denizk0461.studip.fragment.CanteenFragment] @@ -15,21 +18,12 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { // Instantiate a parser, should the user want to refresh the canteen offers private val parser = StwParser(app) - /** - * Retrieves all Stud.IP events. - * - * @return all Stud.IP events exposed through a LiveData object - */ - val allOffers: LiveData> = repo.allOffers - - - /** * Retrieves all canteen offer date objects. * * @return a list of instances of canteen offer dates */ - fun getDates(): List = returnBlocking { repo.getDates() } + fun getDates(): LiveData> = repo.getDates() /** * Retrieves the opening hours of the canteen as a string. @@ -41,25 +35,24 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { /** * Fetch the canteen offers asynchronously. Updates will be provided through a LiveData object. * - * @param canteen canteen from which to fetch offers from - * @param onRefreshUpdate action to execute when a status update is available - * @param onFinish action to execute when the operation has finished + * @param canteen canteen from which to fetch offers from + * @param onFinish action to execute when the operation has finished + * @param onError action to execute when the operation encountered an error */ - fun fetchOffers(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) { - doAsync { parser.parse(canteen, onRefreshUpdate, onFinish) } + fun fetchOffers( + canteen: Int, + onFinish: () -> Unit, + onError: () -> Unit, + ) { + viewModelScope.launch(Dispatchers.IO) { + parser.parse(canteen, onFinish, onError) + } } fun registerDietaryPreferencesUpdate() { repo.dietaryPreferencesUpdate.postValue(repo.dietaryPreferencesUpdate.value?.plus(1)) } - /** - * Retrieves the amount of dates represented in the offers stored locally. Can be observed. - * - * @return date count as LiveData - */ - fun getDateCount(): LiveData = repo.getDateCount() - /** * Updates a given dietary preference to a new value. * @@ -78,12 +71,6 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { */ fun getPreference(pref: DietaryPreferences): Boolean = repo.getBooleanPreference(pref) - /** - * This value determines whether the user wants to have allergens marked. - */ - val preferenceAllergen: Boolean - get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN, defaultValue = true) - /** * This value determines which canteen the user has selected. */ diff --git a/app/src/main/res/layout/fragment_canteen.xml b/app/src/main/res/layout/fragment_canteen.xml index 90f7421..202005d 100644 --- a/app/src/main/res/layout/fragment_canteen.xml +++ b/app/src/main/res/layout/fragment_canteen.xml @@ -76,6 +76,14 @@ + + Ein Fehler ist aufgetreten! + Mensaplan konnte nicht heruntergeladen werden! Speichern Löschen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cd39ccb..a0c0eb2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,7 +20,8 @@ Something went wrong! - + Couldn\'t retrieve the canteen plan! + Save Delete Cancel