diff --git a/app/build.gradle b/app/build.gradle index c64eec87..bb4f9476 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -176,6 +176,7 @@ dependencies { implementation "at.favre.lib:slf4j-timber:1.0.1" implementation "com.squareup.okhttp3:logging-interceptor:3.12.6" implementation "com.mikepenz:aboutlibraries:7.0.4" + implementation 'com.wdullaer:materialdatetimepicker:4.2.3' playImplementation "com.google.firebase:firebase-core:17.2.1" playImplementation "com.crashlytics.sdk.android:crashlytics:2.10.1" diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index fb6309f7..b57bf7d0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -7,6 +7,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager @@ -17,9 +18,11 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_attendance.* +import org.threeten.bp.LocalDate import javax.inject.Inject class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildView, @@ -71,6 +74,7 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie attendanceSwipe.setOnRefreshListener(presenter::onSwipeRefresh) attendancePreviousButton.setOnClickListener { presenter.onPreviousDay() } + attendanceNavDate.setOnClickListener { presenter.onPickDate() } attendanceNextButton.setOnClickListener { presenter.onNextDay() } attendanceNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -141,6 +145,21 @@ class AttendanceFragment : BaseFragment(), AttendanceView, MainView.MainChildVie (activity as? MainActivity)?.showDialogFragment(AttendanceDialog.newInstance(lesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@AttendanceFragment.parentFragmentManager, null) + } + } + override fun openSummaryView() { (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index b8114208..0e1f5040 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -56,6 +56,15 @@ class AttendancePresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the attendance") loadData(currentDate, true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index 04fe94a4..20b3cd9e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.attendance import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface AttendanceView : BaseView { @@ -35,6 +36,8 @@ interface AttendanceView : BaseView { fun showAttendanceDialog(lesson: Attendance) + fun showDatePickerDialog(currentDate: LocalDate) + fun openSummaryView() fun popView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index a64dee8d..e1d7db8d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -7,6 +7,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.FlexibleItemDecoration import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager @@ -17,9 +18,11 @@ import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable.* +import org.threeten.bp.LocalDate import javax.inject.Inject class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, @@ -72,6 +75,7 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh) timetablePreviousButton.setOnClickListener { presenter.onPreviousDay() } + timetableNavDate.setOnClickListener {presenter.onPickDate() } timetableNextButton.setOnClickListener { presenter.onNextDay() } timetableNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -142,6 +146,21 @@ class TimetableFragment : BaseFragment(), TimetableView, MainView.MainChildView, (activity as? MainActivity)?.showDialogFragment(TimetableDialog.newInstance(lesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@TimetableFragment.parentFragmentManager, null) + } + } + override fun openCompletedLessonsView() { (activity as? MainActivity)?.pushView(CompletedLessonsFragment.newInstance()) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index cea5bf2f..be80fbd4 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -17,6 +17,7 @@ import io.github.wulkanowy.utils.previousSchoolDay import io.github.wulkanowy.utils.toFormattedString import org.threeten.bp.LocalDate import org.threeten.bp.LocalDate.now +import org.threeten.bp.LocalDate.of import org.threeten.bp.LocalDate.ofEpochDay import timber.log.Timber import java.util.concurrent.TimeUnit.MILLISECONDS @@ -55,6 +56,15 @@ class TimetablePresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the timetable") loadData(currentDate, true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 84f28974..749f11e0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface TimetableView : BaseView { @@ -35,6 +36,8 @@ interface TimetableView : BaseView { fun showTimetableDialog(lesson: Timetable) + fun showDatePickerDialog(currentDate: LocalDate) + fun popView() fun openCompletedLessonsView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index c7b5c6ca..67d35824 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.common.SmoothScrollLinearLayoutManager import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -12,10 +13,12 @@ import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.SchooldaysRangeLimiter import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.getCompatDrawable import io.github.wulkanowy.utils.setOnItemClickListener import kotlinx.android.synthetic.main.fragment_timetable_completed.* +import org.threeten.bp.LocalDate import javax.inject.Inject class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView.TitledView { @@ -56,6 +59,7 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. completedLessonsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) completedLessonsPreviousButton.setOnClickListener { presenter.onPreviousDay() } + completedLessonsNavDate.setOnClickListener { presenter.onPickDate() } completedLessonsNextButton.setOnClickListener { presenter.onNextDay() } completedLessonsNavContainer.setElevationCompat(requireContext().dpToPx(8f)) @@ -110,6 +114,21 @@ class CompletedLessonsFragment : BaseFragment(), CompletedLessonsView, MainView. (activity as? MainActivity)?.showDialogFragment(CompletedLessonDialog.newInstance(completedLesson)) } + override fun showDatePickerDialog(currentDate: LocalDate) { + val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth -> + presenter.onDateSet(year, month + 1, dayOfMonth) + } + val datePickerDialog = DatePickerDialog.newInstance(dateSetListener, + currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth) + + with(datePickerDialog) { + setDateRangeLimiter(SchooldaysRangeLimiter()) + version = DatePickerDialog.Version.VERSION_2 + scrollOrientation = DatePickerDialog.ScrollOrientation.VERTICAL + show(this@CompletedLessonsFragment.parentFragmentManager, null) + } + } + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay()) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index b936c657..3a439e1a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -57,6 +57,15 @@ class CompletedLessonsPresenter @Inject constructor( reloadView() } + fun onPickDate() { + view?.showDatePickerDialog(currentDate) + } + + fun onDateSet(year: Int, month: Int, day: Int) { + loadData(LocalDate.of(year, month, day)) + reloadView() + } + fun onSwipeRefresh() { Timber.i("Force refreshing the completed lessons") loadData(currentDate, true) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index a8bef66f..e1e167cd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.timetable.completed import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.ui.base.BaseView +import org.threeten.bp.LocalDate interface CompletedLessonsView : BaseView { @@ -32,4 +33,6 @@ interface CompletedLessonsView : BaseView { fun showNextButton(show: Boolean) fun showCompletedLessonDialog(completedLesson: CompletedLesson) + + fun showDatePickerDialog(currentDate: LocalDate) } diff --git a/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt new file mode 100644 index 00000000..922aafbd --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SchooldaysRangeLimiter.kt @@ -0,0 +1,51 @@ +package io.github.wulkanowy.utils + +import android.os.Parcel +import android.os.Parcelable +import com.wdullaer.materialdatetimepicker.date.DateRangeLimiter +import org.threeten.bp.DayOfWeek +import org.threeten.bp.LocalDate +import java.util.Calendar + +@Suppress("UNUSED_PARAMETER") +class SchooldaysRangeLimiter : DateRangeLimiter { + + private val now = LocalDate.now() + + override fun setToNearestDate(day: Calendar): Calendar = day + + override fun isOutOfRange(year: Int, month: Int, day: Int): Boolean { + val date = LocalDate.of(year, month + 1, day) + val dayOfWeek = date.dayOfWeek + return dayOfWeek == DayOfWeek.SUNDAY || dayOfWeek == DayOfWeek.SATURDAY || date.isHolidays + } + + override fun getStartDate(): Calendar { + val startYear = if (now.monthValue <= 6) now.year - 1 else now.year + val startOfSchoolYear = now.withYear(startYear).firstSchoolDay + + val calendar = Calendar.getInstance() + calendar.set(startOfSchoolYear.year, startOfSchoolYear.monthValue - 1, startOfSchoolYear.dayOfMonth) + return calendar + } + + override fun getEndDate(): Calendar { + val endYear = if (now.monthValue > 6) now.year + 1 else now.year + val endOfSchoolYear = now.withYear(endYear).lastSchoolDay + + val calendar = Calendar.getInstance() + calendar.set(endOfSchoolYear.year, endOfSchoolYear.monthValue - 1, endOfSchoolYear.dayOfMonth) + return calendar + } + + override fun writeToParcel(parcel: Parcel, flags: Int) {} + + override fun describeContents() = 0 + + companion object CREATOR : Parcelable.Creator { + + override fun createFromParcel(parcel: Parcel): SchooldaysRangeLimiter = SchooldaysRangeLimiter() + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } +} diff --git a/app/src/main/res/layout/fragment_attendance.xml b/app/src/main/res/layout/fragment_attendance.xml index 4877103e..b88066e5 100644 --- a/app/src/main/res/layout/fragment_attendance.xml +++ b/app/src/main/res/layout/fragment_attendance.xml @@ -60,8 +60,8 @@ android:layout_width="match_parent" android:layout_height="48dp" android:layout_gravity="bottom" - android:orientation="horizontal" android:gravity="center" + android:orientation="horizontal" tools:ignore="UnusedAttribute"> + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_timetable.xml b/app/src/main/res/layout/fragment_timetable.xml index db5c6fc2..27c5c4c1 100644 --- a/app/src/main/res/layout/fragment_timetable.xml +++ b/app/src/main/res/layout/fragment_timetable.xml @@ -60,28 +60,30 @@ android:layout_width="match_parent" android:layout_height="48dp" android:layout_gravity="bottom" - android:orientation="horizontal" android:gravity="center" + android:orientation="horizontal" tools:ignore="UnusedAttribute"> + + app:srcCompat="@drawable/ic_chevron_left" /> + app:srcCompat="@drawable/ic_chevron_right" /> diff --git a/app/src/main/res/layout/fragment_timetable_completed.xml b/app/src/main/res/layout/fragment_timetable_completed.xml index 7c093a4a..7c40063e 100644 --- a/app/src/main/res/layout/fragment_timetable_completed.xml +++ b/app/src/main/res/layout/fragment_timetable_completed.xml @@ -88,6 +88,7 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:fontFamily="sans-serif" + android:background="?selectableItemBackgroundBorderless" android:gravity="center" android:text="@string/app_name" android:textSize="16sp" /> diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 3e4a2466..49264538 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -10,6 +10,7 @@ ?android:textColorPrimary @android:color/black false + true + +