User can now add, edit, and delete timetables without crashing (hopefully)

This commit is contained in:
denizk0461
2023-07-31 22:09:04 +02:00
parent 1f05fad84c
commit 453e65ec48
16 changed files with 265 additions and 38 deletions
@@ -164,6 +164,12 @@ class AppRepository(app: Application) {
fun insertTimetable(timetable: Timetable) {
dao.insertTimetable(timetable)
/*
* require-non-null can be done here as we're adding an entry to this table beforehand,
* which guarantees that the element will not be null
*/
setPreferenceSelectedTimetable(dao.getLargestTimetableId()!!)
}
fun updateTimetable(timetable: Timetable) { dao.updateTimetable(timetable) }
@@ -174,7 +180,10 @@ class AppRepository(app: Application) {
fun getTimetableForId(id: Int): Timetable = dao.getTimetableForId(id)
fun deleteTimetable(id: Int) { dao.deleteTimetable(id) }
fun deleteTimetable(id: Int) {
dao.deleteTimetable(id)
setPreferenceSelectedTimetable(dao.getLargestTimetableId() ?: 0)
}
/**
* Retrieves all canteen offer date objects.
@@ -56,6 +56,9 @@ class EventFragment : AppFragment<FragmentEventBinding>() {
}
}
// Set the title to be selected so it scrolls (marquee)
binding.appTitleBar.isSelected = true
// Get localised weekday names
weekdays = context.resources?.getStringArray(R.array.weekdays) ?: arrayOf()
@@ -113,6 +116,12 @@ class EventFragment : AppFragment<FragmentEventBinding>() {
// Set visibility of the fetch button depending on whether any events have been stored
viewModel.getEventCount().observe(viewLifecycleOwner) { eventCount ->
val timetableName = viewModel.getSelectedTimetableName()
if (timetableName.isNotBlank()) {
binding.appTitleBar.text = timetableName
}
binding.buttonFetchSchedule.visibility = if (eventCount == 0) {
View.VISIBLE
} else {
@@ -27,6 +27,7 @@ import com.denizk0461.weserplaner.databinding.FragmentSettingsBinding
import com.denizk0461.weserplaner.model.FormattedDate
import com.denizk0461.weserplaner.model.Timetable
import com.denizk0461.weserplaner.sheet.AllergenConfigSheet
import com.denizk0461.weserplaner.sheet.TimetableAddSheet
import com.denizk0461.weserplaner.sheet.TimetableEditSheet
import com.denizk0461.weserplaner.values.AppLayout
import com.denizk0461.weserplaner.viewmodel.SettingsViewModel
@@ -112,6 +113,10 @@ class SettingsFragment : AppFragment<FragmentSettingsBinding>() {
binding.autoCompleteTimetable.setText(timetableAdapter.getItem(position), false)
}
binding.buttonAddTimetable.setOnClickListener {
openBottomSheet(TimetableAddSheet())
}
binding.buttonEditTimetable.setOnClickListener {
if (binding.autoCompleteTimetable.text.isBlank()) {
context.theme.showErrorSnackBar(
@@ -12,6 +12,6 @@ import androidx.room.PrimaryKey
*/
@Entity(tableName = "timetables")
data class Timetable(
@PrimaryKey(autoGenerate = true) val id: Int,
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
)
@@ -0,0 +1,51 @@
package com.denizk0461.weserplaner.sheet
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.viewBinding
import com.denizk0461.weserplaner.databinding.SheetTimetableAddBinding
import com.denizk0461.weserplaner.fragment.SettingsFragment
import com.denizk0461.weserplaner.model.Timetable
import com.denizk0461.weserplaner.viewmodel.SheetTimetableAddViewModel
class TimetableAddSheet : AppSheet(R.layout.sheet_timetable_add) {
// View binding
private val binding: SheetTimetableAddBinding by viewBinding(SheetTimetableAddBinding::bind)
// View model
private val viewModel: SheetTimetableAddViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set up FAB behaviour for parent fragment's FAB if activity is not null
val parentFragment = activity?.let { activity ->
// Retrieve parent fragment to access its functions
val navHostFragment = activity
.supportFragmentManager
.fragments[0] as NavHostFragment
navHostFragment
.childFragmentManager
.primaryNavigationFragment as SettingsFragment
}
binding.buttonCancel.setOnClickListener {
dismiss()
}
binding.buttonSave.setOnClickListener {
viewModel.insertTimetable(Timetable(name = binding.editTextTitle.text.toString().trim()))
// Tell the user that the event has been updated
parentFragment?.showSnackBar(
getString(R.string.settings_timetable_sheet_confirm_add)
)
dismiss()
}
}
}
@@ -9,7 +9,7 @@ import com.denizk0461.weserplaner.values.AppLayout
*
* @param app reference to the app
*/
class EventViewModel(app: Application) : AppViewModel(app) {
class EventViewModel(private val app: Application) : AppViewModel(app) {
/**
* Retrieves the number of Stud.IP events available in the database.
@@ -18,6 +18,14 @@ class EventViewModel(app: Application) : AppViewModel(app) {
*/
fun getEventCount(): LiveData<Int> = repo.getEventCount()
fun getSelectedTimetableName(): String = returnBlocking {
try {
repo.getTimetableForId(repo.getPreferenceSelectedTimetable()).name
} catch (e: NullPointerException) {
""
}
}
/**
* This value determines whether the user wants their timetable to launch with the current day.
*/
@@ -0,0 +1,12 @@
package com.denizk0461.weserplaner.viewmodel
import android.app.Application
import androidx.lifecycle.viewModelScope
import com.denizk0461.weserplaner.model.Timetable
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class SheetTimetableAddViewModel(application: Application) : AppViewModel(application) {
fun insertTimetable(timetable: Timetable) { viewModelScope.launch(Dispatchers.IO) { repo.insertTimetable(timetable) }}
}
+41 -31
View File
@@ -144,45 +144,55 @@
android:text="@string/settings_selected_timetable_header"
android:textSize="16sp" />
<androidx.appcompat.widget.LinearLayoutCompat
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_timetable"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:hint="@string/settings_selected_timetable_hint"
app:boxStrokeColor="@color/selector_text_input"
app:hintTextColor="@color/selector_text_input">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_timetable"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/auto_complete_timetable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:hint="@string/settings_selected_timetable_hint"
app:boxStrokeColor="@color/selector_text_input"
app:hintTextColor="@color/selector_text_input">
android:textColor="?attr/colorOnSurfaceVariant"
android:lines="1"
android:maxLines="1"
tools:text="asdf asdf asdf asdf asdf ??? asdf asdf asdf asdf asdf asdf asdf asdf asdf"
android:inputType="text"
android:clickable="false"
android:focusable="false"/>
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/auto_complete_timetable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/colorOnSurfaceVariant"
android:inputType="none"/>
</com.google.android.material.textfield.TextInputLayout>
</com.google.android.material.textfield.TextInputLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:id="@+id/button_edit_timetable"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="0dp"
app:icon="@drawable/edit"
app:iconPadding="0dp"
app:strokeColor="?attr/colorOutlineVariant"
app:iconTint="?attr/colorOnSurfaceVariant"
tools:ignore="UnusedAttribute"
android:tooltipText="@string/settings_selected_timetable_edit_button_hint"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="16dp">
</androidx.appcompat.widget.LinearLayoutCompat>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.OutlinedButton"
android:id="@+id/button_add_timetable"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_timetable_button_add"
app:strokeColor="?attr/colorOutlineVariant"
android:layout_marginEnd="8dp"/>
<com.google.android.material.button.MaterialButton
style="@style/Widget.Material3.Button.OutlinedButton"
android:id="@+id/button_edit_timetable"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_timetable_button_edit"
app:strokeColor="?attr/colorOutlineVariant"/>
</androidx.appcompat.widget.LinearLayoutCompat>
@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<com.google.android.material.button.MaterialButton
style="@style/SheetCloseButton"
android:id="@+id/button_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/sheet_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="22sp"
android:paddingHorizontal="24dp"
android:layout_marginBottom="8dp"
app:fontFamily="@font/lato_blackitalic"
android:text="@string/settings_timetable_sheet_header_add"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_cancel"/>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="16dp"
android:paddingHorizontal="24dp">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_timetable_add_hint"/>
<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:id="@+id/text_layout_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:hint="@string/settings_selected_timetable_hint"
app:boxStrokeColor="@color/selector_text_input"
app:hintTextColor="@color/selector_text_input">
<com.google.android.material.textfield.TextInputEditText
style="@style/ThemeOverlay.Material3.TextInputEditText.OutlinedBox"
android:textColor="?attr/colorOnSurfaceVariant"
android:id="@+id/edit_text_title"
android:inputType="text"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end"
android:layout_marginVertical="8dp"
android:animateLayoutChanges="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/button_save"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save"
app:icon="@drawable/save"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.core.widget.NestedScrollView>
</androidx.appcompat.widget.LinearLayoutCompat>
@@ -70,6 +70,8 @@
style="@style/ThemeOverlay.Material3.TextInputEditText.OutlinedBox"
android:textColor="?attr/colorOnSurfaceVariant"
android:id="@+id/edit_text_title"
android:inputType="text"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
+6
View File
@@ -249,11 +249,17 @@
<string name="settings_selected_timetable_header">Wähle, welchen Stundenplan einzusehen</string>
<string name="settings_selected_timetable_hint">Stundenplan</string>
<string name="settings_selected_timetable_edit_button_hint">Stundenplan bearbeiten</string>
<string name="settings_selected_timetable_edit_text_title">Name des Stundenplans</string>
<string name="settings_timetable_button_add">Hinzufügen</string>
<string name="settings_timetable_button_edit">Bearbeiten</string>
<string name="settings_timetable_sheet_header">Diesen Stundenplan bearbeiten</string>
<string name="settings_timetable_sheet_header_add">Neuen Stundenplan hinzufügen</string>
<string name="settings_timetable_edit_fail">Es gibt keinen Stundenplan zum bearbeiten!</string>
<string name="settings_timetable_sheet_confirm_save">Stundenplan wurde aktualisiert.</string>
<string name="settings_timetable_sheet_confirm_add">Stundenplan wurde hinzugefügt.</string>
<string name="settings_timetable_sheet_confirm_delete">Stundenplan wurde gelöscht.</string>
<string name="settings_timetable_sheet_delete_confirm">Sicher?</string>
<string name="settings_timetable_add_hint">Hinweis: wenn du einen Stundenplan von Stud.IP herunterlädst, wird dieser automatisch als neuer Stundenplan hinzugefügt, also musst du in diesem Fall hier nichts machen!</string>
<string name="settings_current_day_title">Starte Stundenplan am heutigen Tag</string>
<string name="settings_current_day_subtitle">Ansonsten wird Montag beim Start angezeigt</string>
+6
View File
@@ -254,11 +254,17 @@
<string name="settings_selected_timetable_header">Select which timetable to display</string>
<string name="settings_selected_timetable_hint">Timetable</string>
<string name="settings_selected_timetable_edit_button_hint">Edit timetable</string>
<string name="settings_selected_timetable_edit_text_title">Timetable name</string>
<string name="settings_timetable_button_add">Add</string>
<string name="settings_timetable_button_edit">Edit</string>
<string name="settings_timetable_sheet_header">Edit this timetable</string>
<string name="settings_timetable_sheet_header_add">Add a new timetable</string>
<string name="settings_timetable_edit_fail">There is no timetable to edit!</string>
<string name="settings_timetable_sheet_confirm_save">Timetable has been updated.</string>
<string name="settings_timetable_sheet_confirm_add">Timetable has been added.</string>
<string name="settings_timetable_sheet_confirm_delete">Timetable has been deleted.</string>
<string name="settings_timetable_sheet_delete_confirm">Sure?</string>
<string name="settings_timetable_add_hint">Hint: if you download a timetable from Stud.IP, it automatically gets added as a new timetable, so you won\'t need to do anything here!</string>
<string name="settings_current_day_title">Launch timetable on current day</string>
<string name="settings_current_day_subtitle">If unselected, Monday will be shown on launch instead</string>