Implemented functionality to switch canteens. UI for this is not fully implemented.

This commit is contained in:
denizk0461
2023-04-25 15:38:30 +02:00
parent 840c8f1db7
commit 6b1d77dfb3
21 changed files with 363 additions and 106 deletions
@@ -11,6 +11,7 @@ import android.widget.Toast
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.denizk0461.studip.R import com.denizk0461.studip.R
import com.denizk0461.studip.data.StudIPParser import com.denizk0461.studip.data.StudIPParser
import com.denizk0461.studip.data.getThemedColor
import com.denizk0461.studip.databinding.ActivityFetcherBinding import com.denizk0461.studip.databinding.ActivityFetcherBinding
import com.denizk0461.studip.viewmodel.FetcherViewModel import com.denizk0461.studip.viewmodel.FetcherViewModel
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@@ -86,16 +87,6 @@ class FetcherActivity : Activity() {
} }
} catch (e: IOException) { } catch (e: IOException) {
// Set up error colours
val colorErrorContainer = TypedValue()
val colorOnErrorContainer = TypedValue()
// Resolve themed attributes to get the right values for day and night modes
theme.apply {
resolveAttribute(R.attr.colorErrorContainer, colorErrorContainer, true)
resolveAttribute(R.attr.colorOnErrorContainer, colorOnErrorContainer, true)
}
// Let the user know that an error occurred // Let the user know that an error occurred
Snackbar Snackbar
.make( .make(
@@ -104,8 +95,8 @@ class FetcherActivity : Activity() {
Snackbar.LENGTH_SHORT Snackbar.LENGTH_SHORT
) )
// Set colours to signify an error // Set colours to signify an error
.setBackgroundTint(colorErrorContainer.data) .setBackgroundTint(theme.getThemedColor(R.attr.colorErrorContainer))
.setTextColor(colorOnErrorContainer.data) .setTextColor(theme.getThemedColor(R.attr.colorOnErrorContainer))
.show() .show()
} }
} }
@@ -75,11 +75,12 @@ class MainActivity : AppCompatActivity() {
// Prepare and commit a transaction between the current and the next fragment // Prepare and commit a transaction between the current and the next fragment
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.nav_host_fragment_content_main, fragment, id.toString()) .replace(R.id.nav_host_fragment_content_main, fragment, id.toString())
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
// .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.commit() .commit()
// Set text of the app bar accordingly // Set text of the app bar accordingly
binding.contentMain.appTitleBar.text = getTitleString(id) // binding.contentMain.appTitleBar.text = getTitleString(id)
// Note down the new current fragment // Note down the new current fragment
currentFragment = id currentFragment = id
@@ -1,8 +1,11 @@
package com.denizk0461.studip.adapter package com.denizk0461.studip.adapter
import android.content.res.ColorStateList
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.R
import com.denizk0461.studip.data.getThemedColor
import com.denizk0461.studip.data.parseToMinutes import com.denizk0461.studip.data.parseToMinutes
import com.denizk0461.studip.model.StudIPEvent import com.denizk0461.studip.model.StudIPEvent
import com.denizk0461.studip.databinding.ItemEventBinding import com.denizk0461.studip.databinding.ItemEventBinding
@@ -83,30 +86,32 @@ class StudIPEventItemAdapter(
// resolveAttribute(R.attr.colorTextHintLighter, colorTextHintLighter, true) // resolveAttribute(R.attr.colorTextHintLighter, colorTextHintLighter, true)
// } // }
val theme = holder.binding.root.context.theme
/* Highlight the next upcoming course of the day if the day of the adapter matches the /* Highlight the next upcoming course of the day if the day of the adapter matches the
* current day, and if no other course has been highlighted, to avoid double highlighting. * current day, and if no other course has been highlighted, to avoid double highlighting.
*/ */
// if (!isAnyCourseHighlighted && currentItem.isCurrentCourse(currentCalendar)) { if (!isAnyCourseHighlighted && currentItem.isCurrentCourse(currentCalendar)) {
// // Ensure that no other course will be highlighted // Ensure that no other course will be highlighted
// isAnyCourseHighlighted = true isAnyCourseHighlighted = true
//
// holder.binding.cardBackground.apply { holder.binding.cardBackground.apply {
// // Set the card's background colour to a desaturated shade of the primary colour // Set the card's background colour to a desaturated shade of the primary colour
// backgroundTintList = ColorStateList.valueOf(colorPrimaryTranslucent.data) backgroundTintList = ColorStateList.valueOf(theme.getThemedColor(R.attr.colorSecondaryContainer))
//
// // Hide card stroke // Hide card stroke
// strokeColor = context.getColor(android.R.color.transparent) strokeColor = context.getColor(android.R.color.transparent)
// } }
// // Otherwise, apply colours to ensure that the item will not be highlighted // Otherwise, apply colours to ensure that the item will not be highlighted
// } else { } else {
// holder.binding.cardBackground.apply { holder.binding.cardBackground.apply {
// // Set the card's background colour to its default value // Set the card's background colour to its default value
// backgroundTintList = ColorStateList.valueOf(colorCardBackground.data) backgroundTintList = ColorStateList.valueOf(theme.getThemedColor(R.attr.colorSurface))
//
// // Set the card's stroke to its default colour // Set the card's stroke to its default colour
// strokeColor = colorTextHintLighter.data strokeColor = theme.getThemedColor(R.attr.colorOutlineVariant)
// } }
// } }
// Set up single click listener // Set up single click listener
holder.binding.cardBackground.setOnClickListener { holder.binding.cardBackground.setOnClickListener {
@@ -1,5 +1,10 @@
package com.denizk0461.studip.data package com.denizk0461.studip.data
import android.content.res.Resources
import android.util.TypedValue
import androidx.annotation.AttrRes
import com.denizk0461.studip.R
/** /**
* Miscellaneous functions and variables that don't belong into any specific class. * Miscellaneous functions and variables that don't belong into any specific class.
*/ */
@@ -15,6 +20,16 @@ fun String.parseToMinutes(): Int {
return (parts[0].toInt() * 60) + parts[1].toInt() return (parts[0].toInt() * 60) + parts[1].toInt()
} }
/**
* Retrieves a specified colour customised to the currently applied theme.
*
* @param id attribute ID of the colour reference
* @return resolved colour
*/
fun Resources.Theme.getThemedColor(@AttrRes id: Int): Int = TypedValue().also { value ->
resolveAttribute(id, value, true)
}.data
object Misc { object Misc {
// private val timeslotAcademicQuarter = mapOf( // private val timeslotAcademicQuarter = mapOf(
@@ -1,5 +1,6 @@
package com.denizk0461.studip.data package com.denizk0461.studip.data
import com.denizk0461.studip.R
import com.denizk0461.studip.db.AppRepository import com.denizk0461.studip.db.AppRepository
import com.denizk0461.studip.model.* import com.denizk0461.studip.model.*
import org.jsoup.Jsoup import org.jsoup.Jsoup
@@ -31,7 +32,7 @@ class StwParser {
* available * available
* @param onFinish action call for when the fetch has finished * @param onFinish action call for when the fetch has finished
*/ */
fun parse(onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) { fun parse(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) {
/* /*
* Reset primary key values. This needs to be done in case this method is called multiple * Reset primary key values. This needs to be done in case this method is called multiple
* times within the app's lifespan * times within the app's lifespan
@@ -41,21 +42,20 @@ class StwParser {
categoryId = 0 categoryId = 0
itemId = 0 itemId = 0
/* // Check which plan needs to be fetched
* Create and populate a list of all the canteen plans that will be fetched. val link = when (canteen) {
* TODO implement functionality that will allow for fetching multiple plans, if necessary 0 -> urlUniMensa
*/ 1 -> urlCafeCentral
val links = mutableListOf<String>() 2 -> urlNW1
if (true) { links.add(urlUniMensa) } 3 -> urlGW2
// if (true) { links.add(urlCafeCentral) } 4 -> urlHSBNeustadt
// if (true) { links.add(urlNW1) } 5 -> urlHSBWerder
// if (true) { links.add(urlGW2) } 6 -> urlHSBAirport
// if (true) { links.add(urlHSBNeustadt) } 7 -> urlMensaBHV
// if (true) { links.add(urlHSBAirport) } 8 -> urlCafeBHV
// if (true) { links.add(urlHSBWerder) } 9 -> urlHfK
// if (true) { links.add(urlHfK) } else -> urlUniMensa
// if (true) { links.add(urlMensaBHV) } }
// if (true) { links.add(urlCafeBHV) }
// Delete all previous entries and start afresh // Delete all previous entries and start afresh
Dependencies.repo.nukeOffers() Dependencies.repo.nukeOffers()
@@ -68,11 +68,11 @@ class StwParser {
* implemented here instead. Another option would be to set the conflict policy to IGNORE * implemented here instead. Another option would be to set the conflict policy to IGNORE
* and move the dateId reset from parseFromPage() to parse(). * and move the dateId reset from parseFromPage() to parse().
*/ */
links.forEach { link -> // links.forEach { link ->
parseFromPage(link, Dependencies.repo) parseFromPage(link, Dependencies.repo)
// TODO implement onRefresh(Int) // TODO implement onRefresh(Int)
} // }
// Action call once all fetching activities have finished // Action call once all fetching activities have finished
onFinish() onFinish()
@@ -121,7 +121,7 @@ class AppRepository(app: Application) {
* @param pref the dietary preference that should be retrieved * @param pref the dietary preference that should be retrieved
* @return whether the preference needs to be met1 * @return whether the preference needs to be met1
*/ */
fun getPreference(pref: DietaryPreferences): Boolean = fun getBooleanPreference(pref: DietaryPreferences): Boolean =
getDietaryPrefs()[pref.ordinal] == DietaryPrefObject.C_TRUE getDietaryPrefs()[pref.ordinal] == DietaryPrefObject.C_TRUE
/** /**
@@ -170,7 +170,15 @@ class AppRepository(app: Application) {
* @param pref preference to retrieve * @param pref preference to retrieve
* @return whether the user set this preference * @return whether the user set this preference
*/ */
fun getPreference(pref: SettingsPreferences): Boolean = prefs.getBoolean(pref.key, false) fun getBooleanPreference(pref: SettingsPreferences): Boolean = prefs.getBoolean(pref.key, false)
/**
* Retrieves ta user-set integer preference.
*
* @param pref preference to retrieve
* @return whether the user set this preference
*/
fun getIntPreference(pref: SettingsPreferences): Int = prefs.getInt(pref.key, 0)
/** /**
* Updates a user-set boolean preference. * Updates a user-set boolean preference.
@@ -181,4 +189,14 @@ class AppRepository(app: Application) {
fun setPreference(pref: SettingsPreferences, newValue: Boolean) { fun setPreference(pref: SettingsPreferences, newValue: Boolean) {
prefs.edit().putBoolean(pref.key, newValue).apply() prefs.edit().putBoolean(pref.key, newValue).apply()
} }
/**
* Updates a user-set integer preference.
*
* @param pref preference to set
* @param newValue new value to set the preference to
*/
fun setPreference(pref: SettingsPreferences, newValue: Int) {
prefs.edit().putInt(pref.key, newValue).apply()
}
} }
@@ -2,9 +2,12 @@ package com.denizk0461.studip.fragment
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import com.denizk0461.studip.R
import com.denizk0461.studip.adapter.CanteenOfferItemAdapter import com.denizk0461.studip.adapter.CanteenOfferItemAdapter
import com.denizk0461.studip.adapter.CanteenOfferPageAdapter import com.denizk0461.studip.adapter.CanteenOfferPageAdapter
import com.denizk0461.studip.databinding.FragmentCanteenBinding import com.denizk0461.studip.databinding.FragmentCanteenBinding
@@ -53,6 +56,31 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// TODO KDoc and finish
binding.buttonCanteenPicker.setOnClickListener {
PopupMenu(binding.root.context, binding.buttonCanteenPicker).apply {
setOnMenuItemClickListener { item ->
viewModel.preferenceCanteen = when (item?.itemId) {
R.id.mensa_uni -> 0
R.id.cafe_central -> 1
R.id.mensa_nw1 -> 2
R.id.cafeteria_gw2 -> 3
R.id.mensa_neustadt -> 4
R.id.mensa_werder -> 5
R.id.mensa_airport -> 6
R.id.mensa_bhv -> 7
R.id.cafeteria_bhv -> 8
R.id.mensa_hfk -> 9
else -> 0
}
true
}
inflate(R.menu.menu_canteens)
show()
}
}
// 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.FAIR,
@@ -128,7 +156,7 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
// Set up functions for when the user swipes to refresh the view // Set up functions for when the user swipes to refresh the view
binding.swipeRefreshLayout.setOnRefreshListener { binding.swipeRefreshLayout.setOnRefreshListener {
// Retrieve new offers from the website(s) // Retrieve new offers from the website(s)
viewModel.fetchOffers(onRefreshUpdate = { status -> viewModel.fetchOffers(viewModel.preferenceCanteen, onRefreshUpdate = { status ->
// TODO refresh updates // TODO refresh updates
}, onFinish = { }, onFinish = {
// createTabLayoutMediator() // createTabLayoutMediator()
@@ -140,7 +168,8 @@ class CanteenFragment : AppFragment(), CanteenOfferItemAdapter.OnClickListener {
/** /**
* Create and attach the object mediating the tabs for the view pager. * Create and attach the object mediating the tabs for the view pager.
* TODO this is crash-prone * TODO this is crash-prone and doesn't refresh when the dates get renewed (when the last update
* was from the day prior)
*/ */
private fun createTabLayoutMediator() { private fun createTabLayoutMediator() {
// Fetch all dates from the database // Fetch all dates from the database
@@ -68,6 +68,14 @@ class SettingsFragment : AppFragment() {
} }
} }
// Set up switch for launching the app with a specific view
binding.switchLaunchCanteen.apply {
isChecked = viewModel.preferenceLaunchCanteen
setOnCheckedChangeListener { _, newValue ->
viewModel.preferenceLaunchCanteen = newValue
}
}
// Set up switch for crash report opt-in // Set up switch for crash report opt-in
binding.switchCrashlytics.apply { binding.switchCrashlytics.apply {
isChecked = viewModel.preferenceDataHandling isChecked = viewModel.preferenceDataHandling
@@ -11,7 +11,9 @@ enum class SettingsPreferences(val key: String) {
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"),
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"),
CANTEEN("setting_canteen"),
; ;
} }
@@ -57,6 +57,17 @@ class ScheduleUpdateSheet(
).show((context as FragmentActivity).supportFragmentManager, "timePicker") ).show((context as FragmentActivity).supportFragmentManager, "timePicker")
} }
/*
* TODO implement option to change the colour for the course - single-selection coloured
* filter buttons with checkmarks when they're selected maybe?
*/
// Prepare cancel button
binding.buttonCancel.setOnClickListener {
// Do nothing and dismiss the sheet
dismiss()
}
// Prepare delete button // Prepare delete button
binding.buttonDelete.setOnClickListener { binding.buttonDelete.setOnClickListener {
onDelete(event) onDelete(event)
@@ -67,6 +78,7 @@ class ScheduleUpdateSheet(
// Prepare update/save button // Prepare update/save button
binding.buttonSave.setOnClickListener { binding.buttonSave.setOnClickListener {
onUpdate( onUpdate(
// construct new StudIPEvent from the data the user may have edited
StudIPEvent( StudIPEvent(
id = event.id, id = event.id,
title = binding.editTextTitle.text.toString(), title = binding.editTextTitle.text.toString(),
@@ -98,7 +110,7 @@ class ScheduleUpdateSheet(
// If start > end, show an error // If start > end, show an error
if ((isEventStart && newTimestamp.parseToMinutes() > timeslotEnd.parseToMinutes()) || if ((isEventStart && newTimestamp.parseToMinutes() > timeslotEnd.parseToMinutes()) ||
(!isEventStart && newTimestamp.parseToMinutes() < timeslotStart.parseToMinutes())) { (!isEventStart && newTimestamp.parseToMinutes() < timeslotStart.parseToMinutes())) {
// tell user that the timestamp must be set earlier/later // TODO tell user that the timestamp must be set earlier/later
} else { } else {
// If the new timestamp is valid, save it // If the new timestamp is valid, save it
if (isEventStart) { if (isEventStart) {
@@ -37,8 +37,8 @@ class CanteenViewModel(app: Application) : AppViewModel(app) {
fun getDates(): List<OfferDate> = returnBlocking { repo.getDates() } fun getDates(): List<OfferDate> = returnBlocking { repo.getDates() }
// 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.
fun fetchOffers(onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) { fun fetchOffers(canteen: Int, onRefreshUpdate: (status: Int) -> Unit, onFinish: () -> Unit) {
doAsync { parser.parse(onRefreshUpdate, onFinish) } doAsync { parser.parse(canteen, onRefreshUpdate, onFinish) }
} }
/** /**
@@ -57,11 +57,15 @@ class CanteenViewModel(app: Application) : AppViewModel(app) {
* @param pref the dietary preference that should be retrieved * @param pref the dietary preference that should be retrieved
* @return whether the preference needs to be met * @return whether the preference needs to be met
*/ */
fun getPreference(pref: DietaryPreferences): Boolean = repo.getPreference(pref) fun getPreference(pref: DietaryPreferences): Boolean = repo.getBooleanPreference(pref)
/** /**
* This value determines whether the user wants to have allergens marked. * This value determines whether the user wants to have allergens marked.
*/ */
val preferenceAllergen: Boolean val preferenceAllergen: Boolean
get() = repo.getPreference(SettingsPreferences.ALLERGEN) get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN)
var preferenceCanteen: Int
get() = repo.getIntPreference(SettingsPreferences.CANTEEN)
set(newValue) { repo.setPreference(SettingsPreferences.CANTEEN, newValue) }
} }
@@ -11,23 +11,31 @@ import com.denizk0461.studip.model.SettingsPreferences
class SettingsViewModel(app: Application) : AppViewModel(app) { class SettingsViewModel(app: Application) : AppViewModel(app) {
/** /**
* This value determines whether the user opts into submitting crash reports. * This value determines whether the user wants to have the next course in their schedule
* highlighted.
*/ */
var preferenceCourseHighlighting: Boolean var preferenceCourseHighlighting: Boolean
get() = repo.getPreference(SettingsPreferences.DATA_HANDLING) get() = repo.getBooleanPreference(SettingsPreferences.COURSE_HIGHLIGHTING)
set(newValue) { repo.setPreference(SettingsPreferences.DATA_HANDLING, newValue) } set(newValue) { repo.setPreference(SettingsPreferences.COURSE_HIGHLIGHTING, newValue) }
/** /**
* This value determines whether the user wants to have allergens marked. * This value determines whether the user wants to have allergens marked.
*/ */
var preferenceAllergen: Boolean var preferenceAllergen: Boolean
get() = repo.getPreference(SettingsPreferences.ALLERGEN) get() = repo.getBooleanPreference(SettingsPreferences.ALLERGEN)
set(newValue) { repo.setPreference(SettingsPreferences.ALLERGEN, newValue) } set(newValue) { repo.setPreference(SettingsPreferences.ALLERGEN, newValue) }
/**
* This value determines whether the user wants the app to launch with the canteen view.
*/
var preferenceLaunchCanteen: Boolean
get() = repo.getBooleanPreference(SettingsPreferences.LAUNCH_CANTEEN_ON_START)
set(newValue) { repo.setPreference(SettingsPreferences.LAUNCH_CANTEEN_ON_START, newValue) }
/** /**
* This value determines whether the user opts into submitting crash reports. * This value determines whether the user opts into submitting crash reports.
*/ */
var preferenceDataHandling: Boolean var preferenceDataHandling: Boolean
get() = repo.getPreference(SettingsPreferences.DATA_HANDLING) get() = repo.getBooleanPreference(SettingsPreferences.DATA_HANDLING)
set(newValue) { repo.setPreference(SettingsPreferences.DATA_HANDLING, newValue) } set(newValue) { repo.setPreference(SettingsPreferences.DATA_HANDLING, newValue) }
} }
+1 -22
View File
@@ -5,27 +5,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/app_title_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/title_schedule"
android:textSize="32sp"
android:fontFamily="@font/lato_bolditalic"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment_content_main" android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment" android:name="androidx.navigation.fragment.NavHostFragment"
@@ -35,7 +14,7 @@
app:layout_constraintBottom_toTopOf="@+id/nav_view" app:layout_constraintBottom_toTopOf="@+id/nav_view"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"/> app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.bottomnavigation.BottomNavigationView <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view" android:id="@+id/nav_view"
+34 -5
View File
@@ -4,14 +4,43 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/day_tab_layout" android:id="@+id/toolbar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:tabMode="scrollable" android:fitsSystemWindows="true"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/app_title_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/title_food"
android:textSize="32sp"
android:fontFamily="@font/lato_bolditalic"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/day_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable"/>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/button_canteen_picker"
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"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh_layout" android:id="@+id/swipe_refresh_layout"
@@ -19,7 +48,7 @@
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/day_tab_layout" app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:layout_constraintBottom_toTopOf="@+id/scroll_view_chip"> app:layout_constraintBottom_toTopOf="@+id/scroll_view_chip">
<androidx.appcompat.widget.LinearLayoutCompat <androidx.appcompat.widget.LinearLayoutCompat
+22 -1
View File
@@ -6,6 +6,25 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".fragment.EventFragment"> tools:context=".fragment.EventFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/app_title_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/title_schedule"
android:textSize="32sp"
android:fontFamily="@font/lato_bolditalic"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"/>
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/day_tab_layout" android:id="@+id/day_tab_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -15,13 +34,15 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/> app:layout_constraintEnd_toEndOf="parent"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager" android:id="@+id/view_pager"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/day_tab_layout" app:layout_constraintTop_toBottomOf="@+id/toolbar"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior"--> <!-- app:layout_behavior="@string/appbar_scrolling_view_behavior"-->
@@ -11,6 +11,24 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<TextView
android:id="@+id/app_title_bar"
android:layout_width="match_parent"
android:layout_height="48dp"
android:text="@string/title_settings"
android:textSize="32sp"
android:fontFamily="@font/lato_bolditalic"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"/>
</com.google.android.material.appbar.AppBarLayout>
<!-- Stud.IP schedule settings --> <!-- Stud.IP schedule settings -->
<com.google.android.material.card.MaterialCardView <com.google.android.material.card.MaterialCardView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -222,6 +240,49 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"/> android:layout_marginHorizontal="16dp"/>
<!-- show canteen plan on app launch -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_launch_canteen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/switch_launch_canteen"
android:layout_marginEnd="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:fontFamily="@font/lato_bolditalic"
android:text="@string/settings_launch_canteen_title"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="@string/settings_launch_canteen_subtitle"/>
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switch_launch_canteen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- crashlytics opt-in --> <!-- crashlytics opt-in -->
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layout_crashlytics" android:id="@+id/layout_crashlytics"
@@ -13,13 +13,34 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/> android:layout_gravity="center"/>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="22sp" android:textSize="22sp"
android:fontFamily="@font/lato_blackitalic" android:fontFamily="@font/lato_blackitalic"
android:layout_marginBottom="8dp" android:text="@string/sheet_schedule_update_header"
android:text="@string/sheet_schedule_update_header"/> android:layout_gravity="start|center_vertical"/>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.TextButton"
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="0dp"
app:icon="@drawable/cross"
app:iconPadding="0dp"
android:layout_gravity="end|center_vertical"
app:iconTint="?attr/colorOnSurfaceVariant"/>
<!-- app:icon="@drawable/cross"-->
<!-- app:strokeColor="?attr/colorOutlineVariant"-->
<!-- android:text="@string/cancel"-->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/mensa_uni"
android:title="@string/mensa_uni"
android:enabled="true"/>
<item
android:id="@+id/cafe_central"
android:title="@string/cafe_central"
android:enabled="true"/>
<item
android:id="@+id/mensa_nw1"
android:title="@string/mensa_nw1"
android:enabled="true"/>
<item
android:id="@+id/cafeteria_gw2"
android:title="@string/cafeteria_gw2"
android:enabled="true"/>
<item
android:id="@+id/mensa_neustadt"
android:title="@string/mensa_neustadt"
android:enabled="true"/>
<item
android:id="@+id/mensa_werder"
android:title="@string/mensa_werder"
android:enabled="true"/>
<item
android:id="@+id/mensa_airport"
android:title="@string/mensa_airport"
android:enabled="true"/>
<item
android:id="@+id/mensa_bhv"
android:title="@string/mensa_bhv"
android:enabled="true"/>
<item
android:id="@+id/cafeteria_bhv"
android:title="@string/cafeteria_bhv"
android:enabled="true"/>
<item
android:id="@+id/mensa_hfk"
android:title="@string/mensa_hfk"
android:enabled="true"/>
</menu>
+4
View File
@@ -13,6 +13,7 @@
<string name="save">Speichern</string> <string name="save">Speichern</string>
<string name="delete">Löschen</string> <string name="delete">Löschen</string>
<string name="cancel">Abbrechen</string>
<string name="sheet_schedule_update_header">Veranstaltung bearbeiten</string> <string name="sheet_schedule_update_header">Veranstaltung bearbeiten</string>
<string name="sheet_schedule_update_title_hint">Veranstaltungstitel</string> <string name="sheet_schedule_update_title_hint">Veranstaltungstitel</string>
<string name="sheet_schedule_update_lecturers_hint">Dozierende(r)</string> <string name="sheet_schedule_update_lecturers_hint">Dozierende(r)</string>
@@ -114,6 +115,9 @@
<string name="settings_allergens_title">Markiere Angebote mit Allergenen mit einem Stern*</string> <string name="settings_allergens_title">Markiere Angebote mit Allergenen mit einem Stern*</string>
<string name="settings_allergens_subtitle">Wird auch für Zusatzstoffe genutzt</string> <string name="settings_allergens_subtitle">Wird auch für Zusatzstoffe genutzt</string>
<string name="settings_launch_canteen_title">Starte die App mit dem Mensaplan</string>
<string name="settings_launch_canteen_subtitle">Sonst wird Dein Stundenplan beim Start gezeigt</string>
<string name="settings_crashlytics_title">Stimme Sammlung von Absturzreporten zu</string> <string name="settings_crashlytics_title">Stimme Sammlung von Absturzreporten zu</string>
<string name="settings_crashlytics_subtitle">Daten werden anonymisiert übertragen</string> <!-- TODO is this true? --> <string name="settings_crashlytics_subtitle">Daten werden anonymisiert übertragen</string> <!-- TODO is this true? -->
+2
View File
@@ -6,4 +6,6 @@
<attr name="colorOnSurfaceVariant" type="color"/> <attr name="colorOnSurfaceVariant" type="color"/>
<attr name="colorErrorContainer" type="color"/> <attr name="colorErrorContainer" type="color"/>
<attr name="colorOnErrorContainer" type="color"/> <attr name="colorOnErrorContainer" type="color"/>
<attr name="colorSecondaryContainer" type="color"/>
<attr name="colorOnSecondaryContainer" type="color"/>
</resources> </resources>
+4
View File
@@ -13,6 +13,7 @@
<string name="save">Save</string> <string name="save">Save</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="cancel">Cancel</string>
<string name="sheet_schedule_update_header">Edit Event</string> <string name="sheet_schedule_update_header">Edit Event</string>
<string name="sheet_schedule_update_title_hint">Event title</string> <string name="sheet_schedule_update_title_hint">Event title</string>
<string name="sheet_schedule_update_lecturers_hint">Lecturer(s)</string> <string name="sheet_schedule_update_lecturers_hint">Lecturer(s)</string>
@@ -117,6 +118,9 @@
<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>
<string name="settings_launch_canteen_title">Show canteen plan on app launch</string>
<string name="settings_launch_canteen_subtitle">Otherwise your schedule will be shown at launch</string>
<string name="settings_crashlytics_title">Opt-in to sharing crash data</string> <string name="settings_crashlytics_title">Opt-in to sharing crash data</string>
<string name="settings_crashlytics_subtitle">Data will be anonymised</string> <!-- TODO is this true? --> <string name="settings_crashlytics_subtitle">Data will be anonymised</string> <!-- TODO is this true? -->