1
0

Compare commits

..

19 Commits

Author SHA1 Message Date
5ee7fee09d Merge branch 'release/2.6.6' 2024-05-14 22:30:57 +02:00
594d2dbec5 Version 2.6.6 2024-05-14 22:30:51 +02:00
26596e8254 Merge branch 'release/2.6.5' into develop 2024-05-12 17:51:07 +02:00
ff56348586 Merge branch 'release/2.6.5' 2024-05-12 17:51:02 +02:00
6e472d6c5c Version 2.6.5 2024-05-12 17:50:56 +02:00
6f4826249c Add remote mapping for Wulkanowy SDK (#2550)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
2024-05-12 16:21:28 +02:00
2f3e1b6aae Merge branch 'release/2.6.4' into develop 2024-05-10 12:35:39 +02:00
a69361708e Merge branch 'release/2.6.4' 2024-05-10 12:34:11 +02:00
567d868f76 Version 2.6.4 2024-05-10 12:30:30 +02:00
12030efee2 Version 2.6.3 2024-05-09 22:59:20 +02:00
3cbe98d7b8 Merge branch 'release/2.6.3' 2024-05-09 22:58:03 +02:00
503a97bc4c Version 2.6.3 2024-05-09 22:57:58 +02:00
04c382643d Fix a typo in settings - apperance & behaviour - menu configuration (#2547) 2024-05-09 18:17:10 +02:00
fc140ad9c1 Merge branch 'release/2.6.2' into develop 2024-05-08 01:55:42 +02:00
ae6a35121b Merge branch 'release/2.6.2' 2024-05-08 01:55:38 +02:00
78a2cc89e9 Version 2.6.2 2024-05-08 01:55:09 +02:00
558addd097 Merge branch 'release/2.6.1' 2024-05-02 15:20:51 +02:00
e8f9c57c34 Merge branch 'release/2.6.0' 2024-05-01 22:30:48 +02:00
4d67de8e5f Merge branch 'bugfix/2.5.8' 2024-04-25 12:45:19 +02:00
44 changed files with 229 additions and 358 deletions

View File

@ -27,8 +27,8 @@ android {
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 34
versionCode 161
versionName "2.6.1"
versionCode 166
versionName "2.6.6"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
@ -160,7 +160,7 @@ play {
defaultToAppBundles = false
track = 'production'
releaseStatus = ReleaseStatus.IN_PROGRESS
userFraction = 0.25d
userFraction = 0.99d
updatePriority = 1
enabled.set(false)
}
@ -191,7 +191,7 @@ ext {
}
dependencies {
implementation 'io.github.wulkanowy:sdk:2.6.0'
implementation 'io.github.wulkanowy:sdk:2.6.5'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'

View File

@ -13,8 +13,8 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.api.SchoolsService
import io.github.wulkanowy.data.api.services.SchoolsService
import io.github.wulkanowy.data.api.services.WulkanowyService
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
@ -71,7 +71,7 @@ internal class DataModule {
okHttpClient: OkHttpClient,
json: Json,
appInfo: AppInfo
): AdminMessageService = Retrofit.Builder()
): WulkanowyService = Retrofit.Builder()
.baseUrl(appInfo.messagesBaseUrl)
.client(okHttpClient)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))

View File

@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.StudentDao
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
import io.github.wulkanowy.data.repositories.WulkanowyRepository
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.RemoteConfigHelper
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
@ -20,6 +21,7 @@ class WulkanowySdkFactory @Inject constructor(
private val remoteConfig: RemoteConfigHelper,
private val webkitCookieManagerProxy: WebkitCookieManagerProxy,
private val studentDb: StudentDao,
private val wulkanowyRepository: WulkanowyRepository,
) {
private val eduOneMutex = Mutex()
@ -36,14 +38,30 @@ class WulkanowySdkFactory @Inject constructor(
addInterceptor(chuckerInterceptor, network = true)
}
fun create() = sdk
fun createBase() = sdk
suspend fun create(): Sdk {
val mapping = wulkanowyRepository.getMapping()
return createBase().apply {
if (mapping != null) {
endpointsMapping = mapping.endpoints
vTokenMapping = mapping.vTokens
vTokenSchemeMapping = mapping.vTokenScheme
}
}
}
suspend fun create(student: Student, semester: Semester? = null): Sdk {
val overrideIsEduOne = checkEduOneAndMigrateIfNecessary(student)
return buildSdk(student, semester, overrideIsEduOne)
}
private fun buildSdk(student: Student, semester: Semester?, isStudentEduOne: Boolean): Sdk {
private suspend fun buildSdk(
student: Student,
semester: Semester?,
isStudentEduOne: Boolean
): Sdk {
return create().apply {
email = student.email
password = student.password

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.data.api.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class Mapping(
@SerialName("endpoints")
val endpoints: Map<String, Map<String, Map<String, String>>>,
@SerialName("vTokens")
val vTokens: Map<String, Map<String, Map<String, String>>>,
@SerialName("vTokenScheme")
val vTokenScheme: Map<String, Map<String, String>> = emptyMap(),
)

View File

@ -1,4 +1,4 @@
package io.github.wulkanowy.data.api
package io.github.wulkanowy.data.api.services
import io.github.wulkanowy.data.pojos.IntegrityRequest
import io.github.wulkanowy.data.pojos.LoginEvent

View File

@ -1,12 +1,16 @@
package io.github.wulkanowy.data.api
package io.github.wulkanowy.data.api.services
import io.github.wulkanowy.data.api.models.Mapping
import io.github.wulkanowy.data.db.entities.AdminMessage
import retrofit2.http.GET
import javax.inject.Singleton
@Singleton
interface AdminMessageService {
interface WulkanowyService {
@GET("/v1.json")
suspend fun getAdminMessages(): List<AdminMessage>
}
@GET("/mapping2.json")
suspend fun getMapping(): Mapping
}

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@ -12,16 +11,5 @@ import javax.inject.Singleton
interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun loadWithClassId(studentId: Int, classId: Int): Flow<School?>
@Query("SELECT * FROM School WHERE student_id = :studentId")
fun loadNoClassId(studentId: Int): Flow<School?>
fun load(student: Student): Flow<School?> {
return if (student.isEduOne == true) {
loadNoClassId(student.studentId)
} else {
loadWithClassId(student.studentId, student.classId)
}
}
fun load(studentId: Int, classId: Int): Flow<School?>
}

View File

@ -5,7 +5,6 @@ import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import javax.inject.Singleton
@Singleton
@ -15,17 +14,6 @@ interface SemesterDao : BaseDao<Semester> {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertSemesters(items: List<Semester>): List<Long>
@Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId)")
suspend fun loadAllWithClassId(studentId: Int, classId: Int): List<Semester>
@Query("SELECT * FROM Semesters WHERE student_id = :studentId")
suspend fun loadAllNoClassId(studentId: Int): List<Semester>
suspend fun loadAll(student: Student): List<Semester> {
return if (student.isEduOne == true) {
loadAllNoClassId(student.studentId)
} else {
loadAllWithClassId(student.studentId, student.classId)
}
}
@Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId) OR (student_id = :studentId AND class_id = 0)")
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
}

View File

@ -47,9 +47,13 @@ abstract class StudentDao {
abstract suspend fun loadAll(): List<Student>
@Transaction
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id)")
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0)")
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
@Transaction
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0) WHERE Students.id = :id")
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
abstract suspend fun updateCurrent(id: Long)

