Timetables can now be edited and deleted via sheet from SettingsFragment.kt

This commit is contained in:
denizk0461
2023-07-04 14:47:19 +02:00
parent ca2cfe071f
commit a5d8e157de
11 changed files with 309 additions and 19 deletions
@@ -87,6 +87,15 @@ interface AppDAO {
@Query("SELECT * FROM timetables ORDER BY id")
fun getTimetables(): LiveData<List<Timetable>>
@Query("SELECT * FROM timetables WHERE id = :id LIMIT 1")
fun getTimetableForId(id: Int): Timetable
@Update
fun updateTimetable(timetable: Timetable)
@Query("DELETE FROM timetables WHERE id = :id")
fun deleteTimetable(id: Int)
/* --- event tasks --- */
/**
@@ -81,7 +81,7 @@ class AppRepository(app: Application) {
fun getEventsForDay(day: Int): LiveData<List<StudIPEvent>> = dao.getEventsForDay(day)
fun getEventsForDayAndTimetable(day: Int, timetable: Int): LiveData<List<StudIPEvent>> =
dao.getEventsForDayAndTimetable(day, timetable + 1) // I'm not sure why I need +1 here but it works
dao.getEventsForDayAndTimetable(day, timetable)
/**
* Inserts a list of Stud.IP events into the database.
@@ -166,10 +166,16 @@ class AppRepository(app: Application) {
dao.insertTimetable(timetable)
}
fun updateTimetable(timetable: Timetable) { dao.updateTimetable(timetable) }
fun getLargestTimetableId(): Int = dao.getLargestTimetableId() ?: 0
fun getTimetables(): LiveData<List<Timetable>> = dao.getTimetables()
fun getTimetableForId(id: Int): Timetable = dao.getTimetableForId(id)
fun deleteTimetable(id: Int) { dao.deleteTimetable(id) }
/**
* Retrieves all canteen offer date objects.
*
@@ -20,11 +20,14 @@ import com.denizk0461.weserplaner.activity.FetcherActivity
import com.denizk0461.weserplaner.activity.ImageActivity
import com.denizk0461.weserplaner.adapter.DropdownAdapter
import com.denizk0461.weserplaner.data.getTextSheet
import com.denizk0461.weserplaner.data.showErrorSnackBar
import com.denizk0461.weserplaner.data.showSnackBar
import com.denizk0461.weserplaner.data.showToast
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.TimetableEditSheet
import com.denizk0461.weserplaner.values.AppLayout
import com.denizk0461.weserplaner.viewmodel.SettingsViewModel
import java.nio.charset.StandardCharsets
@@ -52,6 +55,8 @@ class SettingsFragment : AppFragment<FragmentSettingsBinding>() {
private lateinit var timetableAdapter: DropdownAdapter
private var savedTimetables: List<Timetable> = listOf()
// Instantiate the view binding
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsBinding.inflate(inflater, container, false)
@@ -92,18 +97,36 @@ class SettingsFragment : AppFragment<FragmentSettingsBinding>() {
timetableAdapter.addAll(timetables.map { it.name })
if (timetables.isNotEmpty()) {
binding.autoCompleteTimetable.setText(
timetableAdapter.getItem(viewModel.getSelectedTimetable()).toString(),
timetableAdapter.getItem(timetables.map { it.id }.indexOf(viewModel.getSelectedTimetable())).toString(),//viewModel.getSelectedTimetable()).toString(),
false,
)
} else {
binding.autoCompleteTimetable.setText("")
}
savedTimetables = timetables
}
binding.autoCompleteTimetable.setAdapter(timetableAdapter)
binding.autoCompleteTimetable.setOnItemClickListener { _, _, position, _ ->
viewModel.setSelectedTimetable(position)
viewModel.setSelectedTimetable(savedTimetables[position].id)
binding.autoCompleteTimetable.setText(timetableAdapter.getItem(position), false)
}
binding.buttonEditTimetable.setOnClickListener {
if (binding.autoCompleteTimetable.text.isBlank()) {
context.theme.showErrorSnackBar(
binding.coordinatorLayout,
getString(R.string.settings_timetable_edit_fail),
)
} else {
openBottomSheet(TimetableEditSheet().also { sheet ->
val bundle = Bundle()
bundle.putInt("timetableId", viewModel.getSelectedTimetable())
sheet.arguments = bundle
})
}
}
// Set up switch for highlighting the next course in the schedule
binding.switchCurrentDay.apply {
isChecked = viewModel.preferenceCurrentDay
@@ -0,0 +1,90 @@
package com.denizk0461.weserplaner.sheet
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment
import androidx.transition.TransitionManager
import com.denizk0461.weserplaner.R
import com.denizk0461.weserplaner.data.viewBinding
import com.denizk0461.weserplaner.databinding.SheetTimetableEditBinding
import com.denizk0461.weserplaner.fragment.SettingsFragment
import com.denizk0461.weserplaner.model.Timetable
import com.denizk0461.weserplaner.viewmodel.SheetTimetableEditViewModel
import java.io.IOException
class TimetableEditSheet : AppSheet(R.layout.sheet_timetable_edit) {
// View binding
private val binding: SheetTimetableEditBinding by viewBinding(SheetTimetableEditBinding::bind)
// View model
private val viewModel: SheetTimetableEditViewModel by viewModels()
private var hasClickedDelete = false
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
}
// Retrieve event from arguments
val timetableId = arguments?.getInt("timetableId") ?: throw IOException("asdf wtf?") // TODO better error handling
val timetable = viewModel.getTimetableForId(timetableId)
binding.editTextTitle.setText(timetable.name)
binding.buttonCancel.setOnClickListener {
dismiss()
}
binding.buttonDelete.setOnClickListener {
/*
* Check if the user has already clicked the button before; this is done to prevent
* accidentally deleting an event.
*/
if (hasClickedDelete) {
viewModel.setSelectedTimetable(viewModel.getLargestTimetableId())
viewModel.deleteTimetable(timetableId)
// Tell the user that the event has been deleted
parentFragment?.showSnackBar(
getString(R.string.settings_timetable_sheet_confirm_delete)
)
// Dismiss the sheet upon deletion
dismiss()
} else {
// Update the text to show the user that they clicked the button once
TransitionManager
.beginDelayedTransition(binding.buttonContainer as ViewGroup)
binding.buttonDelete.text =
getString(R.string.settings_timetable_sheet_delete_confirm)
hasClickedDelete = true
}
}
binding.buttonSave.setOnClickListener {
viewModel.updateTimetable(Timetable(timetableId, binding.editTextTitle.text.toString().trim()))
// Tell the user that the event has been updated
parentFragment?.showSnackBar(
getString(R.string.settings_timetable_sheet_confirm_save)
)
dismiss()
}
}
}
@@ -0,0 +1,17 @@
package com.denizk0461.weserplaner.viewmodel
import android.app.Application
import com.denizk0461.weserplaner.model.Timetable
class SheetTimetableEditViewModel(application: Application) : AppViewModel(application) {
fun getTimetableForId(id: Int) = returnBlocking { repo.getTimetableForId(id) }
fun updateTimetable(timetable: Timetable) { doAsync { repo.updateTimetable(timetable) } }
fun deleteTimetable(id: Int) = doAsync { repo.deleteTimetable(id) }
fun setSelectedTimetable(newValue: Int) = repo.setPreferenceSelectedTimetable(newValue)
fun getLargestTimetableId(): Int = returnBlocking { repo.getLargestTimetableId() }
}
@@ -90,6 +90,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginBottom="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintBottom_toBottomOf="parent"
app:strokeColor="?attr/colorOutlineVariant"/>
</androidx.constraintlayout.widget.ConstraintLayout>
+34 -13
View File
@@ -133,7 +133,6 @@
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:orientation="vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
@@ -145,23 +144,45 @@
android:text="@string/settings_selected_timetable_header"
android:textSize="16sp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_timetable"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/settings_selected_timetable_hint"
app:boxStrokeColor="@color/selector_text_input"
app:hintTextColor="@color/selector_text_input">
android:orientation="horizontal">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/auto_complete_timetable"
android:layout_width="match_parent"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/text_layout_timetable"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?attr/colorOnSurfaceVariant"
android:inputType="none"/>
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">
</com.google.android.material.textfield.TextInputLayout>
<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.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>
</androidx.appcompat.widget.LinearLayoutCompat>
@@ -0,0 +1,111 @@
<?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"
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">
<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: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_delete"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/delete"
app:icon="@drawable/delete"
app:strokeColor="?attr/colorOutlineVariant"/>
<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>
+6
View File
@@ -248,6 +248,12 @@
<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_timetable_sheet_header">Diesen Stundenplan bearbeiten</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_delete">Stundenplan wurde gelöscht.</string>
<string name="settings_timetable_sheet_delete_confirm">Sicher?</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
@@ -253,6 +253,12 @@
<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_timetable_sheet_header">Edit this 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_delete">Timetable has been deleted.</string>
<string name="settings_timetable_sheet_delete_confirm">Sure?</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>