diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3aecceb..a5e3a03 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:supportsRtl="true" android:theme="@style/Theme.StudIPTimetable" tools:targetApi="31"> + + ( } } -fun Fragment.viewBinding(viewBindingFactory: (View) -> T) = +fun Fragment.viewBinding(viewBindingFactory: (View) -> T): FragmentViewBindingDelegate = FragmentViewBindingDelegate(this, viewBindingFactory) \ No newline at end of file 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 2aeea6b..092ce7e 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,8 @@ import android.content.res.Resources import android.util.TypedValue import android.widget.Toast import androidx.annotation.AttrRes -import com.denizk0461.studip.R +import com.denizk0461.studip.exception.AcademicQuarterNotApplicableException +import kotlin.jvm.Throws /** * Miscellaneous functions and variables that don't belong into any specific class. @@ -45,7 +46,9 @@ fun showToast(context: Context, text: String) { * Provides a conversion method between a timestamp ending in a full hour, and one with an academic * quarter applied. */ -val timeslotsAcademicQuarter = listOf( +val timeslotsAcademicQuarter: List = listOf( + "0:00", + "2:00", "4:00", "6:00", "8:00", @@ -56,28 +59,57 @@ val timeslotsAcademicQuarter = listOf( "18:00", "20:00", "22:00", + "24:00", ) -val timeslotsAcademicQuarterStart = mapOf( - "4:00" to "4:15", - "6:00" to "6:15", - "8:00" to "8:15", - "10:00" to "10:15", - "12:00" to "12:15", - "14:00" to "14:15", - "16:00" to "16:15", - "18:00" to "18:15", - "20:00" to "20:15", -) +/** + * Provides a means of converting from a timestamp ending in a full hour to a timestamp that takes + * the academic quarter into account. Should only be used on events where it can be assumed that the + * academic quarter was implied in the timestamp. Use this for the beginning of an event. + * + * @param input timestamp to apply the academic quarter to + * @return timestamp with the academic quarter applied to it + */ +@Throws(AcademicQuarterNotApplicableException::class) +fun getTimestampAcademicQuarterStart(input: String): String = when (input) { + "0:00" -> "0:15" + "2:00" -> "2:15" + "4:00" -> "4:15" + "6:00" -> "6:15" + "8:00" -> "8:15" + "10:00" -> "10:15" + "12:00" -> "12:15" + "14:00" -> "14:15" + "16:00" -> "16:15" + "18:00" -> "18:15" + "20:00" -> "20:15" + "22:00" -> "22:15" + "24:00" -> "0:15" + else -> throw AcademicQuarterNotApplicableException() +} -val timeslotsAcademicQuarterEnd = mapOf( - "6:00" to "5:45", - "8:00" to "7:45", - "10:00" to "9:45", - "12:00" to "11:45", - "14:00" to "13:45", - "16:00" to "15:45", - "18:00" to "17:45", - "20:00" to "19:45", - "22:00" to "21:45", -) \ No newline at end of file +/** + * Provides a means of converting from a timestamp ending in a full hour to a timestamp that takes + * the academic quarter into account. Should only be used on events where it can be assumed that the + * academic quarter was implied in the timestamp. Use this for the end of an event. + * + * @param input timestamp to apply the academic quarter to + * @return timestamp with the academic quarter applied to it + */ +@Throws(AcademicQuarterNotApplicableException::class) +fun getTimestampAcademicQuarterEnd(input: String): String = when (input) { + "0:00" -> "23:45" + "2:00" -> "1:45" + "4:00" -> "3:45" + "6:00" -> "5:45" + "8:00" -> "7:45" + "10:00" -> "9:45" + "12:00" -> "11:45" + "14:00" -> "13:45" + "16:00" -> "15:45" + "18:00" -> "17:45" + "20:00" -> "19:45" + "22:00" -> "21:45" + "24:00" -> "23:45" + else -> throw AcademicQuarterNotApplicableException() +} \ No newline at end of file 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 4807d82..19fc18e 100644 --- a/app/src/main/java/com/denizk0461/studip/data/StwParser.kt +++ b/app/src/main/java/com/denizk0461/studip/data/StwParser.kt @@ -180,7 +180,7 @@ class StwParser { .getElementsByClass("field field-name-field-food-types")[0] // Parse the preferences into a string for the database - val prefString = DietaryPrefObject( + val prefString = DietaryPreferences.Object( isFair = prefs.isDietaryPreferenceMet(imageLinkPrefFair), isFish = prefs.isDietaryPreferenceMet(imageLinkPrefFish), isPoultry = prefs.isDietaryPreferenceMet(imageLinkPrefPoultry), 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 d3afc20..4c86893 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppDatabase.kt @@ -21,7 +21,9 @@ import com.denizk0461.studip.model.* ) abstract class AppDatabase : RoomDatabase() { - // The database access object + /** + * The database access object. Database transactions go through this. + */ abstract fun dao(): AppDAO companion object { 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 30e593e..a03bac2 100644 --- a/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt +++ b/app/src/main/java/com/denizk0461/studip/db/AppRepository.kt @@ -23,7 +23,7 @@ class AppRepository(app: Application) { private val dietaryPrefString = "prefs_obj" // Blank dietary preference regular expression - private val blankDietaryPrefs: String = DietaryPrefObject.C_FALSE.toString().repeat(10) + private val blankDietaryPrefs: String = DietaryPreferences.C_FALSE.toString().repeat(10) /** * Retrieves all Stud.IP events. @@ -49,7 +49,7 @@ class AppRepository(app: Application) { * * @return opening hours */ - fun getCanteenOpeningHours() = dao.getCanteenOpeningHours() + fun getCanteenOpeningHours(): String = dao.getCanteenOpeningHours() /** * Retrieves all canteen offers. @@ -89,16 +89,17 @@ class AppRepository(app: Application) { * * @return dietary preferences as a regular expression string */ - private fun getDietaryPrefs(): String { + private fun getDietaryPrefsAsString(): String { return prefs.getString(dietaryPrefString, blankDietaryPrefs) ?: blankDietaryPrefs } /** - * Retrieve the user's dietary preferences as a DietaryPrefObject. + * Retrieve the user's dietary preferences as a [DietaryPreferences.Object]. * - * @return dietary preferences as an instance of DietaryPrefObject.kt + * @return dietary preferences as an instance of [DietaryPreferences.Object] */ - fun getDietaryPrefsAsObj(): DietaryPrefObject = DietaryPrefObject.construct(getDietaryPrefs()) + fun getDietaryPrefsAsObject(): DietaryPreferences.Object = + DietaryPreferences.construct(getDietaryPrefsAsString()) /** * Updates a given dietary preference to a new value. @@ -107,7 +108,7 @@ class AppRepository(app: Application) { * @param newValue value to set the preference to */ fun setPreference(pref: DietaryPreferences, newValue: Boolean) { - val oldString = getDietaryPrefs().toMutableList() + val oldString = getDietaryPrefsAsString().toMutableList() oldString[pref.ordinal] = newValue.toChar() val newString = oldString.joinToString("") @@ -120,7 +121,11 @@ class AppRepository(app: Application) { * * @return char denoting whether a preference needs to be met */ - private fun Boolean.toChar() = if (this) DietaryPrefObject.C_TRUE else DietaryPrefObject.C_FALSE + private fun Boolean.toChar() = if (this) { + DietaryPreferences.C_TRUE + } else { + DietaryPreferences.C_FALSE + } /** * Retrieves a single user-specified dietary preference. @@ -129,7 +134,7 @@ class AppRepository(app: Application) { * @return whether the preference needs to be met1 */ fun getBooleanPreference(pref: DietaryPreferences): Boolean = - getDietaryPrefs()[pref.ordinal] == DietaryPrefObject.C_TRUE + getDietaryPrefsAsString()[pref.ordinal] == DietaryPreferences.C_TRUE /** * Deletes all canteen offers from the database. diff --git a/app/src/main/java/com/denizk0461/studip/dialog/TimePickerFragment.kt b/app/src/main/java/com/denizk0461/studip/dialog/TimePickerFragment.kt index 87b802d..0cd2ed1 100644 --- a/app/src/main/java/com/denizk0461/studip/dialog/TimePickerFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/dialog/TimePickerFragment.kt @@ -6,6 +6,13 @@ import android.app.TimePickerDialog import android.os.Bundle import android.widget.TimePicker +/** + * Dialogue used for letting the user pick a timestamp. + * + * @param timestamp timestamp that is to be edited + * @param listener listener for when the user finishes setting a time + * @param isEventStart whether the timestamp to be edited is the start of the event + */ class TimePickerFragment( private val timestamp: String, private val listener: OnTimeSetListener, @@ -14,14 +21,36 @@ class TimePickerFragment( override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val timestampSplit = timestamp.split(":") - return TimePickerDialog(activity, this, timestampSplit[0].toInt(), timestampSplit[1].toInt(), true) + return TimePickerDialog( + activity, + this, + timestampSplit[0].toInt(), + timestampSplit[1].toInt(), + true, + ) } + /** + * Custom implementation of TimePickerDialog#OnTimeSetListener that includes a boolean to find + * out whether the start or the end timestamp has been edited. + */ interface OnTimeSetListener { + + /** + * Timestamp has been edited by the user. + * + * @param hours hour of the timestamp + * @param minutes minute of the timestamp + * @param isEventStart whether the timestamp to be edited is the start of the event + */ fun onTimeSet(hours: Int, minutes: Int, isEventStart: Boolean) } + /** + * Override the implementation of TimePickerDialog#OnTimeSetListener. + */ override fun onTimeSet(picker: TimePicker?, hours: Int, minutes: Int) { + // Use custom implementation of OnTimeSetListener listener.onTimeSet(hours, minutes, isEventStart) } } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/exception/AcademicQuarterNotApplicableException.kt b/app/src/main/java/com/denizk0461/studip/exception/AcademicQuarterNotApplicableException.kt new file mode 100644 index 0000000..cdac01e --- /dev/null +++ b/app/src/main/java/com/denizk0461/studip/exception/AcademicQuarterNotApplicableException.kt @@ -0,0 +1,9 @@ +package com.denizk0461.studip.exception + +import java.io.IOException + +/** + * This exception should be used when a conversion to an academic quarter is attempted to be applied + * to a timestamp that is not meant for it. + */ +class AcademicQuarterNotApplicableException : IOException() \ No newline at end of file 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 52655a0..482f689 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/CanteenFragment.kt @@ -108,7 +108,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { // Assign a preference value to every button to filter for dietary preferences val chipMap = mapOf( - binding.chipPrefFair to DietaryPreferences.FAIR, + binding.chipPrefFair to DietaryPreferences.WELFARE, binding.chipPrefFish to DietaryPreferences.FISH, binding.chipPrefPoultry to DietaryPreferences.POULTRY, binding.chipPrefLamb to DietaryPreferences.LAMB, @@ -255,7 +255,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { // Find all preferences that need to be met prefs.forEachIndexed { index, c -> - if (c == DietaryPrefObject.C_TRUE) indices.add(index) + if (c == DietaryPreferences.C_TRUE) indices.add(index) } // Needs to be set to correctly assemble the regular expression @@ -268,7 +268,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { // Add the expression looking for the single preference to the entire string regexString += emptyPreferenceRegex.substring(0 until index) + - DietaryPrefObject.C_TRUE + + DietaryPreferences.C_TRUE + emptyPreferenceRegex.substring(index+1) // Ensure that OR statements will be placed between the individual expressions @@ -290,7 +290,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { // Get dietary preference regex val prefsRegex = getPrefRegex() - val filteredForPreferences = if (prefsRegex.toString() == emptyPreferenceRegex) { + val filteredForPrefs = if (prefsRegex.toString() == emptyPreferenceRegex) { // Show all elements and skip filtering if no preference is set this } else { @@ -300,15 +300,15 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener { } } - return filteredForPreferences.map { offer -> + return filteredForPrefs.map { (_, date, dateId, category, _, canteen, _, _, _, _, _) -> // Create new group elements to group the already filtered elements by their categories CanteenOfferGroup( - offer.date, - offer.dateId, - offer.category, - offer.canteen, - filteredForPreferences.filter { - it.category == offer.category && it.date == offer.date && it.canteen == offer.canteen + date, + dateId, + category, + canteen, + filteredForPrefs.filter { + it.category == category && it.date == date && it.canteen == canteen }.map { // Map the individual items to their respective groups CanteenOfferGroupElement( diff --git a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt index 73f16b1..4eda2ae 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/EventFragment.kt @@ -11,7 +11,6 @@ import com.denizk0461.studip.adapter.StudIPEventPageAdapter import com.denizk0461.studip.databinding.FragmentEventBinding import com.denizk0461.studip.model.StudIPEvent import com.denizk0461.studip.sheet.ScheduleUpdateSheet -import com.denizk0461.studip.sheet.TextSheet import com.denizk0461.studip.viewmodel.EventViewModel import com.google.android.material.tabs.TabLayoutMediator import java.util.* diff --git a/app/src/main/java/com/denizk0461/studip/fragment/SettingsFragment.kt b/app/src/main/java/com/denizk0461/studip/fragment/SettingsFragment.kt index f3d0556..63d7812 100644 --- a/app/src/main/java/com/denizk0461/studip/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/denizk0461/studip/fragment/SettingsFragment.kt @@ -137,7 +137,7 @@ class SettingsFragment : AppFragment() { binding.appVersionText.text = "${BuildConfig.VERSION_NAME}-${ if (BuildConfig.DEBUG) - "dev-[${SimpleDateFormat("yyyy-MM-dd, HH:mm:ss.SSS").format(Date(BuildConfig.TIMESTAMP))}]" + "dev-[${SimpleDateFormat("yyyy-MM-dd, HH:mm:ss.SSS", Locale.GERMANY).format(Date(BuildConfig.TIMESTAMP))}]" else "release" }" diff --git a/app/src/main/java/com/denizk0461/studip/model/Allergens.kt b/app/src/main/java/com/denizk0461/studip/model/Allergens.kt index e1ef3d1..f69dd01 100644 --- a/app/src/main/java/com/denizk0461/studip/model/Allergens.kt +++ b/app/src/main/java/com/denizk0461/studip/model/Allergens.kt @@ -2,7 +2,11 @@ package com.denizk0461.studip.model import com.denizk0461.studip.R +/** + * This class provides static functions for dealing with allergens present in a canteen offer. + */ 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: 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 57d1d09..3619e9a 100644 --- a/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt +++ b/app/src/main/java/com/denizk0461/studip/model/CanteenOffer.kt @@ -18,7 +18,7 @@ import androidx.room.ColumnInfo * @param title text content of the individual canteen offer * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see - * DietaryPrefObject.kt + * [DietaryPreferences] * @param allergens allergens and additives that are present in the item */ data class CanteenOffer( 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 e1e18fe..11cf27b 100644 --- a/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt +++ b/app/src/main/java/com/denizk0461/studip/model/CanteenOfferGroupElement.kt @@ -5,10 +5,10 @@ package com.denizk0461.studip.model * this are assigned to corresponding date, canteen, and category via an instance of * CanteenOfferGroup.kt. * - * @param title text content of the individual canteen offer - * @param price students' price for the individual canteen offer + * @param title text content of the individual canteen offer + * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see - * DietaryPrefObject.kt + * [DietaryPreferences] * @param allergens allergens and additives that are present in the item */ data class CanteenOfferGroupElement( diff --git a/app/src/main/java/com/denizk0461/studip/model/DietaryPrefObject.kt b/app/src/main/java/com/denizk0461/studip/model/DietaryPrefObject.kt deleted file mode 100644 index 3d3e1c9..0000000 --- a/app/src/main/java/com/denizk0461/studip/model/DietaryPrefObject.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.denizk0461.studip.model - -/** - * Object that holds dietary preferences. Can be used to hold both the user-set values as well as - * the values of an individual canteen item. - * - * @param isFair item contains meat from fairly-treated animals (lol sure) - * @param isFish item contains fish - * @param isPoultry item contains chicken - * @param isLamb item contains lamb - * @param isVital i don't actually know - * @param isBeef item contains beef - * @param isPork item contains pork - * @param isVegan item is plant-based; contains no animal-derived ingredients - * @param isVegetarian item is vegetarian; contains no animal parts - * @param isGame item contains game meat (e.g. deer) - */ -data class DietaryPrefObject( - val isFair: Boolean, - val isFish: Boolean, - val isPoultry: Boolean, - val isLamb: Boolean, - val isVital: Boolean, - val isBeef: Boolean, - val isPork: Boolean, - val isVegan: Boolean, - val isVegetarian: Boolean, - val isGame: Boolean, -) { - companion object { - // Char used to construct a regex to denote that a preference is met - const val C_TRUE = 't' - // Char used to construct a regex to denote that a preference is not met - const val C_FALSE = '.' - - /** - * 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 - * false boolean value. - * - * @param values the regex string - * @return an instance of DietaryPrefObject - */ - fun construct(values: String) = DietaryPrefObject( - isFair = values[0] == C_TRUE, - isFish = values[1] == C_TRUE, - isPoultry = values[2] == C_TRUE, - isLamb = values[3] == C_TRUE, - isVital = values[4] == C_TRUE, - isBeef = values[5] == C_TRUE, - isPork = values[6] == C_TRUE, - isVegan = values[7] == C_TRUE, - isVegetarian = values[8] == C_TRUE, - isGame = values[9] == C_TRUE, - ) - } - - /** - * Constructs a regular expression string from a DietaryPrefObject. This can be used to filter for specific - * preferences. Example: - * .......t.. - * This expression filters for items that are vegan ('t' in position 8) and ignores all other - * preference options ('.' in all other positions). - * - * @return a 10-character long regular expression - */ - fun deconstruct() = String( - charArrayOf( - if (this.isFair) C_TRUE else C_FALSE, - if (this.isFish) C_TRUE else C_FALSE, - if (this.isPoultry) C_TRUE else C_FALSE, - if (this.isLamb) C_TRUE else C_FALSE, - if (this.isVital) C_TRUE else C_FALSE, - if (this.isBeef) C_TRUE else C_FALSE, - if (this.isPork) C_TRUE else C_FALSE, - if (this.isVegan) C_TRUE else C_FALSE, - if (this.isVegetarian) C_TRUE else C_FALSE, - if (this.isGame) C_TRUE else C_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 d236961..2c5c536 100644 --- a/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt +++ b/app/src/main/java/com/denizk0461/studip/model/DietaryPreferences.kt @@ -3,35 +3,170 @@ package com.denizk0461.studip.model import com.denizk0461.studip.R /** - * Enumeration that can be used in conjunction with DietaryPrefObject.kt to more concisely look up, - * specify, and iterate through dietary preferences. Order of these elements matches the order used - * on the website of the Studierendenwerk Bremen. + * Enumeration for more concisely looking up, specifying, and iterating through dietary preferences. + * Order of these elements matches the order used on the website of the Studierendenwerk Bremen. * * @param value string value that can be used where the enum value itself cannot be used (e.g. * SharedPreferences) */ enum class DietaryPreferences(val value: String) { - FAIR("isFair"), + /** + * Offer contains meat for which animal welfare standards were upheld. + */ + WELFARE("isFair"), + + /** + * Offer contains fish products. + */ FISH("isFish"), + + /** + * Offer contains poultry (chicken) products. + */ POULTRY("isPoultry"), + + /** + * Offer contains lamb products. + */ LAMB("isLamb"), + + /** + * Offer contains products that are good for ur health i guess? + */ VITAL("isVital"), + + /** + * Offer contains beef (cow) products. + */ BEEF("isBeef"), + + /** + * Offer contains pork (pig) products. + */ PORK("isPork"), + + /** + * Offer contains no animal products and is fully plant-based (vegan). + */ VEGAN("isVegan"), + + /** + * Offer contains no animal parts and is vegetarian. + */ VEGETARIAN("isVegetarian"), + + /** + * Offer contains game (wild animals such as deer, gosh, is that not barbaric? Sorry for being + * dramatic, but I didn't even know that was served at our canteens until I started making this + * app). + */ GAME("isGame"), + + /** + * Offer doesn't fulfil any of the dietary preferences listed above. + */ NONE("hasNone"), + + /** + * Offer has encountered an error. + */ ERROR("onError") ; + /** + * Object that holds dietary preferences. Can be used to hold both the user-set values as well as + * the values of an individual canteen item. + * + * @param isFair item contains meat from fairly-treated animals (lol sure) + * @param isFish item contains fish + * @param isPoultry item contains chicken + * @param isLamb item contains lamb + * @param isVital i don't actually know + * @param isBeef item contains beef + * @param isPork item contains pork + * @param isVegan item is plant-based; contains no animal-derived ingredients + * @param isVegetarian item is vegetarian; contains no animal parts + * @param isGame item contains game meat (e.g. deer) + */ + data class Object( + val isFair: Boolean, + val isFish: Boolean, + val isPoultry: Boolean, + val isLamb: Boolean, + val isVital: Boolean, + val isBeef: Boolean, + val isPork: Boolean, + val isVegan: Boolean, + val isVegetarian: Boolean, + val isGame: Boolean, + ) { + /** + * Constructs a regular expression string from a DietaryPrefObject. This can be used to filter for specific + * preferences. Example: + * .......t.. + * This expression filters for items that are vegan ('t' in position 8) and ignores all other + * preference options ('.' in all other positions). + * + * @return a 10-character long regular expression + */ + fun deconstruct(): String = String( + charArrayOf( + if (this.isFair) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isFish) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isPoultry) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isLamb) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isVital) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isBeef) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isPork) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isVegan) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isVegetarian) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + if (this.isGame) DietaryPreferences.C_TRUE else DietaryPreferences.C_FALSE, + ) + ) + } + companion object { /** - * Provides a converting function between + * Char used to construct a regex to denote that a preference is met + */ + const val C_TRUE: Char = 't' + + /** + * Char used to construct a regex to denote that a preference is not met + */ + const val C_FALSE: Char = '.' + + /** + * 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 + * false boolean value. + * + * @param values the regex string + * @return an instance of DietaryPrefObject + */ + fun construct(values: String): Object = Object( + isFair = values[0] == DietaryPreferences.C_TRUE, + isFish = values[1] == DietaryPreferences.C_TRUE, + isPoultry = values[2] == DietaryPreferences.C_TRUE, + isLamb = values[3] == DietaryPreferences.C_TRUE, + isVital = values[4] == DietaryPreferences.C_TRUE, + isBeef = values[5] == DietaryPreferences.C_TRUE, + isPork = values[6] == DietaryPreferences.C_TRUE, + isVegan = values[7] == DietaryPreferences.C_TRUE, + isVegetarian = values[8] == DietaryPreferences.C_TRUE, + isGame = values[9] == DietaryPreferences.C_TRUE, + ) + + /** + * Provides a converting function between the ordinals of the enumeration and corresponding + * string and drawable values. + * + * @param index ordinal of the enum item + * @return string resource ID and drawable resource ID as a pair */ fun getData(index: Int): Pair = when (index) { - FAIR.ordinal -> Pair(R.string.pref_fair, R.drawable.handshake) + WELFARE.ordinal -> Pair(R.string.pref_fair, R.drawable.handshake) FISH.ordinal -> Pair(R.string.pref_fish, R.drawable.fish) POULTRY.ordinal -> Pair(R.string.pref_poultry, R.drawable.chicken) LAMB.ordinal -> Pair(R.string.pref_lamb, R.drawable.sheep) 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 5b12364..af36d26 100644 --- a/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt +++ b/app/src/main/java/com/denizk0461/studip/model/OfferItem.kt @@ -13,7 +13,7 @@ import androidx.room.PrimaryKey * @param title text content of the individual canteen offer * @param price students' price for the individual canteen offer * @param dietaryPreferences dietary preferences used to filter for the user's needs - see - * DietaryPrefObject.kt + * [DietaryPreferences] * @param allergens allergens and additives that are present in the item */ @Entity( diff --git a/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt b/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt index 85d339c..20a136f 100644 --- a/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt +++ b/app/src/main/java/com/denizk0461/studip/model/SettingsPreferences.kt @@ -7,13 +7,29 @@ package com.denizk0461.studip.model * @param key key for the SharedPreferences transaction */ enum class SettingsPreferences(val key: String) { - // Whether the user wants to have allergens displayed in the canteen overview + + /** + * Whether the user wants to have allergens displayed in the canteen overview. + */ ALLERGEN("setting_allergen"), - // Whether the user wants the next course in his schedule to be highlighted + + /** + * Whether the user wants the next course in his schedule to be highlighted. + */ COURSE_HIGHLIGHTING("setting_highlight"), + /** + * Whether the user wants the app to launch with the canteen fragment. + */ LAUNCH_CANTEEN_ON_START("settings_launch_canteen"), - // Whether the user opts into sending crash report + + /** + * Whether the user opts into sending crash report. + */ DATA_HANDLING("setting_data_handling"), + + /** + * The canteen the user has picked. + */ CANTEEN("setting_canteen"), ; } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/sheet/AppSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/AppSheet.kt index 46ce437..de0cc16 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/AppSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/AppSheet.kt @@ -19,7 +19,9 @@ open class AppSheet(@LayoutRes private val layoutId: Int) : BottomSheetDialogFra // Internal context object private lateinit var _context: Context - // Get non-null context. Only valid after onAttach() + /** + * Get non-null context. Only valid after onAttach(). + */ override fun getContext(): Context = _context override fun onAttach(context: Context) { @@ -27,7 +29,6 @@ open class AppSheet(@LayoutRes private val layoutId: Int) : BottomSheetDialogFra _context = context } - // Inflate the given layout override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = LayoutInflater.from(context).inflate(layoutId, container, false) } \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/studip/sheet/ScheduleUpdateSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/ScheduleUpdateSheet.kt index fa7fae9..2b1ec6b 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/ScheduleUpdateSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/ScheduleUpdateSheet.kt @@ -6,14 +6,15 @@ import android.view.ViewGroup import androidx.fragment.app.FragmentActivity import androidx.transition.TransitionManager import com.denizk0461.studip.R +import com.denizk0461.studip.data.getTimestampAcademicQuarterEnd +import com.denizk0461.studip.data.getTimestampAcademicQuarterStart import com.denizk0461.studip.data.parseToMinutes import com.denizk0461.studip.data.showToast import com.denizk0461.studip.data.timeslotsAcademicQuarter -import com.denizk0461.studip.data.timeslotsAcademicQuarterEnd -import com.denizk0461.studip.data.timeslotsAcademicQuarterStart import com.denizk0461.studip.data.viewBinding import com.denizk0461.studip.databinding.SheetScheduleUpdateBinding import com.denizk0461.studip.dialog.TimePickerFragment +import com.denizk0461.studip.exception.AcademicQuarterNotApplicableException import com.denizk0461.studip.model.StudIPEvent /** @@ -60,10 +61,8 @@ class ScheduleUpdateSheet( binding.buttonAcademicQuarter.setOnClickListener { try { // Retrieve new timestamps with the academic quarter applied, or throw a tantrum - val newStart = timeslotsAcademicQuarterStart[timeslotStart] - ?: throw NullPointerException() - val newEnd = timeslotsAcademicQuarterEnd[timeslotEnd] - ?: throw NullPointerException() + val newStart = getTimestampAcademicQuarterStart(timeslotStart) + val newEnd = getTimestampAcademicQuarterEnd(timeslotEnd) // Set the buttons to the new timesatmps timeslotStart = newStart @@ -79,7 +78,7 @@ class ScheduleUpdateSheet( binding.buttonAcademicQuarter.visibility = View.GONE // Catch if a timeslot couldn't be found in the list, and tell the user - } catch (e: NullPointerException) { + } catch (e: AcademicQuarterNotApplicableException) { showToast(context, getString(R.string.sheet_schedule_update_hint_quarter_error)) } } diff --git a/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt b/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt index 5f2fc75..9879af5 100644 --- a/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt +++ b/app/src/main/java/com/denizk0461/studip/sheet/TextSheet.kt @@ -2,7 +2,6 @@ package com.denizk0461.studip.sheet import android.os.Bundle import android.view.View -import androidx.annotation.StringRes import com.denizk0461.studip.R import com.denizk0461.studip.data.viewBinding import com.denizk0461.studip.databinding.SheetTextBinding diff --git a/app/src/main/java/com/denizk0461/studip/viewmodel/AppViewModel.kt b/app/src/main/java/com/denizk0461/studip/viewmodel/AppViewModel.kt index 7d77b20..5dc9ef6 100644 --- a/app/src/main/java/com/denizk0461/studip/viewmodel/AppViewModel.kt +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/AppViewModel.kt @@ -18,7 +18,9 @@ import kotlinx.coroutines.runBlocking */ open class AppViewModel(app: Application) : AndroidViewModel(app) { - // Reference to the app's repository for database transactions + /** + * Reference to the app's repository for database transactions. + */ protected val repo: AppRepository = Dependencies.repo /** 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 f617da4..2a4ac1c 100644 --- a/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt +++ b/app/src/main/java/com/denizk0461/studip/viewmodel/CanteenViewModel.kt @@ -27,7 +27,7 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { * * @return dietary preferences */ - fun getDietaryPrefs(): DietaryPrefObject = repo.getDietaryPrefsAsObj() + fun getDietaryPrefs(): DietaryPreferences.Object = repo.getDietaryPrefsAsObject() /** * Retrieves all canteen offer date objects. @@ -43,7 +43,13 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { */ fun getCanteenOpeningHours(): String = returnBlocking { repo.getCanteenOpeningHours() } - // Fetch the canteen offers asynchronously. Updates will be provided through a LiveData object. + /** + * 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 + */ fun fetchOffers(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) { doAsync { parser.parse(canteen, onRefreshUpdate, onFinish) } } @@ -72,6 +78,9 @@ class CanteenViewModel(app: Application) : AppViewModel(app) { val preferenceAllergen: Boolean get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN, defaultValue = true) + /** + * This value determines which canteen the user has selected. + */ var preferenceCanteen: Int get() = repo.getIntPreference(SettingsPreferences.CANTEEN) set(newValue) { repo.setPreference(SettingsPreferences.CANTEEN, newValue) } diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/flatware.xml b/app/src/main/res/drawable/flatware.xml deleted file mode 100644 index e21135a..0000000 --- a/app/src/main/res/drawable/flatware.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..7e9689c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_canteen.xml b/app/src/main/res/layout/fragment_canteen.xml index 2a29546..90f7421 100644 --- a/app/src/main/res/layout/fragment_canteen.xml +++ b/app/src/main/res/layout/fragment_canteen.xml @@ -36,7 +36,6 @@ style="@style/Widget.Material3.Button.TextButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="hello" android:textSize="18sp" android:fontFamily="@font/lato_bolditalic" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/item_canteen_line.xml b/app/src/main/res/layout/item_canteen_line.xml index 6ed7399..5137b78 100644 --- a/app/src/main/res/layout/item_canteen_line.xml +++ b/app/src/main/res/layout/item_canteen_line.xml @@ -7,8 +7,7 @@ android:clickable="true" android:focusable="true" android:paddingHorizontal="16dp" - android:paddingVertical="2dp" - android:background="?android:attr/selectableItemBackground"> + android:paddingVertical="2dp"> - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cf..7353dbd 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml deleted file mode 100644 index 6f3b755..0000000 --- a/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..c57cfd7 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..c57cfd7 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d..0000000 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..cea0c83 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d6..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..cea0c83 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611d..0000000 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..8028973 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a307..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8028973 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a695..0000000 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..b03f865 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..b03f865 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f50..0000000 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..9fe7b4b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d642..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..9fe7b4b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae3..0000000 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/app/src/main/res/values-v29/themes.xml b/app/src/main/res/values-v29/themes.xml index fe5092b..5b2a3a2 100644 --- a/app/src/main/res/values-v29/themes.xml +++ b/app/src/main/res/values-v29/themes.xml @@ -1,5 +1,4 @@ - - +