Made further documentation and safety changes. Scraper classes (StudIPParser.kt, StwParser.kt) still need to be checked for safety.
@@ -14,6 +14,7 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.StudIPTimetable"
|
android:theme="@style/Theme.StudIPTimetable"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.MainActivity"
|
android:name=".activity.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.FetcherActivity"
|
android:name=".activity.FetcherActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -3,7 +3,6 @@ package com.denizk0461.studip.activity
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.TypedValue
|
|
||||||
import android.webkit.WebResourceRequest
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
@@ -29,7 +28,7 @@ class FetcherActivity : Activity() {
|
|||||||
// View model
|
// View model
|
||||||
private lateinit var viewModel: FetcherViewModel
|
private lateinit var viewModel: FetcherViewModel
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Enabling JavaScript is necessary to get the HTML source in this context. As the HTML can only
|
* Enabling JavaScript is necessary to get the HTML source in this context. As the HTML can only
|
||||||
* be accessed once the user has logged in, a simple HTML request would not suffice here.
|
* be accessed once the user has logged in, a simple HTML request would not suffice here.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -45,5 +45,5 @@ class FragmentViewBindingDelegate<T : ViewBinding>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T) =
|
fun <T : ViewBinding> Fragment.viewBinding(viewBindingFactory: (View) -> T): FragmentViewBindingDelegate<T> =
|
||||||
FragmentViewBindingDelegate(this, viewBindingFactory)
|
FragmentViewBindingDelegate(this, viewBindingFactory)
|
||||||
@@ -5,7 +5,8 @@ import android.content.res.Resources
|
|||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.AttrRes
|
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.
|
* 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
|
* Provides a conversion method between a timestamp ending in a full hour, and one with an academic
|
||||||
* quarter applied.
|
* quarter applied.
|
||||||
*/
|
*/
|
||||||
val timeslotsAcademicQuarter = listOf(
|
val timeslotsAcademicQuarter: List<String> = listOf(
|
||||||
|
"0:00",
|
||||||
|
"2:00",
|
||||||
"4:00",
|
"4:00",
|
||||||
"6:00",
|
"6:00",
|
||||||
"8:00",
|
"8:00",
|
||||||
@@ -56,28 +59,57 @@ val timeslotsAcademicQuarter = listOf(
|
|||||||
"18:00",
|
"18:00",
|
||||||
"20:00",
|
"20:00",
|
||||||
"22:00",
|
"22:00",
|
||||||
|
"24:00",
|
||||||
)
|
)
|
||||||
|
|
||||||
val timeslotsAcademicQuarterStart = mapOf(
|
/**
|
||||||
"4:00" to "4:15",
|
* Provides a means of converting from a timestamp ending in a full hour to a timestamp that takes
|
||||||
"6:00" to "6:15",
|
* the academic quarter into account. Should only be used on events where it can be assumed that the
|
||||||
"8:00" to "8:15",
|
* academic quarter was implied in the timestamp. Use this for the beginning of an event.
|
||||||
"10:00" to "10:15",
|
*
|
||||||
"12:00" to "12:15",
|
* @param input timestamp to apply the academic quarter to
|
||||||
"14:00" to "14:15",
|
* @return timestamp with the academic quarter applied to it
|
||||||
"16:00" to "16:15",
|
*/
|
||||||
"18:00" to "18:15",
|
@Throws(AcademicQuarterNotApplicableException::class)
|
||||||
"20:00" to "20:15",
|
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",
|
* Provides a means of converting from a timestamp ending in a full hour to a timestamp that takes
|
||||||
"8:00" to "7:45",
|
* the academic quarter into account. Should only be used on events where it can be assumed that the
|
||||||
"10:00" to "9:45",
|
* academic quarter was implied in the timestamp. Use this for the end of an event.
|
||||||
"12:00" to "11:45",
|
*
|
||||||
"14:00" to "13:45",
|
* @param input timestamp to apply the academic quarter to
|
||||||
"16:00" to "15:45",
|
* @return timestamp with the academic quarter applied to it
|
||||||
"18:00" to "17:45",
|
*/
|
||||||
"20:00" to "19:45",
|
@Throws(AcademicQuarterNotApplicableException::class)
|
||||||
"22:00" to "21:45",
|
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()
|
||||||
|
}
|
||||||
@@ -180,7 +180,7 @@ class StwParser {
|
|||||||
.getElementsByClass("field field-name-field-food-types")[0]
|
.getElementsByClass("field field-name-field-food-types")[0]
|
||||||
|
|
||||||
// Parse the preferences into a string for the database
|
// Parse the preferences into a string for the database
|
||||||
val prefString = DietaryPrefObject(
|
val prefString = DietaryPreferences.Object(
|
||||||
isFair = prefs.isDietaryPreferenceMet(imageLinkPrefFair),
|
isFair = prefs.isDietaryPreferenceMet(imageLinkPrefFair),
|
||||||
isFish = prefs.isDietaryPreferenceMet(imageLinkPrefFish),
|
isFish = prefs.isDietaryPreferenceMet(imageLinkPrefFish),
|
||||||
isPoultry = prefs.isDietaryPreferenceMet(imageLinkPrefPoultry),
|
isPoultry = prefs.isDietaryPreferenceMet(imageLinkPrefPoultry),
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import com.denizk0461.studip.model.*
|
|||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
// The database access object
|
/**
|
||||||
|
* The database access object. Database transactions go through this.
|
||||||
|
*/
|
||||||
abstract fun dao(): AppDAO
|
abstract fun dao(): AppDAO
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class AppRepository(app: Application) {
|
|||||||
private val dietaryPrefString = "prefs_obj"
|
private val dietaryPrefString = "prefs_obj"
|
||||||
|
|
||||||
// Blank dietary preference regular expression
|
// 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.
|
* Retrieves all Stud.IP events.
|
||||||
@@ -49,7 +49,7 @@ class AppRepository(app: Application) {
|
|||||||
*
|
*
|
||||||
* @return opening hours
|
* @return opening hours
|
||||||
*/
|
*/
|
||||||
fun getCanteenOpeningHours() = dao.getCanteenOpeningHours()
|
fun getCanteenOpeningHours(): String = dao.getCanteenOpeningHours()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all canteen offers.
|
* Retrieves all canteen offers.
|
||||||
@@ -89,16 +89,17 @@ class AppRepository(app: Application) {
|
|||||||
*
|
*
|
||||||
* @return dietary preferences as a regular expression string
|
* @return dietary preferences as a regular expression string
|
||||||
*/
|
*/
|
||||||
private fun getDietaryPrefs(): String {
|
private fun getDietaryPrefsAsString(): String {
|
||||||
return prefs.getString(dietaryPrefString, blankDietaryPrefs) ?: blankDietaryPrefs
|
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.
|
* 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
|
* @param newValue value to set the preference to
|
||||||
*/
|
*/
|
||||||
fun setPreference(pref: DietaryPreferences, newValue: Boolean) {
|
fun setPreference(pref: DietaryPreferences, newValue: Boolean) {
|
||||||
val oldString = getDietaryPrefs().toMutableList()
|
val oldString = getDietaryPrefsAsString().toMutableList()
|
||||||
|
|
||||||
oldString[pref.ordinal] = newValue.toChar()
|
oldString[pref.ordinal] = newValue.toChar()
|
||||||
val newString = oldString.joinToString("")
|
val newString = oldString.joinToString("")
|
||||||
@@ -120,7 +121,11 @@ class AppRepository(app: Application) {
|
|||||||
*
|
*
|
||||||
* @return char denoting whether a preference needs to be met
|
* @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.
|
* Retrieves a single user-specified dietary preference.
|
||||||
@@ -129,7 +134,7 @@ class AppRepository(app: Application) {
|
|||||||
* @return whether the preference needs to be met1
|
* @return whether the preference needs to be met1
|
||||||
*/
|
*/
|
||||||
fun getBooleanPreference(pref: DietaryPreferences): Boolean =
|
fun getBooleanPreference(pref: DietaryPreferences): Boolean =
|
||||||
getDietaryPrefs()[pref.ordinal] == DietaryPrefObject.C_TRUE
|
getDietaryPrefsAsString()[pref.ordinal] == DietaryPreferences.C_TRUE
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes all canteen offers from the database.
|
* Deletes all canteen offers from the database.
|
||||||
|
|||||||
@@ -6,6 +6,13 @@ import android.app.TimePickerDialog
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.TimePicker
|
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(
|
class TimePickerFragment(
|
||||||
private val timestamp: String,
|
private val timestamp: String,
|
||||||
private val listener: OnTimeSetListener,
|
private val listener: OnTimeSetListener,
|
||||||
@@ -14,14 +21,36 @@ class TimePickerFragment(
|
|||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val timestampSplit = timestamp.split(":")
|
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 {
|
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)
|
fun onTimeSet(hours: Int, minutes: Int, isEventStart: Boolean)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the implementation of TimePickerDialog#OnTimeSetListener.
|
||||||
|
*/
|
||||||
override fun onTimeSet(picker: TimePicker?, hours: Int, minutes: Int) {
|
override fun onTimeSet(picker: TimePicker?, hours: Int, minutes: Int) {
|
||||||
|
// Use custom implementation of OnTimeSetListener
|
||||||
listener.onTimeSet(hours, minutes, isEventStart)
|
listener.onTimeSet(hours, minutes, isEventStart)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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()
|
||||||
@@ -108,7 +108,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
|
|||||||
|
|
||||||
// Assign a preference value to every button to filter for dietary preferences
|
// Assign a preference value to every button to filter for dietary preferences
|
||||||
val chipMap = mapOf(
|
val chipMap = mapOf(
|
||||||
binding.chipPrefFair to DietaryPreferences.FAIR,
|
binding.chipPrefFair to DietaryPreferences.WELFARE,
|
||||||
binding.chipPrefFish to DietaryPreferences.FISH,
|
binding.chipPrefFish to DietaryPreferences.FISH,
|
||||||
binding.chipPrefPoultry to DietaryPreferences.POULTRY,
|
binding.chipPrefPoultry to DietaryPreferences.POULTRY,
|
||||||
binding.chipPrefLamb to DietaryPreferences.LAMB,
|
binding.chipPrefLamb to DietaryPreferences.LAMB,
|
||||||
@@ -255,7 +255,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
|
|||||||
|
|
||||||
// Find all preferences that need to be met
|
// Find all preferences that need to be met
|
||||||
prefs.forEachIndexed { index, c ->
|
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
|
// 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
|
// Add the expression looking for the single preference to the entire string
|
||||||
regexString += emptyPreferenceRegex.substring(0 until index) +
|
regexString += emptyPreferenceRegex.substring(0 until index) +
|
||||||
DietaryPrefObject.C_TRUE +
|
DietaryPreferences.C_TRUE +
|
||||||
emptyPreferenceRegex.substring(index+1)
|
emptyPreferenceRegex.substring(index+1)
|
||||||
|
|
||||||
// Ensure that OR statements will be placed between the individual expressions
|
// Ensure that OR statements will be placed between the individual expressions
|
||||||
@@ -290,7 +290,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
|
|||||||
// Get dietary preference regex
|
// Get dietary preference regex
|
||||||
val prefsRegex = getPrefRegex()
|
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
|
// Show all elements and skip filtering if no preference is set
|
||||||
this
|
this
|
||||||
} else {
|
} 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
|
// Create new group elements to group the already filtered elements by their categories
|
||||||
CanteenOfferGroup(
|
CanteenOfferGroup(
|
||||||
offer.date,
|
date,
|
||||||
offer.dateId,
|
dateId,
|
||||||
offer.category,
|
category,
|
||||||
offer.canteen,
|
canteen,
|
||||||
filteredForPreferences.filter {
|
filteredForPrefs.filter {
|
||||||
it.category == offer.category && it.date == offer.date && it.canteen == offer.canteen
|
it.category == category && it.date == date && it.canteen == canteen
|
||||||
}.map {
|
}.map {
|
||||||
// Map the individual items to their respective groups
|
// Map the individual items to their respective groups
|
||||||
CanteenOfferGroupElement(
|
CanteenOfferGroupElement(
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import com.denizk0461.studip.adapter.StudIPEventPageAdapter
|
|||||||
import com.denizk0461.studip.databinding.FragmentEventBinding
|
import com.denizk0461.studip.databinding.FragmentEventBinding
|
||||||
import com.denizk0461.studip.model.StudIPEvent
|
import com.denizk0461.studip.model.StudIPEvent
|
||||||
import com.denizk0461.studip.sheet.ScheduleUpdateSheet
|
import com.denizk0461.studip.sheet.ScheduleUpdateSheet
|
||||||
import com.denizk0461.studip.sheet.TextSheet
|
|
||||||
import com.denizk0461.studip.viewmodel.EventViewModel
|
import com.denizk0461.studip.viewmodel.EventViewModel
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class SettingsFragment : AppFragment() {
|
|||||||
binding.appVersionText.text =
|
binding.appVersionText.text =
|
||||||
"${BuildConfig.VERSION_NAME}-${
|
"${BuildConfig.VERSION_NAME}-${
|
||||||
if (BuildConfig.DEBUG)
|
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
|
else
|
||||||
"release"
|
"release"
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ package com.denizk0461.studip.model
|
|||||||
|
|
||||||
import com.denizk0461.studip.R
|
import com.denizk0461.studip.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides static functions for dealing with allergens present in a canteen offer.
|
||||||
|
*/
|
||||||
object Allergens {
|
object Allergens {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an allergen into the string ID for its localised full-text version. Elements are
|
* Converts an allergen into the string ID for its localised full-text version. Elements are
|
||||||
* derived from the website of the Studierendenwerk Bremen:
|
* derived from the website of the Studierendenwerk Bremen:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import androidx.room.ColumnInfo
|
|||||||
* @param title text content of the individual canteen offer
|
* @param title text content of the individual canteen offer
|
||||||
* @param price students' price for 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
|
* @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
|
* @param allergens allergens and additives that are present in the item
|
||||||
*/
|
*/
|
||||||
data class CanteenOffer(
|
data class CanteenOffer(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ package com.denizk0461.studip.model
|
|||||||
* @param title text content of the individual canteen offer
|
* @param title text content of the individual canteen offer
|
||||||
* @param price students' price for 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
|
* @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
|
* @param allergens allergens and additives that are present in the item
|
||||||
*/
|
*/
|
||||||
data class CanteenOfferGroupElement(
|
data class CanteenOfferGroupElement(
|
||||||
|
|||||||
@@ -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,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -3,35 +3,170 @@ package com.denizk0461.studip.model
|
|||||||
import com.denizk0461.studip.R
|
import com.denizk0461.studip.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration that can be used in conjunction with DietaryPrefObject.kt to more concisely look up,
|
* Enumeration for more concisely looking up, specifying, and iterating through dietary preferences.
|
||||||
* specify, and iterate through dietary preferences. Order of these elements matches the order used
|
* Order of these elements matches the order used on the website of the Studierendenwerk Bremen.
|
||||||
* 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.
|
* @param value string value that can be used where the enum value itself cannot be used (e.g.
|
||||||
* SharedPreferences)
|
* SharedPreferences)
|
||||||
*/
|
*/
|
||||||
enum class DietaryPreferences(val value: String) {
|
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"),
|
FISH("isFish"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains poultry (chicken) products.
|
||||||
|
*/
|
||||||
POULTRY("isPoultry"),
|
POULTRY("isPoultry"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains lamb products.
|
||||||
|
*/
|
||||||
LAMB("isLamb"),
|
LAMB("isLamb"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains products that are good for ur health i guess?
|
||||||
|
*/
|
||||||
VITAL("isVital"),
|
VITAL("isVital"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains beef (cow) products.
|
||||||
|
*/
|
||||||
BEEF("isBeef"),
|
BEEF("isBeef"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains pork (pig) products.
|
||||||
|
*/
|
||||||
PORK("isPork"),
|
PORK("isPork"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains no animal products and is fully plant-based (vegan).
|
||||||
|
*/
|
||||||
VEGAN("isVegan"),
|
VEGAN("isVegan"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer contains no animal parts and is vegetarian.
|
||||||
|
*/
|
||||||
VEGETARIAN("isVegetarian"),
|
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"),
|
GAME("isGame"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer doesn't fulfil any of the dietary preferences listed above.
|
||||||
|
*/
|
||||||
NONE("hasNone"),
|
NONE("hasNone"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer has encountered an error.
|
||||||
|
*/
|
||||||
ERROR("onError")
|
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 {
|
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<Int, Int> = when (index) {
|
fun getData(index: Int): Pair<Int, Int> = 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)
|
FISH.ordinal -> Pair(R.string.pref_fish, R.drawable.fish)
|
||||||
POULTRY.ordinal -> Pair(R.string.pref_poultry, R.drawable.chicken)
|
POULTRY.ordinal -> Pair(R.string.pref_poultry, R.drawable.chicken)
|
||||||
LAMB.ordinal -> Pair(R.string.pref_lamb, R.drawable.sheep)
|
LAMB.ordinal -> Pair(R.string.pref_lamb, R.drawable.sheep)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import androidx.room.PrimaryKey
|
|||||||
* @param title text content of the individual canteen offer
|
* @param title text content of the individual canteen offer
|
||||||
* @param price students' price for 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
|
* @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
|
* @param allergens allergens and additives that are present in the item
|
||||||
*/
|
*/
|
||||||
@Entity(
|
@Entity(
|
||||||
|
|||||||
@@ -7,13 +7,29 @@ package com.denizk0461.studip.model
|
|||||||
* @param key key for the SharedPreferences transaction
|
* @param key key for the SharedPreferences transaction
|
||||||
*/
|
*/
|
||||||
enum class SettingsPreferences(val key: String) {
|
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"),
|
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"),
|
COURSE_HIGHLIGHTING("setting_highlight"),
|
||||||
|
/**
|
||||||
|
* Whether the user wants the app to launch with the canteen fragment.
|
||||||
|
*/
|
||||||
LAUNCH_CANTEEN_ON_START("settings_launch_canteen"),
|
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"),
|
DATA_HANDLING("setting_data_handling"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The canteen the user has picked.
|
||||||
|
*/
|
||||||
CANTEEN("setting_canteen"),
|
CANTEEN("setting_canteen"),
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,9 @@ open class AppSheet(@LayoutRes private val layoutId: Int) : BottomSheetDialogFra
|
|||||||
// Internal context object
|
// Internal context object
|
||||||
private lateinit var _context: Context
|
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 getContext(): Context = _context
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
@@ -27,7 +29,6 @@ open class AppSheet(@LayoutRes private val layoutId: Int) : BottomSheetDialogFra
|
|||||||
_context = context
|
_context = context
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inflate the given layout
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
|
||||||
LayoutInflater.from(context).inflate(layoutId, container, false)
|
LayoutInflater.from(context).inflate(layoutId, container, false)
|
||||||
}
|
}
|
||||||
@@ -6,14 +6,15 @@ import android.view.ViewGroup
|
|||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import com.denizk0461.studip.R
|
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.parseToMinutes
|
||||||
import com.denizk0461.studip.data.showToast
|
import com.denizk0461.studip.data.showToast
|
||||||
import com.denizk0461.studip.data.timeslotsAcademicQuarter
|
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.data.viewBinding
|
||||||
import com.denizk0461.studip.databinding.SheetScheduleUpdateBinding
|
import com.denizk0461.studip.databinding.SheetScheduleUpdateBinding
|
||||||
import com.denizk0461.studip.dialog.TimePickerFragment
|
import com.denizk0461.studip.dialog.TimePickerFragment
|
||||||
|
import com.denizk0461.studip.exception.AcademicQuarterNotApplicableException
|
||||||
import com.denizk0461.studip.model.StudIPEvent
|
import com.denizk0461.studip.model.StudIPEvent
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,10 +61,8 @@ class ScheduleUpdateSheet(
|
|||||||
binding.buttonAcademicQuarter.setOnClickListener {
|
binding.buttonAcademicQuarter.setOnClickListener {
|
||||||
try {
|
try {
|
||||||
// Retrieve new timestamps with the academic quarter applied, or throw a tantrum
|
// Retrieve new timestamps with the academic quarter applied, or throw a tantrum
|
||||||
val newStart = timeslotsAcademicQuarterStart[timeslotStart]
|
val newStart = getTimestampAcademicQuarterStart(timeslotStart)
|
||||||
?: throw NullPointerException()
|
val newEnd = getTimestampAcademicQuarterEnd(timeslotEnd)
|
||||||
val newEnd = timeslotsAcademicQuarterEnd[timeslotEnd]
|
|
||||||
?: throw NullPointerException()
|
|
||||||
|
|
||||||
// Set the buttons to the new timesatmps
|
// Set the buttons to the new timesatmps
|
||||||
timeslotStart = newStart
|
timeslotStart = newStart
|
||||||
@@ -79,7 +78,7 @@ class ScheduleUpdateSheet(
|
|||||||
binding.buttonAcademicQuarter.visibility = View.GONE
|
binding.buttonAcademicQuarter.visibility = View.GONE
|
||||||
|
|
||||||
// Catch if a timeslot couldn't be found in the list, and tell the user
|
// 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))
|
showToast(context, getString(R.string.sheet_schedule_update_hint_quarter_error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package com.denizk0461.studip.sheet
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import com.denizk0461.studip.R
|
import com.denizk0461.studip.R
|
||||||
import com.denizk0461.studip.data.viewBinding
|
import com.denizk0461.studip.data.viewBinding
|
||||||
import com.denizk0461.studip.databinding.SheetTextBinding
|
import com.denizk0461.studip.databinding.SheetTextBinding
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ import kotlinx.coroutines.runBlocking
|
|||||||
*/
|
*/
|
||||||
open class AppViewModel(app: Application) : AndroidViewModel(app) {
|
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
|
protected val repo: AppRepository = Dependencies.repo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class CanteenViewModel(app: Application) : AppViewModel(app) {
|
|||||||
*
|
*
|
||||||
* @return dietary preferences
|
* @return dietary preferences
|
||||||
*/
|
*/
|
||||||
fun getDietaryPrefs(): DietaryPrefObject = repo.getDietaryPrefsAsObj()
|
fun getDietaryPrefs(): DietaryPreferences.Object = repo.getDietaryPrefsAsObject()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves all canteen offer date objects.
|
* Retrieves all canteen offer date objects.
|
||||||
@@ -43,7 +43,13 @@ class CanteenViewModel(app: Application) : AppViewModel(app) {
|
|||||||
*/
|
*/
|
||||||
fun getCanteenOpeningHours(): String = returnBlocking { repo.getCanteenOpeningHours() }
|
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) {
|
fun fetchOffers(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) {
|
||||||
doAsync { parser.parse(canteen, onRefreshUpdate, onFinish) }
|
doAsync { parser.parse(canteen, onRefreshUpdate, onFinish) }
|
||||||
}
|
}
|
||||||
@@ -72,6 +78,9 @@ class CanteenViewModel(app: Application) : AppViewModel(app) {
|
|||||||
val preferenceAllergen: Boolean
|
val preferenceAllergen: Boolean
|
||||||
get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN, defaultValue = true)
|
get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN, defaultValue = true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This value determines which canteen the user has selected.
|
||||||
|
*/
|
||||||
var preferenceCanteen: Int
|
var preferenceCanteen: Int
|
||||||
get() = repo.getIntPreference(SettingsPreferences.CANTEEN)
|
get() = repo.getIntPreference(SettingsPreferences.CANTEEN)
|
||||||
set(newValue) { repo.setPreference(SettingsPreferences.CANTEEN, newValue) }
|
set(newValue) { repo.setPreference(SettingsPreferences.CANTEEN, newValue) }
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:aapt="http://schemas.android.com/aapt"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
|
||||||
<aapt:attr name="android:fillColor">
|
|
||||||
<gradient
|
|
||||||
android:endX="85.84757"
|
|
||||||
android:endY="92.4963"
|
|
||||||
android:startX="42.9492"
|
|
||||||
android:startY="49.59793"
|
|
||||||
android:type="linear">
|
|
||||||
<item
|
|
||||||
android:color="#44000000"
|
|
||||||
android:offset="0.0" />
|
|
||||||
<item
|
|
||||||
android:color="#00000000"
|
|
||||||
android:offset="1.0" />
|
|
||||||
</gradient>
|
|
||||||
</aapt:attr>
|
|
||||||
</path>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FFFFFF"
|
|
||||||
android:fillType="nonZero"
|
|
||||||
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
|
||||||
android:strokeWidth="1"
|
|
||||||
android:strokeColor="#00000000" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#000000"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M16,7.08c0,1.77 -0.84,3.25 -2,3.82V21h-2V10.9c-1.16,-0.57 -2,-2.05 -2,-3.82C10.01,4.83 11.35,3 13,3C14.66,3 16,4.83 16,7.08zM17,3v18h2v-8h2V7C21,5.24 19.76,3 17,3zM8.28,3c-0.4,0 -0.72,0.32 -0.72,0.72V7H6.72V3.72C6.72,3.32 6.4,3 6,3S5.28,3.32 5.28,3.72V7H4.44V3.72C4.44,3.32 4.12,3 3.72,3S3,3.32 3,3.72V9c0,1.1 0.9,2 2,2v10h2V11c1.1,0 2,-0.9 2,-2V3.72C9,3.32 8.68,3 8.28,3z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -36,7 +36,6 @@
|
|||||||
style="@style/Widget.Material3.Button.TextButton"
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="hello"
|
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:fontFamily="@font/lato_bolditalic"
|
android:fontFamily="@font/lato_bolditalic"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:paddingVertical="2dp"
|
android:paddingVertical="2dp">
|
||||||
android:background="?android:attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
android:id="@+id/image_view_container"
|
android:id="@+id/image_view_container"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
</adaptive-icon>
|
</adaptive-icon>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@drawable/ic_launcher_background" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
|
||||||
</adaptive-icon>
|
|
||||||
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
@@ -1,5 +1,4 @@
|
|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources>
|
||||||
|
|
||||||
<style name="Theme.StudIPTimetable" parent="Base.Theme.StudIPTimetable">
|
<style name="Theme.StudIPTimetable" parent="Base.Theme.StudIPTimetable">
|
||||||
<!-- Transparent system bars for edge-to-edge. -->
|
<!-- Transparent system bars for edge-to-edge. -->
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#A33D2F</color>
|
||||||
|
</resources>
|
||||||
@@ -126,9 +126,6 @@
|
|||||||
<string name="settings_highlight_title">Highlight next course</string>
|
<string name="settings_highlight_title">Highlight next course</string>
|
||||||
<string name="settings_highlight_subtitle">Only works properly if courses don\'t overlap!</string>
|
<string name="settings_highlight_subtitle">Only works properly if courses don\'t overlap!</string>
|
||||||
|
|
||||||
<string name="settings_quarter_title">Use academic quarter</string>
|
|
||||||
<string name="settings_quarter_subtitle">Where applicable</string>
|
|
||||||
|
|
||||||
<string name="settings_allergens_title">Mark offers with allergens with a star*</string>
|
<string name="settings_allergens_title">Mark offers with allergens with a star*</string>
|
||||||
<string name="settings_allergens_subtitle">Also applies to additives</string>
|
<string name="settings_allergens_subtitle">Also applies to additives</string>
|
||||||
|
|
||||||
|
|||||||