diff --git a/.idea/gradle.xml b/.idea/gradle.xml index a2d7c21..ae388c2 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,6 +7,7 @@ + diff --git a/.idea/misc.xml b/.idea/misc.xml index 773fe0f..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/README.md b/README.md index 4d27713..e6884a1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ WeserPlaner is a work-in-progress Android app for students at the University of - Downloading and creating events, as well as displaying them. - Downloading canteen offers, filtering them, and viewing details. - Viewing additional information on the canteen (opening hours, news). +- Storing multiple timetables is supported. ## What's broken Currently, nothing is outright broken, but a few things may be bodged together badly. @@ -15,7 +16,6 @@ Currently, nothing is outright broken, but a few things may be bodged together b - Adding exams, homework, etc.: users will be able to add any tasks they need to do as reminders. Those can be associated with events that either have been downloaded from the user's Stud.IP schedule, or ones the user has created manually. An overview will be provided that allows the user to have, well, an overview of everything they need to do. - Finding your room: I'd like to add a feature that makes finding a specific room on the university's campus much easier than it is now. The university website does have a feature that, in theory, should do exactly that, but it works very poorly and does not list all rooms. My idea would be that the user could type in a room, the app could then make suggestions on which room the user may want to find based on the input, and then show the user a floor plan with the room highlighted. - This could prove quite difficult though, as I do not have access to the university's proper floor plans. My best bet would be to either use the ones avalable online to create my own ones, or draw ones from scratch. As for finding all the rooms, I may have to walk around the university and find all rooms myself. -- Optimisations to data handling will be made. The app currently only supports storing a single timetable, which will be changed in future releases. A planned implementation will see the user being able to download schedules as well as create ones from scratch, with the schedules able to be selected via a dropdown menu. - Some improvements to the Play Store screenshots are sorely needed... ## Why Android-only? 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 d84a957..17ba4ea 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/db/AppRepository.kt @@ -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. diff --git a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt index aa03b63..a18c4dd 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/EventFragment.kt @@ -56,6 +56,9 @@ class EventFragment : AppFragment() { } } + // 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() { // 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 { 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 cd81fb0..6e07989 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/fragment/SettingsFragment.kt @@ -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() { binding.autoCompleteTimetable.setText(timetableAdapter.getItem(position), false) } + binding.buttonAddTimetable.setOnClickListener { + openBottomSheet(TimetableAddSheet()) + } + binding.buttonEditTimetable.setOnClickListener { if (binding.autoCompleteTimetable.text.isBlank()) { context.theme.showErrorSnackBar( diff --git a/app/src/main/java/com/denizk0461/weserplaner/model/Timetable.kt b/app/src/main/java/com/denizk0461/weserplaner/model/Timetable.kt index f5a5988..00255a1 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/model/Timetable.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/model/Timetable.kt @@ -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, ) diff --git a/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableAddSheet.kt b/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableAddSheet.kt new file mode 100644 index 0000000..488ba07 --- /dev/null +++ b/app/src/main/java/com/denizk0461/weserplaner/sheet/TimetableAddSheet.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/denizk0461/weserplaner/viewmodel/EventViewModel.kt b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/EventViewModel.kt index 153f53e..d978f75 100644 --- a/app/src/main/java/com/denizk0461/weserplaner/viewmodel/EventViewModel.kt +++ b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/EventViewModel.kt @@ -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 = 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. */ diff --git a/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableAddViewModel.kt b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableAddViewModel.kt new file mode 100644 index 0000000..03013af --- /dev/null +++ b/app/src/main/java/com/denizk0461/weserplaner/viewmodel/SheetTimetableAddViewModel.kt @@ -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) }} +} \ 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 5ad070e..2a22d8f 100644 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -144,45 +144,55 @@ android:text="@string/settings_selected_timetable_header" android:textSize="16sp" /> - + 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"/> - + - + - + - + + + diff --git a/app/src/main/res/layout/sheet_timetable_add.xml b/app/src/main/res/layout/sheet_timetable_add.xml new file mode 100644 index 0000000..7afece3 --- /dev/null +++ b/app/src/main/res/layout/sheet_timetable_add.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/sheet_timetable_edit.xml b/app/src/main/res/layout/sheet_timetable_edit.xml index 25bd36c..8eeb82e 100644 --- a/app/src/main/res/layout/sheet_timetable_edit.xml +++ b/app/src/main/res/layout/sheet_timetable_edit.xml @@ -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"/> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 83c0138..bff471a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -249,11 +249,17 @@ Wähle, welchen Stundenplan einzusehen Stundenplan Stundenplan bearbeiten + Name des Stundenplans + Hinzufügen + Bearbeiten Diesen Stundenplan bearbeiten + Neuen Stundenplan hinzufügen Es gibt keinen Stundenplan zum bearbeiten! Stundenplan wurde aktualisiert. + Stundenplan wurde hinzugefügt. Stundenplan wurde gelöscht. Sicher? + 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! 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 6e73a8c..e363e27 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -254,11 +254,17 @@ Select which timetable to display Timetable Edit timetable + Timetable name + Add + Edit Edit this timetable + Add a new timetable There is no timetable to edit! Timetable has been updated. + Timetable has been added. Timetable has been deleted. Sure? + 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! Launch timetable on current day If unselected, Monday will be shown on launch instead diff --git a/build.gradle.kts b/build.gradle.kts index 857d518..4cb48c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.0.2" apply false - id("com.android.library") version "8.0.2" apply false + id("com.android.application") version "8.1.0" apply false + id("com.android.library") version "8.1.0" apply false id("org.jetbrains.kotlin.android") version "1.8.21" apply false } \ No newline at end of file