1
0

Compare commits

...

18 Commits
2.2.1 ... 2.2.3

Author SHA1 Message Date
387ff1cba7 Merge branch 'release/2.2.3' 2023-10-26 18:31:56 +02:00
eef3464d0b Version 2.2.3 2023-10-26 18:31:51 +02:00
61297a01c7 New Crowdin updates (#2334) 2023-10-26 14:01:45 +02:00
762d4b1393 Timetable timers fixes (#2333) 2023-10-26 10:06:54 +02:00
2e86b67eec Merge branch 'release/2.2.2' into develop 2023-10-23 20:02:32 +02:00
6071b7571b Merge branch 'release/2.2.2' 2023-10-23 20:02:25 +02:00
fcea2218b5 Version 2.2.2 2023-10-23 19:56:46 +02:00
a4a191700e Bump com.google.firebase:firebase-bom from 32.3.1 to 32.4.0 (#2331) 2023-10-23 17:28:38 +00:00
3d76d41b55 Bump com.squareup.okhttp3:logging-interceptor from 4.11.0 to 4.12.0 (#2330) 2023-10-23 16:49:58 +00:00
0e1c20a952 Bump room from 2.5.2 to 2.6.0 (#2329) 2023-10-23 16:48:23 +00:00
5d14ee7f4e Bump androidx.recyclerview:recyclerview from 1.3.1 to 1.3.2 (#2332) 2023-10-23 16:47:03 +00:00
83527d91f3 Allow direct access to weekend from day navigation when there is any lesson during weekend (#2326) 2023-10-23 13:05:46 +02:00
9d62410530 Sort teachers by name in school and teachers tab (#2327) 2023-10-23 13:05:05 +02:00
5dffbdadfa Points statistics improvements (#2328) 2023-10-23 13:04:42 +02:00
516922d5aa Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2324) 2023-10-14 19:18:28 +00:00
9098e74065 Bump com.google.android.material:material from 1.9.0 to 1.10.0 (#2325) 2023-10-14 19:17:56 +00:00
2f5577cc54 Update SDK to 34 (#2322) 2023-10-06 10:07:55 +02:00
3272c38356 Merge branch 'release/2.2.1' into develop 2023-10-03 01:14:25 +02:00
42 changed files with 246 additions and 137 deletions

View File

@ -20,15 +20,15 @@ apply from: 'hooks.gradle'
android {
namespace 'io.github.wulkanowy'
compileSdk 33
compileSdk 34
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 33
versionCode 133
versionName "2.2.1"
targetSdkVersion 34
versionCode 135
versionName "2.2.3"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@ -185,34 +185,34 @@ huaweiPublish {
ext {
work_manager = "2.8.1"
android_hilt = "1.0.0"
room = "2.5.2"
room = "2.6.0"
chucker = "3.5.2"
mockk = "1.13.8"
coroutines = "1.7.3"
}
dependencies {
implementation 'io.github.wulkanowy:sdk:2.2.1'
implementation 'io.github.wulkanowy:sdk:2.2.3'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.10.1"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation "androidx.activity:activity-ktx:1.7.2"
implementation "androidx.activity:activity-ktx:1.8.0"
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.fragment:fragment-ktx:1.6.1"
implementation "androidx.annotation:annotation:1.7.0"
implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.recyclerview:recyclerview:1.3.1"
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation "com.google.android.material:material:1.9.0"
implementation "com.google.android.material:material:1.10.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.3.0'
@ -236,7 +236,7 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
implementation "com.jakewharton.timber:timber:5.0.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
@ -248,7 +248,7 @@ dependencies {
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
implementation 'org.apache.commons:commons-text:1.10.0'
playImplementation platform('com.google.firebase:firebase-bom:32.3.1')
playImplementation platform('com.google.firebase:firebase-bom:32.4.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-crashlytics:'

View File

@ -1,16 +1,16 @@
apply plugin: "jacoco"
jacoco {
toolVersion "0.8.7"
toolVersion "0.8.10"
reportsDirectory.set(file("$buildDir/reports"))
}
tasks.withType(Test) {
tasks.withType(Test).configureEach {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
task jacocoTestReport(type: JacocoReport) {
tasks.register('jacocoTestReport', JacocoReport) {
group = "Reporting"
description = "Generate Jacoco coverage reports"

View File

@ -194,12 +194,6 @@ class PreferencesRepository @Inject constructor(
)
)
val showTimetableTimers: Boolean
get() = getBoolean(
R.string.pref_key_timetable_show_timers,
R.bool.pref_default_timetable_show_timers
)
val showTimetableGaps: TimetableGapsMode
get() = TimetableGapsMode.getByValue(
getString(

View File

@ -148,6 +148,10 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
binding.attendanceNavDate.text = date
}
override fun showNavigation(show: Boolean) {
binding.attendanceNavContainer.isVisible = show
}
override fun clearData() {
with(attendanceAdapter) {
items = emptyList()
@ -281,7 +285,9 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
presenter.currentDate?.let {
outState.putLong(SAVED_DATE_KEY, it.toEpochDay())
}
}
override fun onDestroyView() {

View File

@ -3,10 +3,14 @@ package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.*
@ -14,6 +18,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDate.now
import java.time.LocalDate.ofEpochDay
@ -28,9 +33,10 @@ class AttendancePresenter @Inject constructor(
private val analytics: AnalyticsHelper
) : BasePresenter<AttendanceView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().previousOrSameSchoolDay
private var initialDate: LocalDate? = null
private var isWeekendHasLessons: Boolean = false
lateinit var currentDate: LocalDate
var currentDate: LocalDate? = null
private set
private lateinit var lastError: Throwable
@ -44,27 +50,34 @@ class AttendancePresenter @Inject constructor(
view.initView()
Timber.i("Attendance view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
currentDate = date?.let(::ofEpochDay)
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
}
fun onPreviousDay() {
val date = if (isWeekendHasLessons) {
currentDate?.minusDays(1)
} else currentDate?.previousSchoolDay
view?.finishActionMode()
attendanceToExcuseList.clear()
reloadView(currentDate.previousSchoolDay)
reloadView(date ?: return)
loadData()
}
fun onNextDay() {
val date = if (isWeekendHasLessons) {
currentDate?.plusDays(1)
} else currentDate?.nextSchoolDay
view?.finishActionMode()
attendanceToExcuseList.clear()
reloadView(currentDate.nextSchoolDay)
reloadView(date ?: return)
loadData()
}
fun onPickDate() {
view?.showDatePickerDialog(currentDate)
view?.showDatePickerDialog(currentDate ?: return)
}
fun onDateSet(year: Int, month: Int, day: Int) {
@ -93,10 +106,8 @@ class AttendancePresenter @Inject constructor(
Timber.i("Attendance view is reselected")
view?.let { view ->
if (view.currentStackSize == 1) {
baseDate = now().previousOrSameSchoolDay
if (currentDate != baseDate) {
reloadView(baseDate)
if (currentDate != initialDate) {
reloadView(initialDate ?: return)
loadData()
} else if (!view.isViewEmpty) {
view.resetView()
@ -188,19 +199,6 @@ class AttendancePresenter @Inject constructor(
return true
}
private fun setBaseDateOnHolidays() {
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading attendance data started")
@ -211,11 +209,13 @@ class AttendancePresenter @Inject constructor(
isParent = student.isParent
val semester = semesterRepository.getCurrentSemester(student)
checkInitialAndCurrentDate(student, semester)
attendanceRepository.getAttendance(
student = student,
semester = semester,
start = currentDate,
end = currentDate,
start = currentDate ?: now(),
end = currentDate ?: now(),
forceRefresh = forceRefresh
)
}
@ -231,6 +231,8 @@ class AttendancePresenter @Inject constructor(
}.sortedBy { item -> item.number }
}
.onResourceData {
isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it)
view?.run {
enableSwipe(true)
showProgress(false)
@ -238,6 +240,7 @@ class AttendancePresenter @Inject constructor(
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
reloadNavigation()
}
}
.onResourceIntermediate { view?.showRefresh(true) }
@ -263,6 +266,43 @@ class AttendancePresenter @Inject constructor(
.launch()
}
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
if (initialDate == null) {
val lessons = attendanceRepository.getAttendance(
student = student,
semester = semester,
start = now().monday,
end = now().sunday,
forceRefresh = false,
).toFirstResult().dataOrNull.orEmpty()
isWeekendHasLessons = isWeekendHasLessons(lessons)
initialDate = getInitialDate(semester)
}
if (currentDate == null) {
currentDate = initialDate
}
}
private fun isWeekendHasLessons(
lessons: List<Attendance>,
): Boolean = lessons.any {
it.date.dayOfWeek in listOf(
DayOfWeek.SATURDAY,
DayOfWeek.SUNDAY,
)
}
private fun getInitialDate(semester: Semester): LocalDate {
val now = now()
return when {
now.isHolidays -> now.getLastSchoolDayIfHoliday(semester.schoolYear)
isWeekendHasLessons -> now
else -> now.previousOrSameSchoolDay
}
}
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
@ -311,7 +351,7 @@ class AttendancePresenter @Inject constructor(
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}")
Timber.i("Reload attendance view with the date ${currentDate?.toFormattedString()}")
view?.apply {
showProgress(true)
enableSwipe(false)
@ -326,10 +366,13 @@ class AttendancePresenter @Inject constructor(
@SuppressLint("DefaultLocale")
private fun reloadNavigation() {
val currentDate = currentDate ?: return
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)
showNextButton(!currentDate.plusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
showNavigation(true)
}
}
}

View File

@ -40,6 +40,8 @@ interface AttendanceView : BaseView {
fun showContent(show: Boolean)
fun showNavigation(show: Boolean)
fun showPreButton(show: Boolean)
fun showNextButton(show: Boolean)

View File

@ -386,7 +386,7 @@ class DashboardPresenter @Inject constructor(
private fun loadLessons(student: Student, forceRefresh: Boolean) {
flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
val date = LocalDate.now()
timetableRepository.getTimetable(
student = student,

View File

@ -22,6 +22,8 @@ import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.roundToInt
class GradeStatisticsAdapter @Inject constructor() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@ -269,7 +271,7 @@ class GradeStatisticsAdapter @Inject constructor() :
valueTextSize = 12f
valueTextColor = binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
valueFormatter = object : ValueFormatter() {
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}"
}
colors = gradePointsColors
}
@ -304,15 +306,20 @@ class GradeStatisticsAdapter @Inject constructor() :
}
xAxis.setDrawLabels(false)
xAxis.setDrawGridLines(false)
val yMaxFromValues = (max(points.others, points.student)).roundToInt() + 30f
val yMaxFromValuesWithMargin = ((yMaxFromValues / 10.0).roundToInt() * 10).toFloat()
val yMax = yMaxFromValuesWithMargin.coerceAtLeast(100f)
val yLabelCount = (yMax / 10).toInt() + 1
with(axisLeft) {
axisMinimum = 0f
axisMaximum = 100f
labelCount = 11
axisMaximum = yMax
labelCount = yLabelCount
}
with(axisRight) {
axisMinimum = 0f
axisMaximum = 100f
labelCount = 11
axisMaximum = yMax
labelCount = yLabelCount
}
invalidate()
}

View File

@ -58,7 +58,10 @@ class TeacherPresenter @Inject constructor(
.logResourceStatus("load teachers data")
.onResourceData {
view?.run {
updateData(it.filter { item -> item.name.isNotBlank() })
updateData(it
.filter { item -> item.name.isNotBlank() }
.sortedBy { it.name }
)
showContent(it.isNotEmpty())
showEmpty(it.isEmpty())
showErrorView(false)

View File

@ -35,7 +35,7 @@ class AdvancedFragment : PreferenceFragmentCompat(),
setPreferencesFromResource(R.xml.scheme_preferences_advanced, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
presenter.onSharedPreferenceChanged(key)
}

View File

@ -18,7 +18,8 @@ class AdvancedPresenter @Inject constructor(
Timber.i("Settings advanced view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
fun onSharedPreferenceChanged(key: String?) {
key ?: return
Timber.i("Change settings $key")
analytics.logEvent("setting_changed", "name" to key)
}

View File

@ -39,7 +39,7 @@ class AppearanceFragment : PreferenceFragmentCompat(),
setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
presenter.onSharedPreferenceChanged(key)
}

View File

@ -22,7 +22,8 @@ class AppearancePresenter @Inject constructor(
Timber.i("Settings appearance view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
fun onSharedPreferenceChanged(key: String?) {
key ?: return
Timber.i("Change settings $key")
preferencesRepository.apply {

View File

@ -114,7 +114,7 @@ class NotificationsFragment : PreferenceFragmentCompat(),
setPreferencesFromResource(R.xml.scheme_preferences_notifications, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
presenter.onSharedPreferenceChanged(key)
}

View File

@ -38,7 +38,8 @@ class NotificationsPresenter @Inject constructor(
Timber.i("Settings notifications view was initialized")
}
fun onSharedPreferenceChanged(key: String) {
fun onSharedPreferenceChanged(key: String?) {
key ?: return
Timber.i("Change settings $key")
preferencesRepository.apply {

View File

@ -52,7 +52,7 @@ class SyncFragment : PreferenceFragmentCompat(),
setPreferencesFromResource(R.xml.scheme_preferences_sync, rootKey)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
presenter.onSharedPreferenceChanged(key)
}

View File

@ -31,7 +31,8 @@ class SyncPresenter @Inject constructor(
setSyncDateInView()
}
fun onSharedPreferenceChanged(key: String) {
fun onSharedPreferenceChanged(key: String?) {
key ?: return
Timber.i("Change settings $key")
preferencesRepository.apply {
@ -52,10 +53,12 @@ class SyncPresenter @Inject constructor(
Timber.i("Setting sync now started")
analytics.logEvent("sync_now", "status" to "started")
}
WorkInfo.State.SUCCEEDED -> {
showMessage(syncSuccessString)
analytics.logEvent("sync_now", "status" to "success")
}
WorkInfo.State.FAILED -> {
showError(
syncFailedString,
@ -66,6 +69,7 @@ class SyncPresenter @Inject constructor(
)
analytics.logEvent("sync_now", "status" to "failed")
}
else -> Timber.d("Sync now state: ${workInfo?.state}")
}
if (workInfo?.state?.isFinished == true) {

View File

@ -325,7 +325,7 @@ class TimetableAdapter @Inject constructor() :
override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? {
return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) {
if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) {
if (oldItem.lesson == newItem.lesson && oldItem.showGroupsInPlan == newItem.showGroupsInPlan && oldItem.timeLeft != newItem.timeLeft) {
"time_left"
} else super.getChangePayload(oldItem, newItem)
} else super.getChangePayload(oldItem, newItem)

View File

@ -9,6 +9,7 @@ import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.core.os.bundleOf
import androidx.core.text.parseAsHtml
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
@ -160,6 +161,10 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
binding.timetableRecycler.visibility = if (show) VISIBLE else GONE
}
override fun showNavigation(show: Boolean) {
binding.timetableNavContainer.isVisible = true
}
override fun showPreButton(show: Boolean) {
binding.timetablePreviousButton.visibility = if (show) VISIBLE else View.INVISIBLE
}
@ -193,7 +198,9 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
presenter.currentDate?.toEpochDay()?.let {
outState.putLong(SAVED_DATE_KEY, it)
}
}
override fun onDestroyView() {

View File

@ -1,5 +1,10 @@
package io.github.wulkanowy.ui.modules.timetable
import android.os.Handler
import android.os.Looper
import io.github.wulkanowy.data.dataOrNull
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
@ -15,6 +20,8 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.data.toFirstResult
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
@ -24,15 +31,16 @@ import io.github.wulkanowy.utils.isHolidays
import io.github.wulkanowy.utils.isJustFinished
import io.github.wulkanowy.utils.isShowTimeUntil
import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.until
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.firstOrNull
import timber.log.Timber
import java.time.DayOfWeek
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDate.now
@ -51,9 +59,10 @@ class TimetablePresenter @Inject constructor(
private val analytics: AnalyticsHelper,
) : BasePresenter<TimetableView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().nextOrSameSchoolDay
private var initialDate: LocalDate? = null
private var isWeekendHasLessons: Boolean = false
lateinit var currentDate: LocalDate
var currentDate: LocalDate? = null
private set
private lateinit var lastError: Throwable
@ -65,23 +74,30 @@ class TimetablePresenter @Inject constructor(
view.initView()
Timber.i("Timetable was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
currentDate = date?.let(::ofEpochDay)
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
}
fun onPreviousDay() {
reloadView(currentDate.previousSchoolDay)
val date = if (isWeekendHasLessons) {
currentDate?.minusDays(1)
} else currentDate?.previousSchoolDay
reloadView(date ?: return)
loadData()
}
fun onNextDay() {
reloadView(currentDate.nextSchoolDay)
val date = if (isWeekendHasLessons) {
currentDate?.plusDays(1)
} else currentDate?.nextSchoolDay
reloadView(date ?: return)
loadData()
}
fun onPickDate() {
view?.showDatePickerDialog(currentDate)
view?.showDatePickerDialog(currentDate ?: return)
}
fun onDateSet(year: Int, month: Int, day: Int) {
@ -110,10 +126,8 @@ class TimetablePresenter @Inject constructor(
Timber.i("Timetable view is reselected")
view?.let { view ->
if (view.currentStackSize == 1) {
baseDate = now().nextOrSameSchoolDay
if (currentDate != baseDate) {
reloadView(baseDate)
if (currentDate != initialDate) {
reloadView(initialDate ?: return)
loadData()
} else if (!view.isViewEmpty) {
view.resetView()
@ -134,34 +148,25 @@ class TimetablePresenter @Inject constructor(
return true
}
private fun setBaseDateOnHolidays() {
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(forceRefresh: Boolean = false) {
flatResourceFlow {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
checkInitialAndCurrentDate(student, semester)
timetableRepository.getTimetable(
student = student,
semester = semester,
start = currentDate,
end = currentDate,
start = currentDate ?: now(),
end = currentDate ?: now(),
forceRefresh = forceRefresh,
timetableType = TimetableRepository.TimetableType.NORMAL
)
}
.logResourceStatus("load timetable data")
.onResourceData {
isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it.lessons)
view?.run {
enableSwipe(true)
showProgress(false)
@ -169,7 +174,8 @@ class TimetablePresenter @Inject constructor(
showContent(it.lessons.isNotEmpty())
showEmpty(it.lessons.isEmpty())
updateData(it.lessons)
setDayHeaderMessage(it.headers.singleOrNull { header -> header.date == currentDate }?.content)
setDayHeaderMessage(it.headers.find { header -> header.date == currentDate }?.content)
reloadNavigation()
}
}
.onResourceIntermediate { view?.showRefresh(true) }
@ -191,17 +197,57 @@ class TimetablePresenter @Inject constructor(
.launch()
}
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
if (initialDate == null) {
val lessons = timetableRepository.getTimetable(
student = student,
semester = semester,
start = now().monday,
end = now().sunday,
forceRefresh = false,
timetableType = TimetableRepository.TimetableType.NORMAL
).toFirstResult().dataOrNull?.lessons.orEmpty()
isWeekendHasLessons = isWeekendHasLessons(lessons)
initialDate = getInitialDate(semester)
}
if (currentDate == null) {
currentDate = initialDate
}
}
private fun isWeekendHasLessons(
lessons: List<Timetable>,
): Boolean = lessons.any {
it.date.dayOfWeek in listOf(
DayOfWeek.SATURDAY,
DayOfWeek.SUNDAY,
)
}
private fun getInitialDate(semester: Semester): LocalDate {
val now = now()
return when {
now.isHolidays -> now.getLastSchoolDayIfHoliday(semester.schoolYear)
isWeekendHasLessons -> now
else -> now.nextOrSameSchoolDay
}
}
private fun updateData(lessons: List<Timetable>) {
tickTimer?.cancel()
if (!prefRepository.showTimetableTimers) {
if (currentDate != now()) {
view?.updateData(createItems(lessons))
} else {
tickTimer = timer(period = 2_000) {
Handler(Looper.getMainLooper()).post {
view?.updateData(createItems(lessons))
}
}
}
}
private fun createItems(items: List<Timetable>): List<TimetableItem> {
val filteredItems = items
@ -285,7 +331,7 @@ class TimetablePresenter @Inject constructor(
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}")
Timber.i("Reload timetable view with the date ${currentDate?.toFormattedString()}")
view?.apply {
showProgress(true)
enableSwipe(false)
@ -298,10 +344,13 @@ class TimetablePresenter @Inject constructor(
}
private fun reloadNavigation() {
val currentDate = currentDate ?: return
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)
showNextButton(!currentDate.plusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
showNavigation(true)
}
}

View File

@ -36,6 +36,8 @@ interface TimetableView : BaseView {
fun showContent(show: Boolean)
fun showNavigation(show: Boolean)
fun showPreButton(show: Boolean)
fun showNextButton(show: Boolean)

View File

@ -1,6 +1,7 @@
Wersja 2.2.1
Wersja 2.2.3
dokonaliśmy kilka poprawek na ekranie logowania
naprawiliśmy przypadek z błędnym wyświetlaniem starej klasy ucznia po zalogowaniu się na konto z nowej klasy
— ułatwiliśmy przełączenie dnia na weekend w planie lekcji przy użyciu strzałek
— poprawiliśmy wsparcie dla statystyk ocen z systemem punktowym
— poprawiliśmy sortowanie nauczycieli w widoku Szkoła i nauczyciele
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -128,7 +128,9 @@
android:layout_gravity="bottom"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UnusedAttribute">
android:visibility="gone"
tools:ignore="UnusedAttribute"
tools:visibility="visible">
<ImageButton
android:id="@+id/attendancePreviousButton"

View File

@ -128,7 +128,9 @@
android:layout_gravity="bottom"
android:gravity="center"
android:orientation="horizontal"
tools:ignore="UnusedAttribute">
android:visibility="gone"
tools:ignore="UnusedAttribute"
tools:visibility="visible">
<ImageButton
android:id="@+id/timetablePreviousButton"

View File

@ -713,7 +713,6 @@
<string name="pref_view_present">Zobrazit přítomnost</string>
<string name="pref_view_app_theme">Motiv</string>
<string name="pref_view_expand_grade">Rozvíjení známek</string>
<string name="pref_view_timetable_show_timers">Označit aktuální lekci</string>
<string name="pref_view_timetable_show_groups">Zobrazit skupiny vedle předmětů</string>
<string name="pref_view_timetable_show_gaps">Zobrazit prázdné dlaždice, kde není žádná lekce</string>
<string name="pref_view_grade_statistics_list">Zobrazit seznam grafů v známkách třídy</string>

View File

@ -623,7 +623,6 @@
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>
<string name="pref_view_expand_grade">Grades expanding</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>

View File

@ -623,7 +623,6 @@
<string name="pref_view_present">Anwesendheit zeigen</string>
<string name="pref_view_app_theme">Thema</string>
<string name="pref_view_expand_grade">Steigende Sorten</string>
<string name="pref_view_timetable_show_timers">Aktuelle Lektion markieren</string>
<string name="pref_view_timetable_show_groups">Gruppen neben Schulfächen anzeigen</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Liste der Diagramme in Klassenbewertungen anzeigen</string>

View File

@ -623,7 +623,6 @@
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>
<string name="pref_view_expand_grade">Grades expanding</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>

View File

@ -623,7 +623,6 @@
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>
<string name="pref_view_expand_grade">Grades expanding</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>

View File

@ -713,7 +713,6 @@
<string name="pref_view_present">Pokazuj obecność</string>
<string name="pref_view_app_theme">Motyw</string>
<string name="pref_view_expand_grade">Rozwijanie ocen</string>
<string name="pref_view_timetable_show_timers">Oznaczaj bieżącą lekcję</string>
<string name="pref_view_timetable_show_groups">Pokazuj grupę obok przedmiotu</string>
<string name="pref_view_timetable_show_gaps">Pokazuj puste kafelki gdzie nie ma lekcji</string>
<string name="pref_view_grade_statistics_list">Pokazuj listę wykresów w ocenach klasy</string>

View File

@ -713,7 +713,6 @@
<string name="pref_view_present">Показывать присутствия</string>
<string name="pref_view_app_theme">Тема</string>
<string name="pref_view_expand_grade">Разворачивание оценок</string>
<string name="pref_view_timetable_show_timers">Отметить текущий урок</string>
<string name="pref_view_timetable_show_groups">Показать группы рядом с темами</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Показывать диаграммы в оценках класса</string>

View File

@ -713,7 +713,6 @@
<string name="pref_view_present">Zobraziť prítomnosť</string>
<string name="pref_view_app_theme">Motív</string>
<string name="pref_view_expand_grade">Rozvijanie známok</string>
<string name="pref_view_timetable_show_timers">Označiť aktuálne lekciu</string>
<string name="pref_view_timetable_show_groups">Zobraziť skupiny vedľa predmetov</string>
<string name="pref_view_timetable_show_gaps">Zobraziť prázdne dlaždice, kde nie je žiadne lekcie</string>
<string name="pref_view_grade_statistics_list">Zobraziť zoznam grafov v známkach triedy</string>

View File

@ -713,7 +713,6 @@
<string name="pref_view_present">Показувати присутність</string>
<string name="pref_view_app_theme">Тема</string>
<string name="pref_view_expand_grade">Розгортання оцінок</string>
<string name="pref_view_timetable_show_timers">Позначити поточний урок</string>
<string name="pref_view_timetable_show_groups">Показувати групи поруч з темами</string>
<string name="pref_view_timetable_show_gaps">Показувати порожні плитки там, де немає уроків</string>
<string name="pref_view_grade_statistics_list">Показувати діаграми в оцінках класу</string>

View File

@ -22,7 +22,6 @@
<bool name="pref_default_timetable_show_groups">false</bool>
<string name="pref_default_timetable_show_whole_class">no</string>
<string name="pref_default_grade_sorting_mode">alphabetic</string>
<bool name="pref_default_timetable_show_timers">false</bool>
<string name="pref_default_timetable_show_gaps">between</string>
<bool name="pref_default_subjects_without_grades">false</bool>
<bool name="pref_default_optional_arithmetic_average">false</bool>

View File

@ -27,7 +27,6 @@
<string name="pref_key_grade_sorting_mode">grade_sorting_mode</string>
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
<string name="pref_key_timetable_show_groups">show_groups_in_plan</string>
<string name="pref_key_timetable_show_timers">timetable_show_timers</string>
<string name="pref_key_timetable_show_gaps">timetable_show_gaps</string>
<string name="pref_key_subjects_without_grades">subjects_without_grades</string>
<string name="pref_key_optional_arithmetic_average">optional_arithmetic_average</string>

View File

@ -701,7 +701,6 @@
<string name="pref_view_present">Show presence</string>
<string name="pref_view_app_theme">Theme</string>
<string name="pref_view_expand_grade">Grades expanding</string>
<string name="pref_view_timetable_show_timers">Mark current lesson</string>
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>

View File

@ -90,11 +90,6 @@
android:layout_height="wrap_content"
app:iconSpaceReserved="false"
app:title="@string/pref_timetable_appearance_view">
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_timers"
app:iconSpaceReserved="false"
app:key="@string/pref_key_timetable_show_timers"
app:title="@string/pref_view_timetable_show_timers" />
<SwitchPreferenceCompat
app:defaultValue="@bool/pref_default_timetable_show_groups"
app:iconSpaceReserved="false"

View File

@ -1,8 +1,8 @@
buildscript {
ext {
kotlin_version = '1.9.10'
about_libraries = '10.9.0'
hilt_version = "2.48"
about_libraries = '10.9.1'
hilt_version = '2.48.1'
}
repositories {
mavenCentral()
@ -21,7 +21,7 @@ buildscript {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
classpath "com.github.triplet.gradle:play-publisher:3.8.4"
classpath "ru.cian:huawei-publish-gradle-plugin:1.4.0"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.4.0.3356"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.4.1.3373"
classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries"
}

Binary file not shown.

View File

@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

5
gradlew vendored
View File

@ -130,10 +130,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.

View File

@ -1,4 +1 @@
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
}
include ':app'