diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 180c772..39f7b16 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,12 +6,12 @@ plugins { android { namespace = "com.denizk0461.weserplaner" - compileSdk = 33 + compileSdk = 34 defaultConfig { applicationId = "com.denizk0461.weserplaner" minSdk = 24 - targetSdk = 33 + targetSdk = 34 versionCode = 9 versionName = "1.2.0" diff --git a/app/src/main/java/com/denizk0461/weserplaner/db/AppDAO.kt b/app/src/main/java/com/denizk0461/weserplaner/db/AppDAO.kt index 2fe2371..4826054 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/db/AppDAO.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/db/AppDAO.kt @@ -87,6 +87,15 @@ interface AppDAO { @Query("SELECT * FROM timetables ORDER BY id") fun getTimetables(): LiveData> + @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 --- */ /** diff --git a/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt b/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt index ece4d3d..d84a957 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt @@ -81,7 +81,7 @@ class AppRepository(app: Application) { fun getEventsForDay(day: Int): LiveData> = dao.getEventsForDay(day) fun getEventsForDayAndTimetable(day: Int, timetable: Int): LiveData> = - 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> = dao.getTimetables() + fun getTimetableForId(id: Int): Timetable = dao.getTimetableForId(id) + + fun deleteTimetable(id: Int) { dao.deleteTimetable(id) } + /** * Retrieves all canteen offer date objects. * diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt index f97a89c..cd81fb0 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt @@ -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() { private lateinit var timetableAdapter: DropdownAdapter + private var savedTimetables: List = 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() { 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 diff --git a/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableEditSheet.kt b/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableEditSheet.kt new file mode 100644 index 0000000..e6cee72 --- /dev/null +++ b/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableEditSheet.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableEditViewModel.kt b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableEditViewModel.kt new file mode 100644 index 0000000..a683a24 --- /dev/null +++ b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableEditViewModel.kt @@ -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() } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_introduction.xml b/app/src/main/res/layout/activity_introduction.xml index e529523..38a704a 100644 --- a/app/src/main/res/layout/activity_introduction.xml +++ b/app/src/main/res/layout/activity_introduction.xml @@ -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"/> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index 3816b9e..5ad070e 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -133,7 +133,6 @@ @@ -145,23 +144,45 @@ android:text="@string/settings_selected_timetable_header" android:textSize="16sp" /> - + android:orientation="horizontal"> - + 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"> - + + + + + + + diff --git a/app/src/main/res/layout/sheet_timetable_edit.xml b/app/src/main/res/layout/sheet_timetable_edit.xml new file mode 100644 index 0000000..25bd36c --- /dev/null +++ b/app/src/main/res/layout/sheet_timetable_edit.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f0aabb7..83c0138 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -248,6 +248,12 @@ Wähle, welchen Stundenplan einzusehen Stundenplan + Stundenplan bearbeiten + Diesen Stundenplan bearbeiten + Es gibt keinen Stundenplan zum bearbeiten! + Stundenplan wurde aktualisiert. + Stundenplan wurde gelöscht. + Sicher? Starte Stundenplan am heutigen Tag Ansonsten wird Montag beim Start angezeigt diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a756b9b..6e73a8c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -253,6 +253,12 @@ Select which timetable to display Timetable + Edit timetable + Edit this timetable + There is no timetable to edit! + Timetable has been updated. + Timetable has been deleted. + Sure? Launch timetable on current day If unselected, Monday will be shown on launch instead