View File

@ -2,7 +2,6 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Teacher
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@ -12,16 +11,5 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAllWithClassId(studentId: Int, classId: Int): Flow<List<Teacher>>
@Query("SELECT * FROM Teachers WHERE student_id = :studentId")
fun loadAllNoClassId(studentId: Int): Flow<List<Teacher>>
fun loadAll(student: Student): Flow<List<Teacher>> {
return if (student.isEduOne == true) {
loadAllNoClassId(student.studentId)
} else {
loadAllWithClassId(student.studentId, student.classId)
}
}
fun loadAll(studentId: Int, classId: Int): Flow<List<Teacher>>
}

View File

@ -6,15 +6,6 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration63 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS `Semesters`")
db.execSQL("DROP TABLE IF EXISTS `School`")
db.execSQL("DROP TABLE IF EXISTS `Teachers`")
db.execSQL("CREATE TABLE IF NOT EXISTS `Semesters` (`student_id` INTEGER NOT NULL, `diary_id` INTEGER NOT NULL, `kindergarten_diary_id` INTEGER NOT NULL DEFAULT 0, `diary_name` TEXT NOT NULL, `school_year` INTEGER NOT NULL, `semester_id` INTEGER NOT NULL, `semester_name` INTEGER NOT NULL, `start` INTEGER NOT NULL, `end` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `unit_id` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `is_current` INTEGER NOT NULL)")
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_Semesters_student_id_diary_id_kindergarten_diary_id_semester_id` ON `Semesters` (`student_id`, `diary_id`, `kindergarten_diary_id`, `semester_id`)")
db.execSQL("CREATE TABLE IF NOT EXISTS `School` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `name` TEXT NOT NULL, `address` TEXT NOT NULL, `contact` TEXT NOT NULL, `headmaster` TEXT NOT NULL, `pedagogue` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
db.execSQL("CREATE TABLE IF NOT EXISTS `Teachers` (`student_id` INTEGER NOT NULL, `class_id` INTEGER NOT NULL, `subject` TEXT NOT NULL, `name` TEXT NOT NULL, `short_name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)")
db.execSQL("UPDATE Students SET is_edu_one = NULL")
db.execSQL("UPDATE Students SET is_edu_one = NULL WHERE is_edu_one = 0")
}
}

