1
0

Compare commits

..

25 Commits

Author SHA1 Message Date
42515fd084 Merge branch 'release/0.20.3' into master 2020-09-04 23:42:07 +02:00
9a7c04fe7b Version 0.20.3 2020-09-04 23:42:01 +02:00
debb21f5f9 Add full stacktrace to errors list in sync now (#945) 2020-09-03 21:10:39 +02:00
18b9bf42e1 Fix crash in flowWithResourceIn() (#944) 2020-09-03 20:54:28 +02:00
6ded83d132 Fix attendance item description (#943) 2020-09-03 20:52:24 +02:00
71d37a1c6c Merge tag '0.20.2' into develop
Version 0.20.2
2020-09-02 00:23:49 +02:00
3975d06cde Merge branch 'release/0.20.2' into master 2020-09-02 00:23:45 +02:00
ee168bafe0 Version 0.20.2 2020-09-02 00:23:41 +02:00
42ed7e0ae1 Merge tag '0.20.1' into develop
Version 0.20.1
2020-09-02 00:14:35 +02:00
0e92447974 Merge branch 'release/0.20.1' into master 2020-09-02 00:14:23 +02:00
40492e6c01 Version 0.20.1 2020-09-02 00:14:18 +02:00
69a1193154 Fix semester list refresh on no current semester found (#940) 2020-09-01 23:58:18 +02:00
0f65af8958 Fix grade summary empty view (#941) 2020-09-01 23:57:56 +02:00
2ad1d086e0 Fix lucky number notification (#937) 2020-09-01 15:39:34 +02:00
f8b7baef24 Remove force sync dialog (#938) 2020-09-01 14:58:45 +02:00
90be9d1add Disable notification sound (#936) 2020-09-01 12:57:45 +02:00
20f931c5cc Fix recaptcha loading in password recover (#935) 2020-09-01 09:33:14 +02:00
9997b1adbb Add skarzyskokamienna vulcan register (#934) 2020-09-01 09:31:36 +02:00
eb616eedc7 Fix crash in flowWithResourceIn() (#933) 2020-09-01 09:31:01 +02:00
a5de39a366 Update UI dependencies (#927) 2020-08-31 12:55:51 +02:00
57bc2b2533 Bump firebase-crashlytics from 17.1.1 to 17.2.1 (#931) 2020-08-31 10:09:37 +00:00
d1ce16d2b1 Bump runner from 1.2.0 to 1.3.0 (#932) 2020-08-31 10:03:20 +00:00
54fb01cd0d Bump firebase-inappmessaging-display-ktx from 19.1.0 to 19.1.1 (#930) 2020-08-31 09:42:25 +00:00
370cfbf22a Bump core from 1.2.0 to 1.3.0 (#929) 2020-08-31 09:42:18 +00:00
d198a2ba21 Merge tag '0.20.0' into develop
Version 0.20.0
2020-08-29 23:57:20 +02:00
27 changed files with 128 additions and 81 deletions

View File

@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.20.0 - 0.20.3
android: android:
licenses: licenses:

View File

@ -47,7 +47,6 @@ You can also download a [development version](https://wulkanowy.github.io/#downl
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk) * [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
* [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger) * [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room) * [Room](https://developer.android.com/topic/libraries/architecture/room)
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) * [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)

View File

@ -48,7 +48,6 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa
## Zbudowana za pomocą ## Zbudowana za pomocą
* [Wulkanowy SDK](https://github.com/wulkanowy/SDK) * [Wulkanowy SDK](https://github.com/wulkanowy/SDK)
* [RxJava 2](https://github.com/ReactiveX/RxJava)
* [Dagger 2](https://github.com/google/dagger) * [Dagger 2](https://github.com/google/dagger)
* [Room](https://developer.android.com/topic/libraries/architecture/room) * [Room](https://developer.android.com/topic/libraries/architecture/room)
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager) * [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)

View File

@ -18,8 +18,8 @@ android {
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 29 targetSdkVersion 29
versionCode 64 versionCode 67
versionName "0.20.0" versionName "0.20.3"
multiDexEnabled true multiDexEnabled true
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -126,7 +126,7 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.20.0" implementation "io.github.wulkanowy:sdk:0.20.3"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
@ -145,9 +145,9 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.viewpager:viewpager:1.0.0" implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:1.1.3" implementation "androidx.constraintlayout:constraintlayout:2.0.1"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
implementation "com.google.android.material:material:1.1.0" implementation "com.google.android.material:material:1.2.0"
implementation "com.github.wulkanowy:material-chips-input:2.1.1" implementation "com.github.wulkanowy:material-chips-input:2.1.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
@ -181,10 +181,10 @@ dependencies {
implementation 'me.xdrop:fuzzywuzzy:1.3.1' implementation 'me.xdrop:fuzzywuzzy:1.3.1'
playImplementation 'com.google.firebase:firebase-analytics:17.5.0' playImplementation 'com.google.firebase:firebase-analytics:17.5.0'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.0' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1" playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1"
playImplementation 'com.google.firebase:firebase-messaging:20.2.4' playImplementation 'com.google.firebase:firebase-messaging:20.2.4'
playImplementation 'com.google.firebase:firebase-crashlytics:17.1.1' playImplementation 'com.google.firebase:firebase-crashlytics:17.2.1'
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
@ -196,8 +196,8 @@ dependencies {
testImplementation "io.mockk:mockk:$mockk" testImplementation "io.mockk:mockk:$mockk"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'
androidTestImplementation "androidx.test:core:1.2.0" androidTestImplementation "androidx.test:core:1.3.0"
androidTestImplementation "androidx.test:runner:1.2.0" androidTestImplementation "androidx.test:runner:1.3.0"
androidTestImplementation "androidx.test.ext:junit:1.1.2" androidTestImplementation "androidx.test.ext:junit:1.1.2"
androidTestImplementation "io.mockk:mockk-android:$mockk" androidTestImplementation "io.mockk:mockk-android:$mockk"
androidTestImplementation "androidx.room:room-testing:$room" androidTestImplementation "androidx.room:room-testing:$room"

View File

@ -12,5 +12,5 @@ import javax.inject.Singleton
interface LuckyNumberDao : BaseDao<LuckyNumber> { interface LuckyNumberDao : BaseDao<LuckyNumber> {
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date") @Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber> fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber?>
} }

View File

@ -3,7 +3,8 @@ package io.github.wulkanowy.data.repositories.luckynumber
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.networkBoundResource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import java.time.LocalDate.now import java.time.LocalDate.now
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -28,11 +29,8 @@ class LuckyNumberRepository @Inject constructor(
} }
) )
fun getNotNotifiedLuckyNumber(student: Student): Flow<LuckyNumber?> { suspend fun getNotNotifiedLuckyNumber(student: Student) =
return local.getLuckyNumber(student, now()) local.getLuckyNumber(student, now()).map { if (it?.isNotified == false) it else null }.first()
}
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) { suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = local.updateLuckyNumber(luckyNumber)
local.updateLuckyNumber(luckyNumber)
}
} }

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.data.repositories.semester package io.github.wulkanowy.data.repositories.semester
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.DispatchersProvider
@ -18,24 +19,33 @@ class SemesterRepository @Inject constructor(
) { ) {
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) { suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
local.getSemesters(student).let { semesters -> val semesters = local.getSemesters(student)
semesters.filter {
!forceRefresh && when { if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) {
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0 refreshSemesters(student)
refreshOnNoCurrent -> semesters.any { semester -> semester.isCurrent } local.getSemesters(student)
else -> true } else semesters
} }
private fun isShouldFetch(student: Student, semesters: List<Semester>, forceRefresh: Boolean, refreshOnNoCurrent: Boolean): Boolean {
val isNoSemesters = semesters.isEmpty()
val isRefreshOnModeChangeRequired = if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
} else false
val isRefreshOnNoCurrentAppropriate = refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate
} }
}.ifEmpty {
private suspend fun refreshSemesters(student: Student) {
val new = remote.getSemesters(student) val new = remote.getSemesters(student)
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
val old = local.getSemesters(student) val old = local.getSemesters(student)
local.deleteSemesters(old.uniqueSubtract(new)) local.deleteSemesters(old.uniqueSubtract(new))
local.saveSemesters(new.uniqueSubtract(old)) local.saveSemesters(new.uniqueSubtract(old))
local.getSemesters(student)
}
} }
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) { suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {

View File

@ -50,13 +50,16 @@ class SyncWorker @WorkerInject constructor(
} catch (e: Throwable) { } catch (e: Throwable) {
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred") Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
if (e is FeatureDisabledException || e is FeatureNotAvailableException) null if (e is FeatureDisabledException || e is FeatureNotAvailableException) null
else e else {
Timber.e(e)
e
}
} }
} }
val result = when { val result = when {
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> { exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
Result.failure(Data.Builder() Result.failure(Data.Builder()
.putString("error", exceptions.toString()) .putString("error", exceptions.map { it.stackTraceToString() }.toString())
.build() .build()
) )
} }

View File

@ -26,6 +26,7 @@ class UpcomingLessonsChannel @Inject constructor(
lockscreenVisibility = VISIBILITY_PUBLIC lockscreenVisibility = VISIBILITY_PUBLIC
setShowBadge(false) setShowBadge(false)
enableVibration(false) enableVibration(false)
setSound(null, null)
} }
) )
} }

View File

@ -19,7 +19,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.getCompatColor import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.waitForResult import io.github.wulkanowy.utils.waitForResult
import kotlinx.coroutines.flow.first
import javax.inject.Inject import javax.inject.Inject
import kotlin.random.Random import kotlin.random.Random
@ -33,7 +32,7 @@ class LuckyNumberWork @Inject constructor(
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester) {
luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult() luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult()
luckyNumberRepository.getNotNotifiedLuckyNumber(student).first()?.let { luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let {
notify(it) notify(it)
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true }) luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
} }

View File

@ -7,6 +7,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
@ -63,7 +64,7 @@ open class BasePresenter<T : BaseView>(
fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job { fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job {
jobs[individualJobTag]?.cancel() jobs[individualJobTag]?.cancel()
val job = launchIn(this@BasePresenter) val job = catch { errorHandler.dispatch(it) }.launchIn(this@BasePresenter)
jobs[individualJobTag] = job jobs[individualJobTag] = job
Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job") Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job")
return job return job

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
import io.github.wulkanowy.databinding.ItemAttendanceBinding import io.github.wulkanowy.databinding.ItemAttendanceBinding
import io.github.wulkanowy.utils.description
import javax.inject.Inject import javax.inject.Inject
class AttendanceAdapter @Inject constructor() : class AttendanceAdapter @Inject constructor() :
@ -34,7 +35,7 @@ class AttendanceAdapter @Inject constructor() :
with(holder.binding) { with(holder.binding) {
attendanceItemNumber.text = item.number.toString() attendanceItemNumber.text = item.number.toString()
attendanceItemSubject.text = item.subject attendanceItemSubject.text = item.subject
attendanceItemDescription.text = item.name attendanceItemDescription.setText(item.description)
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE } attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
attendanceItemNumber.visibility = View.GONE attendanceItemNumber.visibility = View.GONE
attendanceItemExcuseInfo.visibility = View.GONE attendanceItemExcuseInfo.visibility = View.GONE

View File

@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.databinding.DialogAttendanceBinding import io.github.wulkanowy.databinding.DialogAttendanceBinding
import io.github.wulkanowy.utils.description
import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
@ -43,7 +44,7 @@ class AttendanceDialog : DialogFragment() {
with(binding) { with(binding) {
attendanceDialogSubject.text = attendance.subject attendanceDialogSubject.text = attendance.subject
attendanceDialogDescription.text = attendance.name attendanceDialogDescription.setText(attendance.description)
attendanceDialogDate.text = attendance.date.toFormattedString() attendanceDialogDate.text = attendance.date.toFormattedString()
attendanceDialogNumber.text = attendance.number.toString() attendanceDialogNumber.text = attendance.number.toString()
attendanceDialogClose.setOnClickListener { dismiss() } attendanceDialogClose.setOnClickListener { dismiss() }

View File

@ -21,7 +21,7 @@ class GradeSummaryAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerV
var items = emptyList<GradeSummary>() var items = emptyList<GradeSummary>()
override fun getItemCount() = items.size + 1 override fun getItemCount() = items.size + if (items.isNotEmpty()) 1 else 0
override fun getItemViewType(position: Int) = when (position) { override fun getItemViewType(position: Int) = when (position) {
0 -> ViewType.HEADER.id 0 -> ViewType.HEADER.id

View File

@ -45,16 +45,17 @@ class GradeSummaryPresenter @Inject constructor(
Status.LOADING -> Timber.i("Loading grade summary started") Status.LOADING -> Timber.i("Loading grade summary started")
Status.SUCCESS -> { Status.SUCCESS -> {
Timber.i("Loading grade summary result: Success") Timber.i("Loading grade summary result: Success")
val items = createGradeSummaryItems(it.data!!)
view?.run { view?.run {
showEmpty(it.data!!.isEmpty()) showEmpty(items.isEmpty())
showContent(it.data.isNotEmpty()) showContent(items.isNotEmpty())
showErrorView(false) showErrorView(false)
updateData(createGradeSummaryItems(it.data)) updateData(items)
} }
analytics.logEvent( analytics.logEvent(
"load_data", "load_data",
"type" to "grade_summary", "type" to "grade_summary",
"items" to it.data!!.size "items" to it.data.size
) )
} }
Status.ERROR -> { Status.ERROR -> {

View File

@ -66,7 +66,12 @@ class LoginRecoverPresenter @Inject constructor(
showErrorView(false) showErrorView(false)
showCaptcha(false) showCaptcha(false)
} }
Status.SUCCESS -> view?.loadReCaptcha(siteKey = it.data!!.first, url = it.data.second) Status.SUCCESS -> view?.run {
loadReCaptcha(url = it.data!!.first, siteKey = it.data.second)
showProgress(false)
showErrorView(false)
showCaptcha(true)
}
Status.ERROR -> { Status.ERROR -> {
Timber.i("Obtain captcha site key result: An exception occurred") Timber.i("Obtain captcha site key result: An exception occurred")
errorHandler.dispatch(it.error!!) errorHandler.dispatch(it.error!!)

View File

@ -120,15 +120,6 @@ class SettingsFragment : PreferenceFragmentCompat(),
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
override fun showForceSyncDialog() {
AlertDialog.Builder(requireContext())
.setTitle(R.string.pref_services_dialog_force_sync_title)
.setMessage(R.string.pref_services_dialog_force_sync_summary)
.setPositiveButton(android.R.string.ok) { _, _ -> presenter.onForceSyncDialogSubmit() }
.setNegativeButton(android.R.string.cancel) { _, _ -> }
.show()
}
override fun showFixSyncDialog() { override fun showFixSyncDialog() {
AlertDialog.Builder(requireContext()) AlertDialog.Builder(requireContext())
.setTitle(R.string.pref_notify_fix_sync_issues) .setTitle(R.string.pref_notify_fix_sync_issues)

View File

@ -55,14 +55,6 @@ class SettingsPresenter @Inject constructor(
} }
fun onSyncNowClicked() { fun onSyncNowClicked() {
view?.showForceSyncDialog()
}
fun onFixSyncIssuesClicked() {
view?.showFixSyncDialog()
}
fun onForceSyncDialogSubmit() {
view?.run { view?.run {
syncManager.startOneTimeSyncWorker().onEach { workInfo -> syncManager.startOneTimeSyncWorker().onEach { workInfo ->
when (workInfo.state) { when (workInfo.state) {
@ -87,4 +79,8 @@ class SettingsPresenter @Inject constructor(
}.launch("sync") }.launch("sync")
} }
} }
fun onFixSyncIssuesClicked() {
view?.showFixSyncDialog()
}
} }

View File

@ -18,6 +18,5 @@ interface SettingsView : BaseView {
fun setSyncInProgress(inProgress: Boolean) fun setSyncInProgress(inProgress: Boolean)
fun showForceSyncDialog()
fun showFixSyncDialog() fun showFixSyncDialog()
} }

View File

@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
import java.lang.NullPointerException
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDate.now import java.time.LocalDate.now
import java.time.LocalDate.of import java.time.LocalDate.of

View File

@ -1,6 +1,9 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory
/** /**
* [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji] * [UONET+ - Zasady tworzenia podsumowań liczb uczniów obecnych i nieobecnych w tabeli frekwencji]
@ -23,4 +26,15 @@ private fun calculatePercentage(presence: Double, absence: Double): Double {
return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100 return if ((presence + absence) == 0.0) 0.0 else (presence / (presence + absence)) * 100
} }
inline val Attendance.description
get() = when (AttendanceCategory.valueOf(name)) {
AttendanceCategory.PRESENCE -> R.string.attendance_present
AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused
AttendanceCategory.ABSENCE_EXCUSED -> R.string.attendance_absence_excused
AttendanceCategory.UNEXCUSED_LATENESS -> R.string.attendance_unexcused_lateness
AttendanceCategory.EXCUSED_LATENESS -> R.string.attendance_excused_lateness
AttendanceCategory.ABSENCE_FOR_SCHOOL_REASONS -> R.string.attendance_absence_school
AttendanceCategory.EXEMPTION -> R.string.attendance_exemption
AttendanceCategory.DELETED -> R.string.attendance_deleted
else -> R.string.attendance_unknown
}

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.data.Resource import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.Status
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
@ -69,18 +70,20 @@ inline fun <ResultType, RequestType, T> networkBoundResource(
fun <T> flowWithResource(block: suspend () -> T) = flow { fun <T> flowWithResource(block: suspend () -> T) = flow {
emit(Resource.loading()) emit(Resource.loading())
try { emit(try {
emit(Resource.success(block())) Resource.success(block())
} catch (e: Throwable) { } catch (e: Throwable) {
emit(Resource.error(e)) Resource.error(e)
} })
} }
fun <T> flowWithResourceIn(block: suspend () -> Flow<Resource<T>>) = flow { fun <T> flowWithResourceIn(block: suspend () -> Flow<Resource<T>>) = flow {
emit(Resource.loading()) emit(Resource.loading())
try { try {
block().collect { block()
.catch { emit(Resource.error(it)) }
.collect {
if (it.status != Status.LOADING) { // LOADING is already emitted if (it.status != Status.LOADING) { // LOADING is already emitted
emit(it) emit(it)
} }

View File

@ -1,5 +1,4 @@
Wersja 0.20.0 Wersja 0.20.3
- naprawiliśmy obsługę wiadomości - naprawiliśmy opisy wpisów frekwencyjnych, które teraz będą już w całości po polsku
- naprawiliśmy wyświetlanie oznaczenia klasy ucznia w menadżerze kont
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -168,6 +168,8 @@
<string name="attendance_excused_lateness">Spóźnienie usprawiedliwione</string> <string name="attendance_excused_lateness">Spóźnienie usprawiedliwione</string>
<string name="attendance_unexcused_lateness">Spóźnienie nieusprawiedliwione</string> <string name="attendance_unexcused_lateness">Spóźnienie nieusprawiedliwione</string>
<string name="attendance_present">Obecność</string> <string name="attendance_present">Obecność</string>
<string name="attendance_deleted">Usunięty</string>
<string name="attendance_unknown">Nieznany</string>
<string name="attendance_number">Numer lekcji</string> <string name="attendance_number">Numer lekcji</string>
<string name="attendance_no_items">Brak wpisów</string> <string name="attendance_no_items">Brak wpisów</string>
<plurals name="attendance_number_absences"> <plurals name="attendance_number_absences">

View File

@ -10,6 +10,7 @@
<item>Rawa Mazowiecka - Platforma vEdukacja</item> <item>Rawa Mazowiecka - Platforma vEdukacja</item>
<item>Zduńska Wola - e-Urząd</item> <item>Zduńska Wola - e-Urząd</item>
<item>Sieradz - Portal oświatowy</item> <item>Sieradz - Portal oświatowy</item>
<item>Skarżysko-Kamienna - e-Skarżysko</item>
<item>Łask - Platforma vEdukacja</item> <item>Łask - Platforma vEdukacja</item>
<item>Powiat łaski - Platforma edukacyjna</item> <item>Powiat łaski - Platforma edukacyjna</item>
<item>Powiat Krasnostawski - Platforma oświatowa</item> <item>Powiat Krasnostawski - Platforma oświatowa</item>
@ -36,6 +37,7 @@
<item>https://vulcan.net.pl/</item> <item>https://vulcan.net.pl/</item>
<item>https://vulcan.net.pl/</item> <item>https://vulcan.net.pl/</item>
<item>https://vulcan.net.pl/</item> <item>https://vulcan.net.pl/</item>
<item>https://vulcan.net.pl/</item>
<item>http://fakelog.tk/?standard</item> <item>http://fakelog.tk/?standard</item>
</string-array> </string-array>
<string-array name="hosts_symbols"> <string-array name="hosts_symbols">
@ -48,6 +50,7 @@
<item>rawamazowiecka</item> <item>rawamazowiecka</item>
<item>zdunskawola</item> <item>zdunskawola</item>
<item>sieradz</item> <item>sieradz</item>
<item>skarzyskokamienna</item>
<item>lask</item> <item>lask</item>
<item>powiatlaski</item> <item>powiatlaski</item>
<item>powiatkrasnostawski</item> <item>powiatkrasnostawski</item>

View File

@ -170,6 +170,8 @@
<string name="attendance_excused_lateness">Excused lateness</string> <string name="attendance_excused_lateness">Excused lateness</string>
<string name="attendance_unexcused_lateness">Unexcused lateness</string> <string name="attendance_unexcused_lateness">Unexcused lateness</string>
<string name="attendance_present">Present</string> <string name="attendance_present">Present</string>
<string name="attendance_deleted">Deleted</string>
<string name="attendance_unknown">Unknown</string>
<string name="attendance_number">Number of lesson</string> <string name="attendance_number">Number of lesson</string>
<string name="attendance_no_items">No entries</string> <string name="attendance_no_items">No entries</string>
<plurals name="attendance_number_absences"> <plurals name="attendance_number_absences">

View File

@ -63,7 +63,15 @@ class SemesterRepositoryTest {
createSemesterEntity(0, 2, now().minusMonths(3), now()) createSemesterEntity(0, 2, now().minusMonths(3), now())
) )
val goodSemesters = listOf(
createSemesterEntity(122, 1, now().minusMonths(6), now().minusMonths(3)),
createSemesterEntity(123, 2, now().minusMonths(3), now())
)
coEvery { semesterLocal.getSemesters(student) } returns badSemesters coEvery { semesterLocal.getSemesters(student) } returns badSemesters
coEvery { semesterRemote.getSemesters(student) } returns goodSemesters
coEvery { semesterLocal.deleteSemesters(any()) } just Runs
coEvery { semesterLocal.saveSemesters(any()) } just Runs
val items = runBlocking { semesterRepository.getSemesters(student) } val items = runBlocking { semesterRepository.getSemesters(student) }
assertEquals(2, items.size) assertEquals(2, items.size)
@ -152,12 +160,23 @@ class SemesterRepositoryTest {
@Test @Test
fun getSemesters_noCurrent_refreshOnNoCurrent() { fun getSemesters_noCurrent_refreshOnNoCurrent() {
val semesters = listOf( val semestersWithNoCurrent = listOf(
createSemesterEntity(1, 1, now().minusMonths(12), now().minusMonths(6)), createSemesterEntity(1, 1, now().minusMonths(12), now().minusMonths(6)),
createSemesterEntity(1, 2, now().minusMonths(6), now().minusMonths(1)) createSemesterEntity(1, 2, now().minusMonths(6), now().minusMonths(1))
) )
coEvery { semesterLocal.getSemesters(student) } returns semesters val newSemesters = listOf(
createSemesterEntity(1, 1, now().minusMonths(12), now().minusMonths(6)),
createSemesterEntity(1, 2, now().minusMonths(6), now().minusMonths(1)),
createSemesterEntity(2, 1, now().minusMonths(1), now().plusMonths(5)),
createSemesterEntity(2, 2, now().plusMonths(5), now().plusMonths(11)),
)
coEvery { semesterLocal.getSemesters(student) } returns semestersWithNoCurrent
coEvery { semesterRemote.getSemesters(student) } returns newSemesters
coEvery { semesterLocal.deleteSemesters(any()) } just Runs
coEvery { semesterLocal.saveSemesters(any()) } just Runs
val items = runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) } val items = runBlocking { semesterRepository.getSemesters(student, refreshOnNoCurrent = true) }
assertEquals(2, items.size) assertEquals(2, items.size)