From 55369eaa8b110185e40e27911bd3b90ba39b2375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Fri, 14 Oct 2022 00:25:22 +0200 Subject: [PATCH] [UI/Timetable] Add Timetable settings. (#140) * [UI/Timetable] Add timetable config dialog and implement UI options. * [UI/Timetable] Fix reloading timetable after changing config. * [UI/Timetable] Fix calculating lesson range boundaries. --- .../edziennik/config/ProfileConfigUI.kt | 15 +++++ .../dialogs/settings/TimetableConfigDialog.kt | 44 +++++++++++++++ .../ui/timetable/TimetableDayFragment.kt | 56 ++++++++++++------- .../ui/timetable/TimetableFragment.kt | 13 ++++- .../edziennik/utils/MutableLazy.kt | 7 ++- .../main/res/layout/dialog_config_agenda.xml | 2 +- .../res/layout/timetable_config_dialog.xml | 56 +++++++++++++++++++ app/src/main/res/values-en/strings.xml | 2 +- app/src/main/res/values/strings.xml | 6 +- 9 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/TimetableConfigDialog.kt create mode 100644 app/src/main/res/layout/timetable_config_dialog.xml diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt index 187cb6bd..743872b0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigUI.kt @@ -69,4 +69,19 @@ class ProfileConfigUI(private val config: ProfileConfig) { var messagesGreetingText: String? get() { mMessagesGreetingText = mMessagesGreetingText ?: config.values["messagesGreetingText"]; return mMessagesGreetingText } set(value) { config.set("messagesGreetingText", value); mMessagesGreetingText = value } + + private var mTimetableShowAttendance: Boolean? = null + var timetableShowAttendance: Boolean + get() { mTimetableShowAttendance = mTimetableShowAttendance ?: config.values.get("timetableShowAttendance", true); return mTimetableShowAttendance ?: true } + set(value) { config.set("timetableShowAttendance", value); mTimetableShowAttendance = value } + + private var mTimetableShowEvents: Boolean? = null + var timetableShowEvents: Boolean + get() { mTimetableShowEvents = mTimetableShowEvents ?: config.values.get("timetableShowEvents", true); return mTimetableShowEvents ?: true } + set(value) { config.set("timetableShowEvents", value); mTimetableShowEvents = value } + + private var mTimetableTrimHourRange: Boolean? = null + var timetableTrimHourRange: Boolean + get() { mTimetableTrimHourRange = mTimetableTrimHourRange ?: config.values.get("timetableTrimHourRange", false); return mTimetableTrimHourRange ?: false } + set(value) { config.set("timetableTrimHourRange", value); mTimetableTrimHourRange = value } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/TimetableConfigDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/TimetableConfigDialog.kt new file mode 100644 index 00000000..b784165b --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/dialogs/settings/TimetableConfigDialog.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) Kuba Szczodrzyński 2022-10-7. + */ + +package pl.szczodrzynski.edziennik.ui.dialogs.settings + +import android.view.LayoutInflater +import androidx.appcompat.app.AppCompatActivity +import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.R +import pl.szczodrzynski.edziennik.databinding.TimetableConfigDialogBinding +import pl.szczodrzynski.edziennik.ext.Intent +import pl.szczodrzynski.edziennik.ext.onClick +import pl.szczodrzynski.edziennik.ui.dialogs.base.ConfigDialog +import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment + +class TimetableConfigDialog( + activity: AppCompatActivity, + reloadOnDismiss: Boolean = true, + onShowListener: ((tag: String) -> Unit)? = null, + onDismissListener: ((tag: String) -> Unit)? = null, +) : ConfigDialog( + activity, + reloadOnDismiss, + onShowListener, + onDismissListener, +) { + + override val TAG = "TimetableConfigDialog" + + override fun getTitleRes() = R.string.menu_timetable_config + override fun inflate(layoutInflater: LayoutInflater) = + TimetableConfigDialogBinding.inflate(layoutInflater) + + private val profileConfig by lazy { app.config.getFor(app.profileId).ui } + + override suspend fun loadConfig() { + b.config = profileConfig + } + + override suspend fun saveConfig() { + activity.sendBroadcast(Intent(TimetableFragment.ACTION_RELOAD_PAGES)) + } +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableDayFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableDayFragment.kt index 754bd4f5..a2d99247 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableDayFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableDayFragment.kt @@ -36,6 +36,7 @@ import pl.szczodrzynski.edziennik.ui.timetable.TimetableFragment.Companion.DEFAU import pl.szczodrzynski.edziennik.utils.managers.NoteManager import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.edziennik.utils.models.Time +import pl.szczodrzynski.edziennik.utils.mutableLazy import java.util.* import kotlin.coroutines.CoroutineContext import kotlin.math.min @@ -71,8 +72,9 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { // find SwipeRefreshLayout in the hierarchy private val refreshLayout by lazy { view?.findParentById(R.id.refreshLayout) } + private val profileConfig by lazy { app.config.forProfile().ui } - private val dayView by lazy { + private val dayViewDelegate = mutableLazy { val dayView = DayView(activity, DayViewConfig( startHour = startHour, endHour = endHour, @@ -85,8 +87,9 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { eventMargin = 2.dp ), true) dayView.setPadding(10.dp) - return@lazy dayView + return@mutableLazy dayView } + private val dayView by dayViewDelegate override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null @@ -173,8 +176,19 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { return } + if (dayViewDelegate.isInitialized()) + b.dayFrame.removeView(dayView) + + val lessonsActual = lessons.filter { it.type != Lesson.TYPE_NO_LESSONS } + + if (profileConfig.timetableTrimHourRange) { + dayViewDelegate.deinitialize() + // end/start defaults are swapped on purpose + startHour = lessonsActual.minOf { it.displayStartTime?.hour ?: DEFAULT_END_HOUR } + endHour = lessonsActual.maxOf { it.displayEndTime?.hour?.plus(1) ?: DEFAULT_START_HOUR } + } + b.scrollView.isVisible = true - b.dayFrame.removeView(dayView) b.dayFrame.addView(dayView, 0) // Inflate a label view for each hour the day view will display @@ -195,7 +209,7 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { lessons.forEach { it.showAsUnseen = !it.seen } - buildLessonViews(lessons.filter { it.type != Lesson.TYPE_NO_LESSONS }, events, attendanceList) + buildLessonViews(lessonsActual, events, attendanceList) } private fun buildLessonViews(lessons: List, events: List, attendanceList: List) { @@ -215,7 +229,10 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { val colorSecondary = android.R.attr.textColorSecondary.resolveAttr(activity) for (lesson in lessons) { - val attendance = attendanceList.find { it.startTime == lesson.startTime } + val attendance = if (profileConfig.timetableShowAttendance) + attendanceList.find { it.startTime == lesson.startTime } + else + null val startTime = lesson.displayStartTime ?: continue val endTime = lesson.displayEndTime ?: continue @@ -245,23 +262,20 @@ class TimetableDayFragment : LazyFragment(), CoroutineScope { } } - val eventList = events.filter { it.time != null && it.time == lesson.displayStartTime }.take(3) - eventList.getOrNull(0).let { - lb.event1.visibility = if (it == null) View.GONE else View.VISIBLE - lb.event1.background = it?.let { - R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.eventColor) + val eventIcons = listOf(lb.event1, lb.event2, lb.event3) + if (profileConfig.timetableShowEvents) { + val eventList = events.filter { it.time != null && it.time == lesson.displayStartTime }.take(3) + for ((i, eventIcon) in eventIcons.withIndex()) { + eventList.getOrNull(i).let { + eventIcon.isVisible = it != null + eventIcon.background = it?.let { + R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.eventColor) + } + } } - } - eventList.getOrNull(1).let { - lb.event2.visibility = if (it == null) View.GONE else View.VISIBLE - lb.event2.background = it?.let { - R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.eventColor) - } - } - eventList.getOrNull(2).let { - lb.event3.visibility = if (it == null) View.GONE else View.VISIBLE - lb.event3.background = it?.let { - R.drawable.bg_circle.resolveDrawable(activity).setTintColor(it.eventColor) + } else { + for (eventIcon in eventIcons) { + eventIcon.visibility = View.GONE } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableFragment.kt index b046ff68..fc2221bc 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/timetable/TimetableFragment.kt @@ -26,6 +26,7 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.data.db.entity.Metadata import pl.szczodrzynski.edziennik.databinding.FragmentTimetableV2Binding import pl.szczodrzynski.edziennik.ext.getSchoolYearConstrains +import pl.szczodrzynski.edziennik.ui.dialogs.settings.TimetableConfigDialog import pl.szczodrzynski.edziennik.ui.event.EventManualDialog import pl.szczodrzynski.edziennik.utils.models.Date import pl.szczodrzynski.navlib.bottomsheet.items.BottomSheetPrimaryItem @@ -52,6 +53,7 @@ class TimetableFragment : Fragment(), CoroutineScope { private var fabShown = false private val items = mutableListOf() + private val profileConfig by lazy { app.config.forProfile().ui } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null @@ -128,8 +130,8 @@ class TimetableFragment : Fragment(), CoroutineScope { } val lessonRanges = app.db.lessonRangeDao().getAllNow(App.profileId) - startHour = lessonRanges.map { it.startTime.hour }.minOrNull() ?: DEFAULT_START_HOUR - endHour = lessonRanges.map { it.endTime.hour }.maxOrNull()?.plus(1) ?: DEFAULT_END_HOUR + startHour = lessonRanges.minOfOrNull { it.startTime.hour } ?: DEFAULT_START_HOUR + endHour = lessonRanges.maxOfOrNull { it.endTime.hour }?.plus(1) ?: DEFAULT_END_HOUR } deferred.await() if (!isAdded) @@ -208,6 +210,13 @@ class TimetableFragment : Fragment(), CoroutineScope { activity.bottomSheet.close() GenerateBlockTimetableDialog(activity) }), + BottomSheetPrimaryItem(true) + .withTitle(R.string.menu_timetable_config) + .withIcon(CommunityMaterial.Icon.cmd_cog_outline) + .withOnClickListener { + activity.bottomSheet.close() + TimetableConfigDialog(activity, false, null, null).show() + }, BottomSheetSeparatorItem(true), BottomSheetPrimaryItem(true) .withTitle(R.string.menu_mark_as_read) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/MutableLazy.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/MutableLazy.kt index cf000e1f..f742c185 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/MutableLazy.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/MutableLazy.kt @@ -19,7 +19,6 @@ class MutableLazyImpl(initializer: () -> T, lock: Any? = null) { return synchronized(lock) { val typedValue = initializer!!() _value = typedValue - initializer = null typedValue } } @@ -29,7 +28,11 @@ class MutableLazyImpl(initializer: () -> T, lock: Any? = null) { fun isInitialized() = _value !== UNINITIALIZED_VALUE + fun deinitialize() { + _value = UNINITIALIZED_VALUE + } + override fun toString() = if (isInitialized()) _value.toString() else "ChangeableLazy value not initialized yet." } -fun mutableLazy(initializer: () -> T): MutableLazyImpl = MutableLazyImpl(initializer) \ No newline at end of file +fun mutableLazy(initializer: () -> T): MutableLazyImpl = MutableLazyImpl(initializer) diff --git a/app/src/main/res/layout/dialog_config_agenda.xml b/app/src/main/res/layout/dialog_config_agenda.xml index f17b5989..b51464e2 100644 --- a/app/src/main/res/layout/dialog_config_agenda.xml +++ b/app/src/main/res/layout/dialog_config_agenda.xml @@ -31,7 +31,7 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 851129f9..5dad0cb3 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1383,7 +1383,7 @@ Grade read the Privacy Policy and accept its provisions.

The authors of the application are not responsible for the use of the Szkolny.eu application.]]>
Szkolny.eu v%s\n%s - Appearance + Appearance Online learning online lesson Messages settings diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8f81073..3cf6221c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1424,7 +1424,7 @@ przeczytanie Polityki prywatności i akceptujesz jej postanowienia.

Autorzy aplikacji nie biorą odpowiedzialności za korzystanie z aplikacji Szkolny.eu.]]>
Szkolny.eu v%s\n%s Ustawienia terminarza - Wygląd + Wygląd Pokazuj zmiany planu lekcji Pokazuj nieobecności nauczycieli Tryb kompaktowy @@ -1537,4 +1537,8 @@ (rodzic) - nieznany przedmiot - {cmd-information-outline} Oceny, których przedmiot nie został podany w dzienniku. Może to być na przykład taki, który nie jest prowadzony w tym roku szkolnym. + Ustawienia planu lekcji + Pokazuj wydarzenia przy lekcjach + Pokazuj rodzaj obecności na lekcji + Nie pokazuj godzin bez lekcji