View File

@ -1,4 +0,0 @@
package io.github.wulkanowy.data.exceptions
class NoSuchStudentException(id: Long) :
Exception("There is no student with id $id in database")

View File

@ -9,6 +9,7 @@ import com.fredporciuncula.flow.preferences.Preference
import com.fredporciuncula.flow.preferences.Serializer
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.api.models.Mapping
import io.github.wulkanowy.data.enums.AppTheme
import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode
import io.github.wulkanowy.data.enums.GradeColorTheme
@ -375,6 +376,15 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
var mapping: Mapping?
get() {
val value = sharedPref.getString("mapping", null)
return value?.let { json.decodeFromString(it) }
}
set(value) = sharedPref.edit(commit = true) {
putString("mapping", value?.let { json.encodeToString(it) })
}
init {
if (installationId.isEmpty()) {
installationId = UUID.randomUUID().toString()

View File

@ -36,7 +36,7 @@ class SchoolRepository @Inject constructor(
)
it == null || forceRefresh || isExpired
},
query = { schoolDb.load(student) },
query = { schoolDb.load(semester.studentId, semester.classId) },
fetch = {
wulkanowySdkFactory.create(student, semester)
.getSchool()

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.WulkanowySdkFactory
import io.github.wulkanowy.data.api.SchoolsService
import io.github.wulkanowy.data.api.services.SchoolsService
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters

View File

@ -27,11 +27,11 @@ class SemesterRepository @Inject constructor(
forceRefresh: Boolean = false,
refreshOnNoCurrent: Boolean = false
) = withContext(dispatchers.io) {
val semesters = semesterDb.loadAll(student)
val semesters = semesterDb.loadAll(student.studentId, student.classId)
if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) {
refreshSemesters(student)
semesterDb.loadAll(student)
semesterDb.loadAll(student.studentId, student.classId)
} else semesters
}
@ -69,7 +69,7 @@ class SemesterRepository @Inject constructor(
return
}
val old = semesterDb.loadAll(student)
val old = semesterDb.loadAll(student.studentId, student.classId)
semesterDb.removeOldAndSaveNew(
oldItems = old uniqueSubtract new,
newItems = new uniqueSubtract old,

View File

@ -12,7 +12,6 @@ import io.github.wulkanowy.data.db.entities.StudentName
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
import io.github.wulkanowy.data.exceptions.NoSuchStudentException
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToPojo
import io.github.wulkanowy.data.pojos.RegisterUser
@ -66,8 +65,7 @@ class StudentRepository @Inject constructor(
.mapToPojo(password)
.also { it.logErrors() }
@Deprecated("Semesters are not synced within this method and students with empty semesters are not returned")
suspend fun getSavedStudentsWithSemesters(decryptPass: Boolean = true): List<StudentWithSemesters> {
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
StudentWithSemesters(
student = student.apply {
@ -82,25 +80,22 @@ class StudentRepository @Inject constructor(
}
}
suspend fun getSavedStudents(decryptPass: Boolean = true): List<Student> {
val students = studentDb.loadAll()
if (!decryptPass) return students
return students.map { student ->
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
return@map student
}
student.apply {
password = withContext(dispatchers.io) {
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true): StudentWithSemesters? =
studentDb.loadStudentWithSemestersById(id).let { res ->
StudentWithSemesters(
student = res.keys.firstOrNull() ?: return null,
semesters = res.values.first(),
)
}.apply {
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
scrambler.decrypt(student.password)
}
}
}
}
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
val student = studentDb.loadById(id) ?: throw NoSuchStudentException(id)
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
@ -128,7 +123,7 @@ class StudentRepository @Inject constructor(
return
}
val currentStudentSemesters = semesterDb.loadAll(student)
val currentStudentSemesters = semesterDb.loadAll(student.studentId, student.classId)
if (currentStudentSemesters.isEmpty()) {
Timber.d("Check isAuthorized: apply empty semesters workaround")
semesterDb.insertSemesters(
@ -186,8 +181,8 @@ class StudentRepository @Inject constructor(
}
}
suspend fun switchStudent(student: Student) {
studentDb.switchCurrent(student.id)
suspend fun switchStudent(studentWithSemesters: StudentWithSemesters) {
studentDb.switchCurrent(studentWithSemesters.student.id)
}
suspend fun logoutStudent(student: Student) = studentDb.delete(student)
@ -195,8 +190,8 @@ class StudentRepository @Inject constructor(
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
studentDb.update(studentNickAndAvatar)
suspend fun isOneUniqueStudent() = studentDb.loadAll()
.distinctBy { it.studentName }.size == 1
suspend fun isOneUniqueStudent() = getSavedStudents(false)
.distinctBy { it.student.studentName }.size == 1
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
wulkanowySdkFactory.create(student, semester)
@ -204,7 +199,7 @@ class StudentRepository @Inject constructor(
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
.getOrNull() ?: return
@ -214,7 +209,7 @@ class StudentRepository @Inject constructor(
studentDb.update(studentName)
semesterDb.removeOldAndSaveNew(
oldItems = semesterDb.loadAll(student),
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
)
}

View File

@ -35,7 +35,7 @@ class TeacherRepository @Inject constructor(
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
it.isEmpty() || forceRefresh || isExpired
},
query = { teacherDb.loadAll(student) },
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
fetch = {
wulkanowySdkFactory.create(student, semester)
.getTeachers()

View File

@ -1,20 +1,23 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.api.models.Mapping
import io.github.wulkanowy.data.api.services.WulkanowyService
import io.github.wulkanowy.data.db.dao.AdminMessageDao
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.networkBoundResource
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.sync.Mutex
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AdminMessageRepository @Inject constructor(
private val adminMessageService: AdminMessageService,
class WulkanowyRepository @Inject constructor(
private val wulkanowyService: WulkanowyService,
private val adminMessageDao: AdminMessageDao,
private val preferencesRepository: PreferencesRepository,
) {
private val saveFetchResultMutex = Mutex()
@ -24,11 +27,28 @@ class AdminMessageRepository @Inject constructor(
mutex = saveFetchResultMutex,
isResultEmpty = { false },
query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() },
fetch = { wulkanowyService.getAdminMessages() },
shouldFetch = { true },
saveFetchResult = { oldItems, newItems ->
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
},
)
.filterNot { it is Resource.Intermediate }
suspend fun getMapping(): Mapping? {
var savedMapping = preferencesRepository.mapping
if (savedMapping == null) {
fetchMapping()
savedMapping = preferencesRepository.mapping
}
return savedMapping
}
suspend fun fetchMapping() {
runCatching { wulkanowyService.getMapping() }
.onFailure { Timber.e(it) }
.onSuccess { preferencesRepository.mapping = it }
}
}

View File

@ -5,14 +5,14 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageType
import io.github.wulkanowy.data.mapResourceData
import io.github.wulkanowy.data.repositories.AdminMessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.WulkanowyRepository
import io.github.wulkanowy.utils.AppInfo
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class GetAppropriateAdminMessageUseCase @Inject constructor(
private val adminMessageRepository: AdminMessageRepository,
private val wulkanowyRepository: WulkanowyRepository,
private val preferencesRepository: PreferencesRepository,
private val appInfo: AppInfo
) {
@ -22,7 +22,7 @@ class GetAppropriateAdminMessageUseCase @Inject constructor(
}
operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow<Resource<AdminMessage?>> {
return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages ->
return wulkanowyRepository.getAdminMessages().mapResourceData { adminMessages ->
adminMessages
.asSequence()
.filter { it.isNotDismissed() }

View File

@ -59,7 +59,7 @@ class GetMailboxByStudentUseCase @Inject constructor(
private fun String.getUnauthorizedVersion(): String {
return normalizeStudentName().split(" ")
.joinToString(" ") {
it.first() + "*".repeat(it.length - 1)
it.firstOrNull()?.toString().orEmpty() + "*".repeat((it.length - 1).coerceAtLeast(0))
}
}
}

View File

@ -33,7 +33,7 @@ class AccountPresenter @Inject constructor(
}
private fun loadData() {
resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
resourceFlow { studentRepository.getSavedStudents(false) }
.logResourceStatus("load account data")
.onResourceSuccess { view?.updateData(createAccountItems(it)) }
.onResourceError(errorHandler::dispatch)

View File

@ -1,15 +1,9 @@
package io.github.wulkanowy.ui.modules.account.accountdetails
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceLoading
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
@ -20,7 +14,6 @@ import javax.inject.Inject
class AccountDetailsPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semeRepository: SemesterRepository,
private val syncManager: SyncManager
) : BasePresenter<AccountDetailsView>(errorHandler, studentRepository) {
@ -53,12 +46,7 @@ class AccountDetailsPresenter @Inject constructor(
}
private fun loadData() {
resourceFlow {
val student = studentRepository.getStudentById(studentId ?: -1)
val semesters = semeRepository.getSemesters(student)
StudentWithSemesters(student, semesters)
}
resourceFlow { studentRepository.getSavedStudentById(studentId ?: -1) }
.logResourceStatus("loading account details view")
.onResourceLoading {
view?.run {
@ -97,7 +85,7 @@ class AccountDetailsPresenter @Inject constructor(
Timber.i("Select student ${studentWithSemesters!!.student.id}")
resourceFlow { studentRepository.switchStudent(studentWithSemesters!!.student) }
resourceFlow { studentRepository.switchStudent(studentWithSemesters!!) }
.logResourceStatus("change student")
.onResourceSuccess { view?.recreateMainView() }
.onResourceNotLoading { view?.popViewToMain() }
@ -134,12 +122,10 @@ class AccountDetailsPresenter @Inject constructor(
syncManager.stopSyncWorker()
openClearLoginView()
}
studentWithSemesters?.student?.isCurrent == true -> {
Timber.i("Logout result: Logout student and switch to another")
recreateMainView()
}
else -> {
Timber.i("Logout result: Logout student")
recreateMainView()

View File

@ -1,12 +1,8 @@
package io.github.wulkanowy.ui.modules.account.accountquick
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.account.AccountItem
@ -44,7 +40,7 @@ class AccountQuickPresenter @Inject constructor(
return
}
resourceFlow { studentRepository.switchStudent(studentWithSemesters.student) }
resourceFlow { studentRepository.switchStudent(studentWithSemesters) }
.logResourceStatus("change student")
.onResourceSuccess { view?.recreateMainView() }
.onResourceNotLoading { view?.popView() }

View File

@ -59,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
webView = this
with(settings) {
javaScriptEnabled = true
userAgentString = wulkanowySdkFactory.create().userAgent
userAgentString = wulkanowySdkFactory.createBase().userAgent
}
webViewClient = object : WebViewClient() {

View File

@ -118,5 +118,6 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
override fun onResume() {
super.onResume()
inAppUpdateHelper.onResume()
presenter.updateSdkMappings()
}
}

View File

@ -1,12 +1,15 @@
package io.github.wulkanowy.ui.modules.login
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.WulkanowyRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
class LoginPresenter @Inject constructor(
private val wulkanowyRepository: WulkanowyRepository,
errorHandler: ErrorHandler,
studentRepository: StudentRepository
) : BasePresenter<LoginView>(errorHandler, studentRepository) {
@ -16,4 +19,11 @@ class LoginPresenter @Inject constructor(
view.initView()
Timber.i("Login view was initialized")
}
fun updateSdkMappings() {
presenterScope.launch {
runCatching { wulkanowyRepository.fetchMapping() }
.onFailure { Timber.e(it) }
}
}
}

View File

@ -80,7 +80,7 @@ class LoginStudentSelectPresenter @Inject constructor(
private fun loadData() {
resetSelectedState()
resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }.onEach {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
students = it.dataOrNull.orEmpty()
when (it) {
is Resource.Loading -> Timber.d("Login student select students load started")

View File

@ -35,7 +35,7 @@ class LuckyNumberWidgetConfigurePresenter @Inject constructor(
}
private fun loadData() {
resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }.onEach {
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
when (it) {
is Resource.Loading -> Timber.d("Lucky number widget configure students data load")
is Resource.Success -> {

View File

@ -132,7 +132,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking {
try {
val students = studentRepository.getSavedStudents()
val student = students.singleOrNull { it.id == studentId }
val student = students.singleOrNull { it.student.id == studentId }?.student
val currentStudent = when {
student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> {

View File

@ -138,6 +138,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
override fun onResume() {
super.onResume()
inAppUpdateHelper.onResume()
presenter.updateSdkMappings()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {

View File

@ -6,6 +6,7 @@ import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.WulkanowyRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter
@ -29,6 +30,7 @@ class MainPresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository,
private val wulkanowyRepository: WulkanowyRepository,
private val syncManager: SyncManager,
private val analytics: AnalyticsHelper,
private val json: Json,
@ -85,7 +87,7 @@ class MainPresenter @Inject constructor(
return
}
resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
resourceFlow { studentRepository.getSavedStudents(false) }
.logResourceStatus("load student avatar")
.onResourceSuccess {
studentsWitSemesters = it
@ -199,4 +201,11 @@ class MainPresenter @Inject constructor(
.onFailure { errorHandler.dispatch(it) }
}
}
fun updateSdkMappings() {
presenterScope.launch {
runCatching { wulkanowyRepository.fetchMapping() }
.onFailure { Timber.e(it) }
}
}
}

View File

@ -42,24 +42,22 @@ class TimetableWidgetConfigurePresenter @Inject constructor(
}
private fun loadData() {
resourceFlow { studentRepository.getSavedStudentsWithSemesters(false) }
.onEach {
when (it) {
is Resource.Loading -> Timber.d("Timetable widget configure students data load")
is Resource.Success -> {
val selectedStudentId = appWidgetId?.let { id ->
sharedPref.getLong(getStudentWidgetKey(id), 0)
} ?: -1
when {
it.data.isEmpty() -> view?.openLoginView()
it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student)
else -> view?.updateData(it.data, selectedStudentId)
}
resourceFlow { studentRepository.getSavedStudents(false) }.onEach {
when (it) {
is Resource.Loading -> Timber.d("Timetable widget configure students data load")
is Resource.Success -> {
val selectedStudentId = appWidgetId?.let { id ->
sharedPref.getLong(getStudentWidgetKey(id), 0)
} ?: -1
when {
it.data.isEmpty() -> view?.openLoginView()
it.data.size == 1 && !isFromProvider -> onItemSelect(it.data.single().student)
else -> view?.updateData(it.data, selectedStudentId)
}
is Resource.Error -> errorHandler.dispatch(it.error)
}
}.launch()
is Resource.Error -> errorHandler.dispatch(it.error)
}
}.launch()
}
private fun registerStudent(student: Student?) {

View File

@ -95,7 +95,7 @@ class TimetableWidgetFactory(
private suspend fun getStudent(studentId: Long): Student? {
val students = studentRepository.getSavedStudents()
return students.singleOrNull { it.id == studentId }
return students.singleOrNull { it.student.id == studentId }?.student
}
private suspend fun getLessons(

View File

@ -2,11 +2,7 @@ package io.github.wulkanowy.ui.modules.timetablewidget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_DELETED
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID
import android.appwidget.AppWidgetManager.*
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -26,14 +22,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.capitalise
import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.previousSchoolDay
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@ -255,7 +244,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
private suspend fun getStudent(studentId: Long, appWidgetId: Int) = try {
val students = studentRepository.getSavedStudents(false)
val student = students.singleOrNull { it.id == studentId }
val student = students.singleOrNull { it.student.id == studentId }?.student
when {
student != null -> student
studentId != 0L && studentRepository.isCurrentStudentSet() -> {
@ -274,10 +263,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
}
private fun setupAccountView(
context: Context,
student: Student,
remoteViews: RemoteViews,
widgetId: Int
context: Context, student: Student, remoteViews: RemoteViews, widgetId: Int
) {
val accountInitials = getAccountInitials(student.nickOrName)
val accountPickerPendingIntent = createAccountPickerPendingIntent(context, widgetId)

View File

@ -1,8 +1,5 @@
Wersja 2.6.1
Wersja 2.6.6
dodaliśmy kalkulator frekwencji
— dodaliśmy wyświetlanie lekcji dodatkowych w planie lekcji
— ulepszyliśmy wyjaśnienie na ekranie z miejscem na wpisanie numeru PESEL
— naprawiliśmy rzadkie sytuacje, gdy plan lekcji nakładał się na informację o jego braku
to tylko kolejna aktualizacja, która obchodzi blokadę firmy, której nazwy nie napiszemy. Pewnie zaraz znowu zostaniemy zablokowani, ale co tam
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -28,7 +28,7 @@
<string name="student_info_title">Student info</string>
<string name="dashboard_title">Dashboard</string>
<string name="notifications_center_title">Notifications center</string>
<string name="menu_order_title">Menu configuartion</string>
<string name="menu_order_title">Menu configuration</string>
<!--Subtitles-->

View File

@ -8,6 +8,7 @@ import io.mockk.mockk
fun createWulkanowySdkFactoryMock(sdk: Sdk) = mockk<WulkanowySdkFactory>()
.apply {
every { create() } returns sdk
every { createBase() } returns sdk
coEvery { create() } returns sdk
coEvery { create(any(), any()) } returns sdk
}

View File

@ -11,7 +11,6 @@ import io.github.wulkanowy.sdk.pojo.RegisterStudent
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.spyk
@ -40,11 +39,12 @@ class WulkanowySdkFactoryTest {
chuckerInterceptor = mockk(),
remoteConfig = mockk(relaxed = true),
webkitCookieManagerProxy = mockk(),
studentDb = studentDao
studentDb = studentDao,
wulkanowyRepository = mockk(relaxed = true),
)
)
every { wulkanowySdkFactory.create() } returns sdk
coEvery { wulkanowySdkFactory.create() } returns sdk
}
@Test

View File

@ -1,161 +0,0 @@
package io.github.wulkanowy.data.db.dao
import android.content.Context
import android.os.Build
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import dagger.hilt.android.testing.HiltTestApplication
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.test.Test
import kotlin.test.assertEquals
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
class StudentDaoTest {
private lateinit var studentDao: StudentDao
private lateinit var semesterDao: SemesterDao
private lateinit var db: AppDatabase
@Before
fun createDb() {
val context: Context = ApplicationProvider.getApplicationContext()
db = Room.inMemoryDatabaseBuilder(
context = context,
klass = AppDatabase::class.java
).build()
studentDao = db.studentDao
semesterDao = db.semesterDao
}
@Test
fun `get students associated with correct semester with same studentId`() = runTest {
val notEduOneStudent = getStudentEntity()
.copy(
isEduOne = false,
classId = 42,
studentId = 100
)
.apply { id = 1 }
val eduOneStudent = getStudentEntity()
.copy(
isEduOne = true,
classId = 0,
studentId = 100
)
.apply { id = 2 }
val semesterAssociatedWithNotEduOneStudent = getSemesterEntity()
.copy(
studentId = notEduOneStudent.studentId,
classId = notEduOneStudent.classId,
diaryId = 1 // make semester unique
)
.apply { id = 0 }
val semesterAssociatedWithEduOneStudent = getSemesterEntity()
.copy(
studentId = eduOneStudent.studentId,
classId = eduOneStudent.classId,
diaryId = 2 // make semester unique
)
.apply { id = 0 }
studentDao.insertAll(listOf(notEduOneStudent, eduOneStudent))
semesterDao.insertAll(
listOf(
semesterAssociatedWithNotEduOneStudent,
semesterAssociatedWithEduOneStudent
)
)
val studentsWithSemesters = studentDao.loadStudentsWithSemesters()
val notEduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == notEduOneStudent.id }
?.value
val eduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == eduOneStudent.id }
?.value
assertEquals(2, studentsWithSemesters.size)
assertEquals(1, notEduOneSemestersResult?.size)
assertEquals(1, eduOneSemestersResult?.size)
assertEquals(semesterAssociatedWithEduOneStudent, eduOneSemestersResult?.firstOrNull())
assertEquals(
semesterAssociatedWithNotEduOneStudent,
notEduOneSemestersResult?.firstOrNull()
)
}
@Test
fun `get students associated with correct semester with different studentId`() = runTest {
val notEduOneStudent = getStudentEntity()
.copy(
isEduOne = false,
classId = 42,
studentId = 100
)
.apply { id = 1 }
val eduOneStudent = getStudentEntity()
.copy(
isEduOne = true,
classId = 0,
studentId = 101
)
.apply { id = 2 }
val semesterAssociatedWithNotEduOneStudent = getSemesterEntity()
.copy(
studentId = notEduOneStudent.studentId,
classId = notEduOneStudent.classId,
)
.apply { id = 0 }
val semesterAssociatedWithEduOneStudent = getSemesterEntity()
.copy(
studentId = eduOneStudent.studentId,
classId = eduOneStudent.classId,
)
.apply { id = 0 }
studentDao.insertAll(listOf(notEduOneStudent, eduOneStudent))
semesterDao.insertAll(
listOf(
semesterAssociatedWithNotEduOneStudent,
semesterAssociatedWithEduOneStudent
)
)
val studentsWithSemesters = studentDao.loadStudentsWithSemesters()
val notEduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == notEduOneStudent.id }
?.value
val eduOneSemestersResult = studentsWithSemesters.entries
.find { (student, _) -> student.id == eduOneStudent.id }
?.value
assertEquals(2, studentsWithSemesters.size)
assertEquals(1, notEduOneSemestersResult?.size)
assertEquals(1, eduOneSemestersResult?.size)
assertEquals(semesterAssociatedWithEduOneStudent, eduOneSemestersResult?.firstOrNull())
assertEquals(
semesterAssociatedWithNotEduOneStudent,
notEduOneSemestersResult?.firstOrNull()
)
}
@After
fun closeDb() {
db.close()
}
}

View File

@ -12,7 +12,9 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import kotlin.random.Random
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue
@HiltAndroidTest
@RunWith(RobolectricTestRunner::class)
@ -20,10 +22,9 @@ import kotlin.test.assertNull
class Migration63Test : AbstractMigrationTest() {
@Test
fun `update is_edu_one to null`() = runTest {
fun `update is_edu_one to null if 0`() = runTest {
with(helper.createDatabase(dbName, 62)) {
createStudent(1, 0)
createStudent(2, 1)
close()
}
@ -31,15 +32,31 @@ class Migration63Test : AbstractMigrationTest() {
val database = getMigratedRoomDatabase()
val studentDb = database.studentDao
val student1 = studentDb.loadById(1)
val student2 = studentDb.loadById(2)
val student = studentDb.loadById(1)
assertNull(student1!!.isEduOne)
assertNull(student2!!.isEduOne)
assertNull(student!!.isEduOne)
database.close()
}
@Test
fun `check is_edu_one is stay same`() = runTest {
with(helper.createDatabase(dbName, 62)) {
createStudent(1, 1)
close()
}
helper.runMigrationsAndValidate(dbName, 63, true)
val database = getMigratedRoomDatabase()
val studentDb = database.studentDao
val student = studentDb.loadById(1)
val isEduOne = assertNotNull(student!!.isEduOne)
assertTrue(isEduOne)
database.close()
}
private fun SupportSQLiteDatabase.createStudent(id: Long, isEduOneValue: Int) {
insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("scrapper_base_url", "https://fakelog.cf")

View File

@ -50,7 +50,7 @@ class SemesterRepositoryTest {
getSemesterPojo(1, 2, now().minusMonths(3), now())
)
coEvery { semesterDb.loadAll(student) } returns emptyList()
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
@ -77,7 +77,10 @@ class SemesterRepositoryTest {
)
coEvery {
semesterDb.loadAll(student)
semesterDb.loadAll(
student.studentId,
student.classId
)
} returns badSemesters.mapToEntities(student.studentId)
coEvery { sdk.getSemesters() } returns goodSemesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
@ -100,7 +103,7 @@ class SemesterRepositoryTest {
getSemesterPojo(2, 3, now(), now().plusMonths(6)),
)
coEvery { semesterDb.loadAll(any()) } returnsMany listOf(
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returnsMany listOf(
badSemesters.mapToEntities(student.studentId),
badSemesters.mapToEntities(student.studentId),
goodSemesters.mapToEntities(student.studentId)
@ -122,7 +125,7 @@ class SemesterRepositoryTest {
getSemesterEntity(1, 2, now().minusMonths(6), now().minusMonths(1))
)
coEvery { semesterDb.loadAll(student) } returns semesters
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semesters
val items = runBlocking { semesterRepository.getSemesters(student) }
assertEquals(2, items.size)
@ -135,7 +138,7 @@ class SemesterRepositoryTest {
getSemesterEntity(1, 2, now().minusMonths(3), now())
)
coEvery { semesterDb.loadAll(student) } returns semesters
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semesters
val items = runBlocking { semesterRepository.getSemesters(student) }
assertEquals(2, items.size)
@ -148,7 +151,7 @@ class SemesterRepositoryTest {
getSemesterEntity(1, 2, now(), now())
)
coEvery { semesterDb.loadAll(student) } returns semesters
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semesters
val items = runBlocking { semesterRepository.getSemesters(student) }
assertEquals(2, items.size)
@ -161,7 +164,7 @@ class SemesterRepositoryTest {
getSemesterPojo(1, 2, now().minusMonths(3), now())
)
coEvery { semesterDb.loadAll(student) } returns emptyList()
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns semesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
@ -191,7 +194,10 @@ class SemesterRepositoryTest {
)
coEvery {
semesterDb.loadAll(student)
semesterDb.loadAll(
student.studentId,
student.classId
)
} returns semestersWithNoCurrent
coEvery { sdk.getSemesters() } returns newSemesters
coEvery { semesterDb.removeOldAndSaveNew(any(), any()) } just Runs
@ -208,7 +214,7 @@ class SemesterRepositoryTest {
getSemesterEntity(1, 2, now().minusMonths(1), now().plusMonths(1))
)
coEvery { semesterDb.loadAll(student) } returns semesters
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semesters
val items = semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
assertEquals(2, items.size)
@ -221,14 +227,14 @@ class SemesterRepositoryTest {
getSemesterEntity(1, 1, now(), now())
)
coEvery { semesterDb.loadAll(student) } returns semesters
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns semesters
runBlocking { semesterRepository.getCurrentSemester(student) }
}
@Test(expected = RuntimeException::class)
fun getCurrentSemester_emptyList() {
coEvery { semesterDb.loadAll(student) } returns emptyList()
coEvery { semesterDb.loadAll(student.studentId, student.classId) } returns emptyList()
coEvery { sdk.getSemesters() } returns emptyList()
runBlocking { semesterRepository.getCurrentSemester(student) }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.main
import io.github.wulkanowy.MainCoroutineRule
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.WulkanowyRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AdsHelper
@ -31,6 +32,9 @@ class MainPresenterTest {
@MockK
lateinit var studentRepository: StudentRepository
@MockK(relaxed = true)
lateinit var wulkanowyRepository: WulkanowyRepository
@MockK(relaxed = true)
lateinit var prefRepository: PreferencesRepository
@ -65,7 +69,8 @@ class MainPresenterTest {
analytics = analytics,
json = Json,
appInfo = appInfo,
adsHelper = adsHelper
adsHelper = adsHelper,
wulkanowyRepository = wulkanowyRepository
)
presenter.onAttachView(mainView, null)
}