Compare commits
43 Commits
Author | SHA1 | Date | |
---|---|---|---|
4d67de8e5f | |||
f983a23b1a | |||
6a1851da13 | |||
8dbbea2138 | |||
f6226e6b53 | |||
43d13db07c | |||
82210c37e3 | |||
b5cc32d59f | |||
d943d03266 | |||
6eca8c42f5 | |||
af989ba9f6 | |||
4a65a5b192 | |||
bbbafdfe70 | |||
a82e11d694 | |||
4dc5fc65ac | |||
7463cf6253 | |||
d799ec7ac9 | |||
254719f22f | |||
e1e276e1ea | |||
8cdd4311a9 | |||
b7f7b16aef | |||
2e71c50894 | |||
b3faac01a5 | |||
3881678208 | |||
76d038eefa | |||
3a55c3c760 | |||
a0818de7d1 | |||
b280316b07 | |||
0554aa91fd | |||
5a77d1e940 | |||
c9a42a6cf6 | |||
e17129efea | |||
6047af9ff0 | |||
d789aa718e | |||
8623b53357 | |||
78e28ad791 | |||
377c288e9e | |||
b31c7e1720 | |||
d01fe9c370 | |||
5ed19cb21a | |||
0a1f7270b4 | |||
47d8513a77 | |||
00432ab911 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -67,6 +67,10 @@ captures/
|
||||
.idea/discord.xml
|
||||
.idea/migrations.xml
|
||||
.idea/androidTestResultsUserPreferences.xml
|
||||
.idea/copilot
|
||||
.idea/deploymentTargetDropDown.xml
|
||||
.idea/deploymentTargetSelector.xml
|
||||
.idea/kotlinc.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
|
@ -27,8 +27,8 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 149
|
||||
versionName "2.5.0"
|
||||
versionCode 157
|
||||
versionName "2.5.8"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
@ -164,8 +164,8 @@ play {
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.20d
|
||||
updatePriority = 1
|
||||
userFraction = 0.99d
|
||||
updatePriority = 4
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
@ -195,7 +195,7 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'io.github.wulkanowy:sdk:2.5.0'
|
||||
implementation 'io.github.wulkanowy:sdk:2.5.8'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||
|
||||
|
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/62.json
Normal file
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/62.json
Normal file
File diff suppressed because it is too large
Load Diff
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/63.json
Normal file
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/63.json
Normal file
File diff suppressed because it is too large
Load Diff
2559
app/schemas/io.github.wulkanowy.data.db.AppDatabase/64.json
Normal file
2559
app/schemas/io.github.wulkanowy.data.db.AppDatabase/64.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -18,17 +18,13 @@ import io.github.wulkanowy.data.api.SchoolsService
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.create
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -36,23 +32,6 @@ import javax.inject.Singleton
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal class DataModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSdk(
|
||||
chuckerInterceptor: ChuckerInterceptor,
|
||||
remoteConfig: RemoteConfigHelper,
|
||||
webkitCookieManagerProxy: WebkitCookieManagerProxy,
|
||||
) = Sdk().apply {
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
setAdditionalCookieManager(webkitCookieManagerProxy)
|
||||
|
||||
// for debug only
|
||||
addInterceptor(chuckerInterceptor, network = true)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideChuckerCollector(
|
||||
|
@ -0,0 +1,125 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
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.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class WulkanowySdkFactory @Inject constructor(
|
||||
private val chuckerInterceptor: ChuckerInterceptor,
|
||||
private val remoteConfig: RemoteConfigHelper,
|
||||
private val webkitCookieManagerProxy: WebkitCookieManagerProxy,
|
||||
private val studentDb: StudentDao,
|
||||
) {
|
||||
|
||||
private val eduOneMutex = Mutex()
|
||||
private val migrationFailedStudentIds = mutableSetOf<Long>()
|
||||
|
||||
private val sdk = Sdk().apply {
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
setAdditionalCookieManager(webkitCookieManagerProxy)
|
||||
|
||||
// for debug only
|
||||
addInterceptor(chuckerInterceptor, network = true)
|
||||
}
|
||||
|
||||
fun create() = sdk
|
||||
|
||||
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 {
|
||||
return create().apply {
|
||||
email = student.email
|
||||
password = student.password
|
||||
symbol = student.symbol
|
||||
schoolSymbol = student.schoolSymbol
|
||||
studentId = student.studentId
|
||||
classId = student.classId
|
||||
emptyCookieJarInterceptor = true
|
||||
isEduOne = isStudentEduOne
|
||||
|
||||
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
||||
mobileBaseUrl = student.mobileBaseUrl
|
||||
} else {
|
||||
scrapperBaseUrl = student.scrapperBaseUrl
|
||||
domainSuffix = student.scrapperDomainSuffix
|
||||
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
|
||||
}
|
||||
|
||||
mode = Sdk.Mode.valueOf(student.loginMode)
|
||||
mobileBaseUrl = student.mobileBaseUrl
|
||||
keyId = student.certificateKey
|
||||
privatePem = student.privateKey
|
||||
|
||||
if (semester != null) {
|
||||
diaryId = semester.diaryId
|
||||
kindergartenDiaryId = semester.kindergartenDiaryId
|
||||
schoolYear = semester.schoolYear
|
||||
unitId = semester.unitId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkEduOneAndMigrateIfNecessary(student: Student): Boolean {
|
||||
if (student.isEduOne != null) return student.isEduOne
|
||||
|
||||
if (student.id in migrationFailedStudentIds) {
|
||||
Timber.i("Migration eduOne: skipping because of previous failure")
|
||||
return false
|
||||
}
|
||||
|
||||
eduOneMutex.withLock {
|
||||
if (student.id in migrationFailedStudentIds) {
|
||||
Timber.i("Migration eduOne: skipping because of previous failure")
|
||||
return false
|
||||
}
|
||||
|
||||
val studentFromDatabase = studentDb.loadById(student.id)
|
||||
if (studentFromDatabase?.isEduOne != null) {
|
||||
Timber.i("Migration eduOne: already done")
|
||||
return studentFromDatabase.isEduOne
|
||||
}
|
||||
|
||||
Timber.i("Migration eduOne: flag missing. Running migration...")
|
||||
val initializedSdk = buildSdk(
|
||||
student = student,
|
||||
semester = null,
|
||||
isStudentEduOne = false, // doesn't matter
|
||||
)
|
||||
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
||||
.onFailure { Timber.e(it, "Migration eduOne: can't get current student") }
|
||||
.getOrNull()
|
||||
|
||||
if (newCurrentStudent == null) {
|
||||
Timber.i("Migration eduOne: failed, so skipping")
|
||||
migrationFailedStudentIds.add(student.id)
|
||||
return false
|
||||
}
|
||||
|
||||
Timber.i("Migration eduOne: success. New isEduOne flag: ${newCurrentStudent.isEduOne}")
|
||||
|
||||
val studentIsEduOne = StudentIsEduOne(
|
||||
id = student.id,
|
||||
isEduOne = newCurrentStudent.isEduOne
|
||||
)
|
||||
studentDb.update(studentIsEduOne)
|
||||
return newCurrentStudent.isEduOne
|
||||
}
|
||||
}
|
||||
}
|
@ -120,6 +120,7 @@ import io.github.wulkanowy.data.db.migrations.Migration55
|
||||
import io.github.wulkanowy.data.db.migrations.Migration57
|
||||
import io.github.wulkanowy.data.db.migrations.Migration58
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
import io.github.wulkanowy.data.db.migrations.Migration63
|
||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||
@ -174,6 +175,9 @@ import javax.inject.Singleton
|
||||
AutoMigration(from = 58, to = 59),
|
||||
AutoMigration(from = 59, to = 60),
|
||||
AutoMigration(from = 60, to = 61),
|
||||
AutoMigration(from = 61, to = 62),
|
||||
AutoMigration(from = 62, to = 63, spec = Migration63::class),
|
||||
AutoMigration(from = 63, to = 64),
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
@ -182,7 +186,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 61
|
||||
const val VERSION_SCHEMA = 64
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -309,6 +313,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract val adminMessagesDao: AdminMessageDao
|
||||
|
||||
abstract val mutedMessageSendersDao: MutedMessageSendersDao
|
||||
|
||||
|
||||
abstract val gradeDescriptiveDao: GradeDescriptiveDao
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
@Dao
|
||||
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
||||
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>>
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||
}
|
||||
|
@ -14,6 +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")
|
||||
@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>
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||
import io.github.wulkanowy.data.db.entities.StudentName
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import javax.inject.Singleton
|
||||
@ -23,6 +25,12 @@ abstract class StudentDao {
|
||||
@Delete
|
||||
abstract suspend fun delete(student: Student)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentIsAuthorized: StudentIsAuthorized)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentIsEduOne: StudentIsEduOne)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||
|
||||
@ -39,11 +47,11 @@ 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 WHERE Students.id = :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) WHERE Students.id = :id")
|
||||
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
|
||||
|
||||
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||
|
@ -33,7 +33,13 @@ data class GradeSummary(
|
||||
@ColumnInfo(name = "points_sum")
|
||||
val pointsSum: String,
|
||||
|
||||
val average: Double
|
||||
@ColumnInfo(name = "points_sum_all_year")
|
||||
val pointsSumAllYear: String?,
|
||||
|
||||
val average: Double,
|
||||
|
||||
@ColumnInfo(name = "average_all_year")
|
||||
val averageAllYear: Double? = null,
|
||||
) {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
@ -9,8 +9,8 @@ import java.time.Instant
|
||||
@Entity(tableName = "MobileDevices")
|
||||
data class MobileDevice(
|
||||
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "device_id")
|
||||
val deviceId: Int,
|
||||
|
@ -9,8 +9,8 @@ import java.time.LocalDate
|
||||
@Entity(tableName = "SchoolAnnouncements")
|
||||
data class SchoolAnnouncement(
|
||||
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
||||
val studentId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
@ -49,6 +49,7 @@ data class Student(
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@Deprecated("not available in VULCAN anymore")
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
@ -78,6 +79,13 @@ data class Student(
|
||||
|
||||
@ColumnInfo(name = "registration_date")
|
||||
val registrationDate: Instant,
|
||||
|
||||
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
||||
val isAuthorized: Boolean,
|
||||
|
||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||
val isEduOne: Boolean?,
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ -88,3 +96,22 @@ data class Student(
|
||||
@ColumnInfo(name = "avatar_color")
|
||||
var avatarColor = 0L
|
||||
}
|
||||
|
||||
@Entity
|
||||
data class StudentIsAuthorized(
|
||||
|
||||
@PrimaryKey
|
||||
var id: Long,
|
||||
|
||||
@ColumnInfo(name = "is_authorized", defaultValue = "NULL")
|
||||
val isAuthorized: Boolean?,
|
||||
) : Serializable
|
||||
|
||||
@Entity
|
||||
data class StudentIsEduOne(
|
||||
@PrimaryKey
|
||||
var id: Long,
|
||||
|
||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||
val isEduOne: Boolean?,
|
||||
) : Serializable
|
||||
|
@ -0,0 +1,11 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration63 : AutoMigrationSpec {
|
||||
|
||||
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("UPDATE Students SET is_edu_one = NULL WHERE is_edu_one = 0")
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
|
||||
@JvmName("mapDirectorInformationToEntities")
|
||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
userLoginId = student.userLoginId,
|
||||
studentId = student.studentId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
@ -19,7 +19,7 @@ fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
@JvmName("mapLastAnnouncementsToEntities")
|
||||
fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
userLoginId = student.userLoginId,
|
||||
studentId = student.studentId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
|
@ -37,9 +37,11 @@ fun List<SdkGradeSummary>.mapToEntities(semester: Semester) = map {
|
||||
predictedGrade = it.predicted,
|
||||
finalGrade = it.final,
|
||||
pointsSum = it.pointsSum,
|
||||
pointsSumAllYear = it.pointsSumAllYear,
|
||||
proposedPoints = it.proposedPoints,
|
||||
finalPoints = it.finalPoints,
|
||||
average = it.average
|
||||
average = it.average,
|
||||
averageAllYear = it.averageAllYear,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
||||
|
||||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||
MobileDevice(
|
||||
userLoginId = student.userLoginId,
|
||||
studentId = student.studentId,
|
||||
date = it.createDate.toInstant(),
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
|
@ -34,17 +34,19 @@ fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
||||
error = it.error,
|
||||
students = it.subjects
|
||||
.filterIsInstance<SdkRegisterStudent>()
|
||||
.map { registerSubject ->
|
||||
.map { registerStudent ->
|
||||
RegisterStudent(
|
||||
studentId = registerSubject.studentId,
|
||||
studentName = registerSubject.studentName,
|
||||
studentSecondName = registerSubject.studentSecondName,
|
||||
studentSurname = registerSubject.studentSurname,
|
||||
className = registerSubject.className,
|
||||
classId = registerSubject.classId,
|
||||
isParent = registerSubject.isParent,
|
||||
semesters = registerSubject.semesters
|
||||
.mapToEntities(registerSubject.studentId),
|
||||
studentId = registerStudent.studentId,
|
||||
studentName = registerStudent.studentName,
|
||||
studentSecondName = registerStudent.studentSecondName,
|
||||
studentSurname = registerStudent.studentSurname,
|
||||
className = registerStudent.className,
|
||||
classId = registerStudent.classId,
|
||||
isParent = registerStudent.isParent,
|
||||
isAuthorized = registerStudent.isAuthorized,
|
||||
isEduOne = registerStudent.isEduOne,
|
||||
semesters = registerStudent.semesters
|
||||
.mapToEntities(registerStudent.studentId),
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -84,6 +86,8 @@ fun RegisterStudent.mapToStudentWithSemesters(
|
||||
password = user.password.orEmpty(),
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
isAuthorized = this.isAuthorized,
|
||||
isEduOne = this.isEduOne,
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
|
@ -45,4 +45,6 @@ data class RegisterStudent(
|
||||
val classId: Int,
|
||||
val isParent: Boolean,
|
||||
val semesters: List<Semester>,
|
||||
val isAuthorized: Boolean,
|
||||
val isEduOne: Boolean
|
||||
) : java.io.Serializable
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
@ -7,14 +8,11 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Absent
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -28,7 +26,7 @@ import javax.inject.Singleton
|
||||
class AttendanceRepository @Inject constructor(
|
||||
private val attendanceDb: AttendanceDao,
|
||||
private val timetableDb: TimetableDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -59,8 +57,7 @@ class AttendanceRepository @Inject constructor(
|
||||
val lessons = timetableDb.load(
|
||||
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||
)
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getAttendance(start.monday, end.sunday)
|
||||
.mapToEntities(semester, lessons)
|
||||
},
|
||||
@ -90,8 +87,10 @@ class AttendanceRepository @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun excuseForAbsence(
|
||||
student: Student, semester: Semester,
|
||||
absenceList: List<Attendance>, reason: String? = null
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
absenceList: List<Attendance>,
|
||||
reason: String? = null
|
||||
) {
|
||||
val items = absenceList.map { attendance ->
|
||||
Absent(
|
||||
@ -99,8 +98,7 @@ class AttendanceRepository @Inject constructor(
|
||||
timeId = attendance.timeId
|
||||
)
|
||||
}
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.excuseForAbsence(items, reason)
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
@ -20,9 +18,9 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class AttendanceSummaryRepository @Inject constructor(
|
||||
private val attendanceDb: AttendanceSummaryDao,
|
||||
private val sdk: Sdk,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val appDatabase: AppDatabase,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -43,8 +41,7 @@ class AttendanceSummaryRepository @Inject constructor(
|
||||
},
|
||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getAttendanceSummary(subjectId)
|
||||
.mapToEntities(semester, subjectId)
|
||||
},
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class CompletedLessonsRepository @Inject constructor(
|
||||
private val completedLessonsDb: CompletedLessonsDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -53,8 +51,7 @@ class CompletedLessonsRepository @Inject constructor(
|
||||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getCompletedLessons(start.monday, end.sunday)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,16 +1,14 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class ConferenceRepository @Inject constructor(
|
||||
private val conferenceDb: ConferenceDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -46,8 +44,7 @@ class ConferenceRepository @Inject constructor(
|
||||
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getConferences()
|
||||
.mapToEntities(semester)
|
||||
.filter { it.date >= startDate }
|
||||
|
@ -1,18 +1,16 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.endExamsDay
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.startExamsDay
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -23,7 +21,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class ExamRepository @Inject constructor(
|
||||
private val examDb: ExamDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -56,8 +54,7 @@ class ExamRepository @Inject constructor(
|
||||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getExams(start.startExamsDay, start.endExamsDay)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||
@ -10,11 +11,8 @@ import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.toLocalDate
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -30,7 +28,7 @@ class GradeRepository @Inject constructor(
|
||||
private val gradeDb: GradeDao,
|
||||
private val gradeSummaryDb: GradeSummaryDao,
|
||||
private val gradeDescriptiveDb: GradeDescriptiveDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -63,8 +61,7 @@ class GradeRepository @Inject constructor(
|
||||
}
|
||||
},
|
||||
fetch = {
|
||||
val (details, summary, descriptive) = sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
val (details, summary, descriptive) = wulkanowySdkFactory.create(student, semester)
|
||||
.getGrades(semester.semesterId)
|
||||
|
||||
Triple(
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
||||
@ -12,11 +13,8 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
|
||||
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.util.Locale
|
||||
@ -28,7 +26,7 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
|
||||
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
|
||||
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -56,8 +54,7 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
},
|
||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getGradesPartialStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
@ -104,8 +101,7 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
},
|
||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getGradesSemesterStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
@ -163,8 +159,7 @@ class GradeStatisticsRepository @Inject constructor(
|
||||
},
|
||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getGradesPointsStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,18 +1,16 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
@ -22,7 +20,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class HomeworkRepository @Inject constructor(
|
||||
private val homeworkDb: HomeworkDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -55,8 +53,7 @@ class HomeworkRepository @Inject constructor(
|
||||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getHomework(start.monday, end.sunday)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,12 +1,11 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -18,7 +17,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class LuckyNumberRepository @Inject constructor(
|
||||
private val luckyNumberDb: LuckyNumberDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -33,7 +32,9 @@ class LuckyNumberRepository @Inject constructor(
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||
fetch = {
|
||||
sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.getLuckyNumber(student.schoolShortName)
|
||||
?.mapToEntity(student)
|
||||
},
|
||||
saveFetchResult = { oldLuckyNumber, newLuckyNumber ->
|
||||
newLuckyNumber ?: return@networkBoundResource
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||
@ -29,11 +30,9 @@ import io.github.wulkanowy.data.pojos.MessageDraft
|
||||
import io.github.wulkanowy.data.toFirstResult
|
||||
import io.github.wulkanowy.data.waitForResult
|
||||
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Folder
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -48,7 +47,7 @@ class MessageRepository @Inject constructor(
|
||||
private val messagesDb: MessagesDao,
|
||||
private val mutedMessageSendersDao: MutedMessageSendersDao,
|
||||
private val messageAttachmentDao: MessageAttachmentDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
@ApplicationContext private val context: Context,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val sharedPrefProvider: SharedPrefProvider,
|
||||
@ -82,10 +81,16 @@ class MessageRepository @Inject constructor(
|
||||
} else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).getMessages(
|
||||
folder = Folder.valueOf(folder.name),
|
||||
mailboxKey = mailbox?.globalKey,
|
||||
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
|
||||
wulkanowySdkFactory.create(student)
|
||||
.getMessages(
|
||||
folder = Folder.valueOf(folder.name),
|
||||
mailboxKey = mailbox?.globalKey,
|
||||
)
|
||||
.mapToEntities(
|
||||
student = student,
|
||||
mailbox = mailbox,
|
||||
allMailboxes = mailboxDao.loadAll(student.email)
|
||||
)
|
||||
},
|
||||
saveFetchResult = { oldWithAuthors, new ->
|
||||
val old = oldWithAuthors.map { it.message }
|
||||
@ -115,10 +120,11 @@ class MessageRepository @Inject constructor(
|
||||
},
|
||||
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageKey = it!!.message.messageGlobalKey,
|
||||
markAsRead = message.unread && markAsRead,
|
||||
)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.getMessageDetails(
|
||||
messageKey = it!!.message.messageGlobalKey,
|
||||
markAsRead = message.unread && markAsRead,
|
||||
)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
@ -159,19 +165,19 @@ class MessageRepository @Inject constructor(
|
||||
recipients: List<Recipient>,
|
||||
mailbox: Mailbox,
|
||||
) {
|
||||
sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities(),
|
||||
mailboxId = mailbox.globalKey,
|
||||
)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities(),
|
||||
mailboxId = mailbox.globalKey,
|
||||
)
|
||||
refreshFolders(student, mailbox, listOf(SENT))
|
||||
}
|
||||
|
||||
suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
||||
sdk.init(student).restoreMessages(
|
||||
messages = messages.map { it.messageGlobalKey },
|
||||
)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.restoreMessages(messages = messages.map { it.messageGlobalKey })
|
||||
|
||||
refreshFolders(student, mailbox)
|
||||
}
|
||||
@ -182,10 +188,11 @@ class MessageRepository @Inject constructor(
|
||||
|
||||
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||
val firstMessage = messages.first()
|
||||
sdk.init(student).deleteMessages(
|
||||
messages = messages.map { it.messageGlobalKey },
|
||||
removeForever = firstMessage.folderId == TRASHED.id,
|
||||
)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.deleteMessages(
|
||||
messages = messages.map { it.messageGlobalKey },
|
||||
removeForever = firstMessage.folderId == TRASHED.id,
|
||||
)
|
||||
|
||||
if (firstMessage.folderId != TRASHED.id) {
|
||||
val deletedMessages = messages.map {
|
||||
@ -230,7 +237,9 @@ class MessageRepository @Inject constructor(
|
||||
},
|
||||
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
||||
fetch = {
|
||||
sdk.init(student).getMailboxes().mapToEntities(student)
|
||||
wulkanowySdkFactory.create(student)
|
||||
.getMailboxes()
|
||||
.mapToEntities(student)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
@ -8,11 +9,8 @@ import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class MobileDeviceRepository @Inject constructor(
|
||||
private val mobileDb: MobileDeviceDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -40,10 +38,9 @@ class MobileDeviceRepository @Inject constructor(
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { mobileDb.loadAll(student.userLoginId) },
|
||||
query = { mobileDb.loadAll(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getRegisteredDevices()
|
||||
.mapToEntities(student)
|
||||
},
|
||||
@ -57,16 +54,14 @@ class MobileDeviceRepository @Inject constructor(
|
||||
)
|
||||
|
||||
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.unregisterDevice(device.deviceId)
|
||||
|
||||
mobileDb.deleteAll(listOf(device))
|
||||
}
|
||||
|
||||
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
||||
return sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
return wulkanowySdkFactory.create(student, semester)
|
||||
.getToken()
|
||||
.mapToMobileDeviceToken()
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.toLocalDate
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class NoteRepository @Inject constructor(
|
||||
private val noteDb: NoteDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -45,8 +43,7 @@ class NoteRepository @Inject constructor(
|
||||
},
|
||||
query = { noteDb.loadAll(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getNotes()
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
@ -7,10 +8,8 @@ import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -18,14 +17,15 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class RecipientRepository @Inject constructor(
|
||||
private val recipientDb: RecipientDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val cacheKey = "recipient"
|
||||
|
||||
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
|
||||
val new = sdk.init(student).getRecipients(mailbox.globalKey)
|
||||
val new = wulkanowySdkFactory.create(student)
|
||||
.getRecipients(mailbox.globalKey)
|
||||
.mapToEntities(mailbox.globalKey)
|
||||
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
||||
|
||||
@ -60,7 +60,7 @@ class RecipientRepository @Inject constructor(
|
||||
): List<Recipient> {
|
||||
mailbox ?: return emptyList()
|
||||
|
||||
return sdk.init(student)
|
||||
return wulkanowySdkFactory.create(student)
|
||||
.getMessageReplayDetails(message.messageGlobalKey)
|
||||
.sender
|
||||
.let(::listOf)
|
||||
|
@ -1,17 +1,23 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RecoverRepository @Inject constructor(private val sdk: Sdk) {
|
||||
class RecoverRepository @Inject constructor(
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory
|
||||
) {
|
||||
|
||||
suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair<String, String> {
|
||||
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
||||
}
|
||||
suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair<String, String> =
|
||||
wulkanowySdkFactory.create()
|
||||
.getPasswordResetCaptchaCode(host, symbol)
|
||||
|
||||
suspend fun sendRecoverRequest(
|
||||
url: String, symbol: String, email: String, reCaptchaResponse: String
|
||||
): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||
url: String,
|
||||
symbol: String,
|
||||
email: String,
|
||||
reCaptchaResponse: String
|
||||
): String = wulkanowySdkFactory.create()
|
||||
.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||
}
|
||||
|
@ -1,14 +1,13 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -18,7 +17,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class SchoolAnnouncementRepository @Inject constructor(
|
||||
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -38,10 +37,10 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
schoolAnnouncementDb.loadAll(student.studentId)
|
||||
},
|
||||
fetch = {
|
||||
val sdk = sdk.init(student)
|
||||
val sdk = wulkanowySdkFactory.create(student)
|
||||
val lastAnnouncements = sdk.getLastAnnouncements().mapToEntities(student)
|
||||
val directorInformation = sdk.getDirectorInformation().mapToEntities(student)
|
||||
lastAnnouncements + directorInformation
|
||||
@ -58,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
)
|
||||
|
||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||
return schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
||||
}
|
||||
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||
|
@ -1,15 +1,13 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -17,7 +15,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class SchoolRepository @Inject constructor(
|
||||
private val schoolDb: SchoolDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -40,8 +38,7 @@ class SchoolRepository @Inject constructor(
|
||||
},
|
||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getSchool()
|
||||
.mapToEntity(semester)
|
||||
},
|
||||
|
@ -1,17 +1,15 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.api.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
|
||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.utils.IntegrityHelper
|
||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
@ -23,7 +21,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
class SchoolsRepository @Inject constructor(
|
||||
private val integrityHelper: IntegrityHelper,
|
||||
private val schoolsService: SchoolsService,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
) {
|
||||
|
||||
suspend fun logSchoolLogin(loginData: LoginData, students: List<StudentWithSemesters>) {
|
||||
@ -40,10 +38,9 @@ class SchoolsRepository @Inject constructor(
|
||||
private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) {
|
||||
val requestId = UUID.randomUUID().toString()
|
||||
val token = integrityHelper.getIntegrityToken(requestId) ?: return
|
||||
val updatedStudent = student.copy(password = loginData.password)
|
||||
|
||||
val schoolInfo = sdk
|
||||
.init(student.copy(password = loginData.password))
|
||||
.switchSemester(semester)
|
||||
val schoolInfo = wulkanowySdkFactory.create(updatedStudent, semester)
|
||||
.getSchool()
|
||||
|
||||
schoolsService.logLoginEvent(
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
@ -7,7 +8,6 @@ import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.isCurrent
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -18,7 +18,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class SemesterRepository @Inject constructor(
|
||||
private val semesterDb: SemesterDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val dispatchers: DispatchersProvider,
|
||||
) {
|
||||
|
||||
@ -60,8 +60,14 @@ class SemesterRepository @Inject constructor(
|
||||
}
|
||||
|
||||
private suspend fun refreshSemesters(student: Student) {
|
||||
val new = sdk.init(student).getSemesters().mapToEntities(student.studentId)
|
||||
if (new.isEmpty()) return Timber.i("Empty semester list!")
|
||||
val new = wulkanowySdkFactory.create(student)
|
||||
.getSemesters()
|
||||
.mapToEntities(student.studentId)
|
||||
|
||||
if (new.isEmpty()) {
|
||||
Timber.i("Empty semester list from SDK!")
|
||||
return
|
||||
}
|
||||
|
||||
val old = semesterDb.loadAll(student.studentId, student.classId)
|
||||
semesterDb.removeOldAndSaveNew(
|
||||
|
@ -1,13 +1,11 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@ -15,7 +13,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class StudentInfoRepository @Inject constructor(
|
||||
private val studentInfoDao: StudentInfoDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -30,9 +28,9 @@ class StudentInfoRepository @Inject constructor(
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
.getStudentInfo().mapToEntity(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getStudentInfo()
|
||||
.mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
|
@ -1,23 +1,25 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import androidx.room.withTransaction
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
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.StudentIsAuthorized
|
||||
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.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.security.Scrambler
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -26,7 +28,7 @@ class StudentRepository @Inject constructor(
|
||||
private val dispatchers: DispatchersProvider,
|
||||
private val studentDb: StudentDao,
|
||||
private val semesterDb: SemesterDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val appDatabase: AppDatabase,
|
||||
private val scrambler: Scrambler,
|
||||
) {
|
||||
@ -37,9 +39,10 @@ class StudentRepository @Inject constructor(
|
||||
pin: String,
|
||||
symbol: String,
|
||||
token: String
|
||||
): RegisterUser = sdk
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getStudentsFromHebe(token, pin, symbol, "")
|
||||
.mapToPojo(null)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getUserSubjectsFromScrapper(
|
||||
email: String,
|
||||
@ -47,18 +50,20 @@ class StudentRepository @Inject constructor(
|
||||
scrapperBaseUrl: String,
|
||||
domainSuffix: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
||||
.mapToPojo(password)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getStudentsHybrid(
|
||||
email: String,
|
||||
password: String,
|
||||
scrapperBaseUrl: String,
|
||||
symbol: String
|
||||
): RegisterUser = sdk
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||
.mapToPojo(password)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
||||
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
||||
@ -100,6 +105,46 @@ class StudentRepository @Inject constructor(
|
||||
return student
|
||||
}
|
||||
|
||||
suspend fun updateCurrentStudentAuthStatus() {
|
||||
Timber.i("Check isAuthorized: started")
|
||||
val student = getCurrentStudent()
|
||||
if (student.isAuthorized) {
|
||||
Timber.i("Check isAuthorized: already authorized")
|
||||
return
|
||||
}
|
||||
|
||||
val initializedSdk = wulkanowySdkFactory.create(student)
|
||||
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
||||
.onFailure { Timber.e(it, "Check isAuthorized: error occurred") }
|
||||
.getOrNull()
|
||||
|
||||
if (newCurrentStudent == null) {
|
||||
Timber.d("Check isAuthorized: current user is null")
|
||||
return
|
||||
}
|
||||
|
||||
val currentStudentSemesters = semesterDb.loadAll(student.studentId, student.classId)
|
||||
if (currentStudentSemesters.isEmpty()) {
|
||||
Timber.d("Check isAuthorized: apply empty semesters workaround")
|
||||
semesterDb.insertSemesters(
|
||||
items = newCurrentStudent.semesters.mapToEntities(student.studentId),
|
||||
)
|
||||
}
|
||||
|
||||
if (!newCurrentStudent.isAuthorized) {
|
||||
Timber.i("Check isAuthorized: authorization required")
|
||||
throw NoAuthorizationException()
|
||||
}
|
||||
|
||||
val studentIsAuthorized = StudentIsAuthorized(
|
||||
id = student.id,
|
||||
isAuthorized = true
|
||||
)
|
||||
|
||||
Timber.i("Check isAuthorized: already authorized, update local status")
|
||||
studentDb.update(studentIsAuthorized)
|
||||
}
|
||||
|
||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||
|
||||
@ -149,20 +194,24 @@ class StudentRepository @Inject constructor(
|
||||
.distinctBy { it.student.studentName }.size == 1
|
||||
|
||||
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.authorizePermission(pesel)
|
||||
|
||||
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
||||
val newCurrentApiStudent = sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
.getCurrentStudent() ?: return
|
||||
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
|
||||
val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
|
||||
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
|
||||
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
|
||||
.getOrNull() ?: return
|
||||
|
||||
val studentName = StudentName(
|
||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||
).apply { id = student.id }
|
||||
|
||||
studentDb.update(studentName)
|
||||
semesterDb.removeOldAndSaveNew(
|
||||
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
|
||||
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
||||
@ -175,4 +224,18 @@ class StudentRepository @Inject constructor(
|
||||
appDatabase.clearAllTables()
|
||||
}
|
||||
}
|
||||
|
||||
private fun RegisterUser.logErrors() {
|
||||
val symbolsErrors = symbols.filter { it.error != null }
|
||||
.map { it.error }
|
||||
val unitsErrors = symbols.flatMap { it.schools }
|
||||
.filter { it.error != null }
|
||||
.map { it.error }
|
||||
|
||||
(symbolsErrors + unitsErrors).forEach { error ->
|
||||
Timber.e(error, "Error occurred while fetching students")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoAuthorizationException : Exception()
|
||||
|
@ -1,15 +1,13 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
@ -18,7 +16,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class SubjectRepository @Inject constructor(
|
||||
private val subjectDao: SubjectDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -39,8 +37,7 @@ class SubjectRepository @Inject constructor(
|
||||
},
|
||||
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getSubjects()
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,15 +1,13 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
@ -18,7 +16,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class TeacherRepository @Inject constructor(
|
||||
private val teacherDb: TeacherDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
@ -39,8 +37,7 @@ class TeacherRepository @Inject constructor(
|
||||
},
|
||||
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getTeachers()
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||
@ -11,14 +12,11 @@ import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.switchSemester
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
@ -33,7 +31,7 @@ class TimetableRepository @Inject constructor(
|
||||
private val timetableDb: TimetableDao,
|
||||
private val timetableAdditionalDb: TimetableAdditionalDao,
|
||||
private val timetableHeaderDb: TimetableHeaderDao,
|
||||
private val sdk: Sdk,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
@ -74,8 +72,7 @@ class TimetableRepository @Inject constructor(
|
||||
},
|
||||
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
||||
fetch = {
|
||||
val timetableFull = sdk.init(student)
|
||||
.switchSemester(semester)
|
||||
val timetableFull = wulkanowySdkFactory.create(student, semester)
|
||||
.getTimetable(start.monday, end.sunday)
|
||||
|
||||
timetableFull.mapToEntities(semester)
|
||||
|
@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureUnavailableException
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.works.Work
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
@ -48,6 +49,7 @@ class SyncWorker @AssistedInject constructor(
|
||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||
student to semester
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e)
|
||||
return@withContext getResultFromErrors(listOf(e))
|
||||
}
|
||||
|
||||
@ -59,7 +61,7 @@ class SyncWorker @AssistedInject constructor(
|
||||
null
|
||||
} catch (e: Throwable) {
|
||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException) {
|
||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException || e is FeatureUnavailableException) {
|
||||
null
|
||||
} else {
|
||||
Timber.e(e)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.ui.base
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
@ -45,11 +46,19 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
||||
themeManager.applyActivityTheme(this)
|
||||
super.onCreate(savedInstanceState)
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||
applyCustomTaskDescription()
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(
|
||||
ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface))
|
||||
)
|
||||
@Suppress("DEPRECATION")
|
||||
private fun applyCustomTaskDescription() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) return
|
||||
try {
|
||||
val newColor = getThemeAttrColor(R.attr.colorSurface)
|
||||
val taskDescription = ActivityManager.TaskDescription(null, null, newColor)
|
||||
setTaskDescription(taskDescription)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
override fun showError(text: String, error: Throwable) {
|
||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.base
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
||||
import io.github.wulkanowy.data.repositories.NoAuthorizationException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||
@ -40,7 +40,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
||||
is ScramblerException -> onDecryptionFailed()
|
||||
is BadCredentialsException -> onExpiredCredentials()
|
||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||
is AuthorizationRequiredException -> onAuthorizationRequired()
|
||||
is NoAuthorizationException -> onAuthorizationRequired()
|
||||
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -33,17 +34,17 @@ class AttendanceAdapter @Inject constructor() :
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||
val context = holder.binding.root.context
|
||||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
attendanceItemNumber.text = item.number.toString()
|
||||
attendanceItemSubject.text = item.subject.ifBlank {
|
||||
root.context.getString(R.string.all_no_data)
|
||||
}
|
||||
attendanceItemSubject.text = item.subject
|
||||
.ifBlank { context.getString(R.string.all_no_data) }
|
||||
attendanceItemDescription.setText(item.descriptionRes)
|
||||
|
||||
attendanceItemDescription.setTextColor(
|
||||
root.context.getThemeAttrColor(
|
||||
context.getThemeAttrColor(
|
||||
when {
|
||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||
@ -61,13 +62,15 @@ class AttendanceAdapter @Inject constructor() :
|
||||
attendanceItemAlert.isVisible =
|
||||
item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
|
||||
|
||||
attendanceItemAlert.setColorFilter(root.context.getThemeAttrColor(
|
||||
when{
|
||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||
else -> android.R.attr.colorPrimary
|
||||
}
|
||||
))
|
||||
attendanceItemAlert.imageTintList = ColorStateList.valueOf(
|
||||
context.getThemeAttrColor(
|
||||
when {
|
||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||
else -> android.R.attr.colorPrimary
|
||||
}
|
||||
)
|
||||
)
|
||||
attendanceItemNumber.visibility = View.GONE
|
||||
attendanceItemExcuseInfo.visibility = View.GONE
|
||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||
|
@ -78,4 +78,9 @@ class AuthDialog : BaseDialogFragment<DialogAuthBinding>(), AuthView {
|
||||
override fun showDescriptionWithName(name: String) {
|
||||
binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
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 AuthPresenter @Inject constructor(
|
||||
@ -57,8 +58,9 @@ class AuthPresenter @Inject constructor(
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
||||
Timber.d("Auth succeed: $isSuccess")
|
||||
if (isSuccess) {
|
||||
studentRepository.refreshStudentName(student, semester)
|
||||
studentRepository.refreshStudentAfterAuthorize(student, semester)
|
||||
}
|
||||
isSuccess
|
||||
}
|
||||
@ -68,6 +70,7 @@ class AuthPresenter @Inject constructor(
|
||||
view?.showContent(true)
|
||||
}
|
||||
.onSuccess {
|
||||
Timber.d("Auth fully succeed: $it")
|
||||
if (it) {
|
||||
view?.showSuccess(true)
|
||||
view?.showContent(false)
|
||||
|
@ -10,8 +10,8 @@ import android.webkit.WebViewClient
|
||||
import androidx.core.os.bundleOf
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.databinding.DialogCaptchaBinding
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||
import timber.log.Timber
|
||||
@ -21,7 +21,7 @@ import javax.inject.Inject
|
||||
class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var sdk: Sdk
|
||||
lateinit var wulkanowySdkFactory: WulkanowySdkFactory
|
||||
|
||||
@Inject
|
||||
lateinit var webkitCookieManagerProxy: WebkitCookieManagerProxy
|
||||
@ -59,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
||||
webView = this
|
||||
with(settings) {
|
||||
javaScriptEnabled = true
|
||||
userAgentString = sdk.userAgent
|
||||
userAgentString = wulkanowySdkFactory.create().userAgent
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
|
@ -26,5 +26,7 @@ private fun generateSummary(subject: String, predicted: String, final: String) =
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
average = .0
|
||||
average = .0,
|
||||
pointsSumAllYear = null,
|
||||
averageAllYear = null,
|
||||
)
|
||||
|
@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
|
||||
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
|
||||
subject = subject,
|
||||
content = content,
|
||||
userLoginId = 0,
|
||||
studentId = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
|
@ -266,7 +266,9 @@ class GradeAverageProvider @Inject constructor(
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
average = .0
|
||||
pointsSumAllYear = null,
|
||||
average = .0,
|
||||
averageAllYear = null,
|
||||
)
|
||||
}
|
||||
|
||||
@ -294,13 +296,15 @@ class GradeAverageProvider @Inject constructor(
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
pointsSumAllYear = null,
|
||||
average = when {
|
||||
calcAverage -> details
|
||||
.updateModifiers(student, params)
|
||||
.calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
|
||||
|
||||
else -> .0
|
||||
}
|
||||
},
|
||||
averageAllYear = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -31,14 +30,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
@Inject
|
||||
lateinit var presenter: GradePresenter
|
||||
|
||||
private val pagerAdapter by lazy {
|
||||
BaseFragmentPagerAdapter(
|
||||
fragmentManager = childFragmentManager,
|
||||
pagesCount = 3,
|
||||
lifecycle = lifecycle,
|
||||
)
|
||||
}
|
||||
|
||||
private var semesterSwitchMenu: MenuItem? = null
|
||||
|
||||
companion object {
|
||||
@ -52,6 +43,8 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
|
||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||
|
||||
private var pagerAdapter: BaseFragmentPagerAdapter? = null
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -71,13 +64,26 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(binding) {
|
||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun initTabs(pageCount: Int) {
|
||||
pagerAdapter = BaseFragmentPagerAdapter(
|
||||
lifecycle = lifecycle,
|
||||
pagesCount = pageCount,
|
||||
fragmentManager = childFragmentManager
|
||||
)
|
||||
|
||||
with(binding.gradeViewPager) {
|
||||
adapter = pagerAdapter
|
||||
offscreenPageLimit = 3
|
||||
setOnSelectPageListener(presenter::onPageSelected)
|
||||
}
|
||||
|
||||
with(pagerAdapter) {
|
||||
with(pagerAdapter!!) {
|
||||
containerId = binding.gradeViewPager.id
|
||||
titleFactory = {
|
||||
when (it) {
|
||||
@ -99,11 +105,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
||||
|
||||
with(binding) {
|
||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@ -169,19 +170,20 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||
?.onParentLoadData(semesterId, forceRefresh)
|
||||
}
|
||||
|
||||
override fun notifyChildParentReselected(index: Int) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||
}
|
||||
|
||||
override fun notifyChildSemesterChange(index: Int) {
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerAdapter = null
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
@ -22,11 +22,8 @@ class GradePresenter @Inject constructor(
|
||||
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
||||
|
||||
private var selectedIndex = 0
|
||||
|
||||
private var schoolYear = 0
|
||||
|
||||
private var semesters = emptyList<Semester>()
|
||||
|
||||
private var availableSemesters = emptyList<Semester>()
|
||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
@ -40,7 +37,7 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onCreateMenu() {
|
||||
if (semesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||
if (availableSemesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
@ -49,8 +46,8 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onSemesterSwitch(): Boolean {
|
||||
if (semesters.isNotEmpty()) {
|
||||
view?.showSemesterDialog(selectedIndex - 1, semesters.take(2))
|
||||
if (availableSemesters.isNotEmpty()) {
|
||||
view?.showSemesterDialog(selectedIndex - 1, availableSemesters.take(2))
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -83,7 +80,7 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPageSelected(index: Int) {
|
||||
if (semesters.isNotEmpty()) loadChild(index)
|
||||
if (availableSemesters.isNotEmpty()) loadChild(index)
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
@ -101,16 +98,24 @@ class GradePresenter @Inject constructor(
|
||||
private fun loadData() {
|
||||
resourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||
val semesters = semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||
|
||||
student to semesters
|
||||
}
|
||||
.logResourceStatus("load grade data")
|
||||
.onResourceData {
|
||||
val current = it.getCurrentOrLast()
|
||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
||||
schoolYear = current.schoolYear
|
||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
||||
view?.setCurrentSemesterName(current.semesterName, schoolYear)
|
||||
.onResourceData { (student, semesters) ->
|
||||
val currentSemester = semesters.getCurrentOrLast()
|
||||
selectedIndex =
|
||||
if (selectedIndex == 0) currentSemester.semesterName else selectedIndex
|
||||
schoolYear = currentSemester.schoolYear
|
||||
availableSemesters = semesters.filter { semester ->
|
||||
semester.diaryId == currentSemester.diaryId
|
||||
}
|
||||
|
||||
view?.run {
|
||||
initTabs(if (student.isEduOne == true) 2 else 3)
|
||||
setCurrentSemesterName(currentSemester.semesterName, schoolYear)
|
||||
|
||||
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
||||
loadChild(currentPageIndex)
|
||||
showErrorView(false)
|
||||
@ -131,10 +136,10 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}")
|
||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${availableSemesters.joinToString { it.semesterName.toString() }}")
|
||||
|
||||
val newSelectedSemesterId = try {
|
||||
semesters.first { it.semesterName == selectedIndex }.semesterId
|
||||
availableSemesters.first { it.semesterName == selectedIndex }.semesterId
|
||||
} catch (e: NoSuchElementException) {
|
||||
Timber.e(e, "Selected semester no exists")
|
||||
return
|
||||
|
@ -9,6 +9,8 @@ interface GradeView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun initTabs(pageCount: Int)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
@ -96,9 +96,11 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
ViewType.HEADER.id -> HeaderViewHolder(
|
||||
HeaderGradeDetailsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
ViewType.ITEM.id -> ItemViewHolder(
|
||||
ItemGradeDetailsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
@ -110,6 +112,7 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
header = items[position].value as GradeDetailsHeader,
|
||||
position = position
|
||||
)
|
||||
|
||||
is ItemViewHolder -> bindItemViewHolder(
|
||||
holder = holder,
|
||||
grade = items[position].value as Grade
|
||||
@ -133,6 +136,10 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
maxLines = if (expandedPositions[headerPosition]) 2 else 1
|
||||
}
|
||||
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
|
||||
with(gradeHeaderAverageAllYear) {
|
||||
isVisible = header.averageAllYear != null && header.averageAllYear != .0
|
||||
text = formatAverageAllYear(header.averageAllYear, root.context.resources)
|
||||
}
|
||||
gradeHeaderPointsSum.text =
|
||||
context.getString(R.string.grade_points_sum, header.pointsSum)
|
||||
gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty()
|
||||
@ -233,6 +240,13 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
resources.getString(R.string.grade_average, average)
|
||||
}
|
||||
|
||||
private fun formatAverageAllYear(average: Double?, resources: Resources) =
|
||||
if (average == null || average == .0) {
|
||||
resources.getString(R.string.grade_no_average)
|
||||
} else {
|
||||
resources.getString(R.string.grade_average_year, average)
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
|
@ -13,6 +13,7 @@ data class GradeDetailsItem(
|
||||
data class GradeDetailsHeader(
|
||||
val subject: String,
|
||||
val average: Double?,
|
||||
val averageAllYear: Double?,
|
||||
val pointsSum: String?,
|
||||
val grades: List<GradeDetailsItem>
|
||||
) {
|
||||
|
@ -226,8 +226,9 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
GradeDetailsHeader(
|
||||
subject = gradeSubject.subject,
|
||||
average = gradeSubject.average,
|
||||
averageAllYear = gradeSubject.summary.averageAllYear,
|
||||
pointsSum = gradeSubject.points,
|
||||
grades = subItems
|
||||
grades = subItems,
|
||||
).apply {
|
||||
newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size
|
||||
}, ViewType.HEADER
|
||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.grade.summary
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
@ -65,37 +66,55 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
val gradeSummaries = items
|
||||
.filter { it.gradeDescriptive == null }
|
||||
.map { it.gradeSummary }
|
||||
val isSecondSemester = items.any { item ->
|
||||
item.gradeSummary.let { it.averageAllYear != null && it.averageAllYear != .0 }
|
||||
}
|
||||
|
||||
val context = binding.root.context
|
||||
val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) }
|
||||
val calculatedItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
|
||||
val calculatedSemesterItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
|
||||
val calculatedAnnualItemsCount =
|
||||
gradeSummaries.count { value -> value.averageAllYear != 0.0 }
|
||||
val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) }
|
||||
val finalAverage = gradeSummaries.calcFinalAverage(
|
||||
preferencesRepository.gradePlusModifier,
|
||||
preferencesRepository.gradeMinusModifier
|
||||
plusModifier = preferencesRepository.gradePlusModifier,
|
||||
minusModifier = preferencesRepository.gradeMinusModifier,
|
||||
)
|
||||
val calculatedAverage = gradeSummaries.filter { value -> value.average != 0.0 }
|
||||
val calculatedSemesterAverage = gradeSummaries.filter { value -> value.average != 0.0 }
|
||||
.map { values -> values.average }
|
||||
.reversed() // fix average precision
|
||||
.average()
|
||||
.let { if (it.isNaN()) 0.0 else it }
|
||||
val calculatedAnnualAverage = gradeSummaries.filter { value -> value.averageAllYear != 0.0 }
|
||||
.mapNotNull { values -> values.averageAllYear }
|
||||
.reversed() // fix average precision
|
||||
.average()
|
||||
.let { if (it.isNaN()) 0.0 else it }
|
||||
|
||||
with(binding) {
|
||||
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedSemesterAverage)
|
||||
gradeSummaryScrollableHeaderCalculatedAnnual.text =
|
||||
formatAverage(calculatedAnnualAverage)
|
||||
gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage)
|
||||
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedAverage)
|
||||
gradeSummaryScrollableHeaderFinalSubjectCount.text =
|
||||
context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
finalItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString(
|
||||
gradeSummaryScrollableHeaderFinalSubjectCount.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
calculatedItemsCount,
|
||||
finalItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
calculatedSemesterItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedSubjectCountAnnual.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
calculatedAnnualItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedAnnualContainer.isVisible = isSecondSemester
|
||||
|
||||
gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() }
|
||||
gradeSummaryCalculatedAverageHelpAnnual.setOnClickListener { onCalculatedHelpClickListener() }
|
||||
gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() }
|
||||
}
|
||||
}
|
||||
@ -107,7 +126,12 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
with(binding) {
|
||||
gradeSummaryItemTitle.text = gradeSummary.subject
|
||||
gradeSummaryItemPoints.text = gradeSummary.pointsSum
|
||||
|
||||
gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "")
|
||||
gradeSummaryItemAverageAllYear.text = gradeSummary.averageAllYear?.let {
|
||||
formatAverage(it, "")
|
||||
}
|
||||
|
||||
gradeSummaryItemPredicted.text =
|
||||
"${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim()
|
||||
gradeSummaryItemFinal.text =
|
||||
@ -116,6 +140,12 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
root.context.getString(R.string.all_no_data)
|
||||
}
|
||||
|
||||
gradeSummaryItemAverageContainer.isVisible = gradeSummary.average != .0
|
||||
gradeSummaryItemAverageDivider.isVisible = gradeSummaryItemAverageContainer.isVisible
|
||||
gradeSummaryItemAverageAllYearContainer.isGone =
|
||||
gradeSummary.averageAllYear == null || gradeSummary.averageAllYear == .0
|
||||
gradeSummaryItemAverageAllYearDivider.isGone =
|
||||
gradeSummaryItemAverageAllYearContainer.isGone
|
||||
gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null
|
||||
@ -123,6 +153,7 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null
|
||||
gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank()
|
||||
gradeSummaryItemPointsDivider.isVisible = gradeSummaryItemPointsContainer.isVisible
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,9 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
|
||||
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
||||
onDateSelected = {
|
||||
date = it
|
||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||
if (isAdded) {
|
||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -19,19 +19,23 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (LoginStudentSelectItemType.values()[viewType]) {
|
||||
return when (LoginStudentSelectItemType.entries[viewType]) {
|
||||
LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false),
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.STUDENT -> StudentViewHolder(
|
||||
ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.HELP -> HelpViewHolder(
|
||||
ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
@ -98,9 +102,11 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
with(binding) {
|
||||
loginStudentSelectHeaderSchoolName.text = buildString {
|
||||
append(item.unit.schoolName.trim())
|
||||
append(" (")
|
||||
append(item.unit.schoolShortName)
|
||||
append(")")
|
||||
if (item.unit.schoolShortName.isNotBlank()) {
|
||||
append(" (")
|
||||
append(item.unit.schoolShortName)
|
||||
append(")")
|
||||
}
|
||||
}
|
||||
loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty()
|
||||
loginStudentSelectHeaderSchoolError.text = item.unit.error?.message
|
||||
@ -170,9 +176,11 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> {
|
||||
oldItem.symbol == newItem.symbol
|
||||
}
|
||||
|
||||
oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> {
|
||||
oldItem.student == newItem.student
|
||||
}
|
||||
|
||||
else -> oldItem == newItem
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.SchoolsRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -108,8 +109,8 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun createItems(): List<LoginStudentSelectItem> = buildList {
|
||||
val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() }
|
||||
val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() }
|
||||
val notEmptySymbols = registerUser.symbols.filter { it.shouldShowOnTop() }
|
||||
val emptySymbols = registerUser.symbols.filter { !it.shouldShowOnTop() }
|
||||
|
||||
if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.userEnteredSymbol }) {
|
||||
add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.userEnteredSymbol }))
|
||||
@ -127,6 +128,10 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
add(helpItem)
|
||||
}
|
||||
|
||||
private fun RegisterSymbol.shouldShowOnTop(): Boolean {
|
||||
return schools.isNotEmpty() || error is StudentGraduateException
|
||||
}
|
||||
|
||||
private fun createNotEmptySymbolItems(
|
||||
notEmptySymbols: List<RegisterSymbol>,
|
||||
students: List<StudentWithSemesters>,
|
||||
|
@ -73,6 +73,7 @@ class MainPresenter @Inject constructor(
|
||||
syncManager.startPeriodicSyncWorker()
|
||||
|
||||
checkAppSupport()
|
||||
updateCurrentStudentAuthStatus()
|
||||
|
||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||
Timber.i("Main view was initialized with $initDestination")
|
||||
@ -191,4 +192,11 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
view?.showStudentAvatar(currentStudent)
|
||||
}
|
||||
|
||||
private fun updateCurrentStudentAuthStatus() {
|
||||
presenterScope.launch {
|
||||
runCatching { studentRepository.updateCurrentStudentAuthStatus() }
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ class MailboxChooserDialog : BaseDialogFragment<DialogMailboxChooserBinding>(),
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(
|
||||
|
@ -82,10 +82,10 @@ class MessagePreviewFragment :
|
||||
get() = getString(R.string.message_not_exists)
|
||||
|
||||
companion object {
|
||||
const val MESSAGE_ID_KEY = "message_id"
|
||||
private const val MESSAGE_ARG_KEY = "message"
|
||||
|
||||
fun newInstance(message: Message) = MessagePreviewFragment().apply {
|
||||
arguments = bundleOf(MESSAGE_ID_KEY to message)
|
||||
arguments = bundleOf(MESSAGE_ARG_KEY to message)
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ class MessagePreviewFragment :
|
||||
messageContainer = binding.messagePreviewContainer
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY),
|
||||
message = requireArguments().serializable(MESSAGE_ARG_KEY),
|
||||
)
|
||||
}
|
||||
|
||||
@ -233,11 +233,6 @@ class MessagePreviewFragment :
|
||||
(activity as MainActivity).popView()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
outState.putSerializable(MESSAGE_ID_KEY, presenter.messageWithAttachments)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
|
@ -3,10 +3,15 @@ package io.github.wulkanowy.ui.modules.message.preview
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.core.text.parseAsHtml
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.flatResourceFlow
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.onResourceData
|
||||
import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
@ -28,17 +33,17 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {
|
||||
|
||||
var messageWithAttachments: MessageWithAttachment? = null
|
||||
private var messageWithAttachments: MessageWithAttachment? = null
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
|
||||
private var retryCallback: () -> Unit = {}
|
||||
|
||||
fun onAttachView(view: MessagePreviewView, message: Message?) {
|
||||
fun onAttachView(view: MessagePreviewView, message: Message) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||
loadData(requireNotNull(message))
|
||||
loadData(message)
|
||||
}
|
||||
|
||||
private fun onMessageLoadRetry(message: Message) {
|
||||
|
@ -27,7 +27,7 @@ class TimetableAdapter @Inject constructor() :
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (TimetableItemType.values()[viewType]) {
|
||||
return when (TimetableItemType.entries[viewType]) {
|
||||
TimetableItemType.SMALL -> SmallViewHolder(
|
||||
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
@ -79,6 +79,7 @@ class TimetableAdapter @Inject constructor() :
|
||||
|
||||
with(binding) {
|
||||
timetableSmallItemNumber.text = lesson.number.toString()
|
||||
timetableSmallItemNumber.isVisible = item.isLessonNumberVisible
|
||||
timetableSmallItemSubject.text = lesson.subject
|
||||
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
||||
timetableSmallItemRoom.text = lesson.room
|
||||
@ -97,6 +98,7 @@ class TimetableAdapter @Inject constructor() :
|
||||
|
||||
with(binding) {
|
||||
timetableItemNumber.text = lesson.number.toString()
|
||||
timetableItemNumber.isVisible = item.isLessonNumberVisible
|
||||
timetableItemSubject.text = lesson.subject
|
||||
timetableItemGroup.text = lesson.group
|
||||
timetableItemRoom.text = lesson.room
|
||||
|
@ -7,12 +7,14 @@ sealed class TimetableItem(val type: TimetableItemType) {
|
||||
|
||||
data class Small(
|
||||
val lesson: Timetable,
|
||||
val isLessonNumberVisible: Boolean,
|
||||
val onClick: (Timetable) -> Unit,
|
||||
) : TimetableItem(TimetableItemType.SMALL)
|
||||
|
||||
data class Normal(
|
||||
val lesson: Timetable,
|
||||
val showGroupsInPlan: Boolean,
|
||||
val isLessonNumberVisible: Boolean,
|
||||
val timeLeft: TimeLeft?,
|
||||
val onClick: (Timetable) -> Unit,
|
||||
) : TimetableItem(TimetableItemType.NORMAL)
|
||||
|
@ -57,6 +57,7 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
private var initialDate: LocalDate? = null
|
||||
private var isWeekendHasLessons: Boolean = false
|
||||
private var isEduOne: Boolean = false
|
||||
|
||||
var currentDate: LocalDate? = null
|
||||
private set
|
||||
@ -149,6 +150,7 @@ class TimetablePresenter @Inject constructor(
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
isEduOne = student.isEduOne == true
|
||||
checkInitialAndCurrentDate(semester)
|
||||
timetableRepository.getTimetable(
|
||||
student = student,
|
||||
@ -234,9 +236,8 @@ class TimetablePresenter @Inject constructor(
|
||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||
it.isStudentPlan
|
||||
} else true
|
||||
}.sortedWith(
|
||||
compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
|
||||
)
|
||||
}
|
||||
.sortedWith(compareBy({ item -> item.start }, { item -> !item.isStudentPlan }))
|
||||
|
||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||
@ -257,13 +258,15 @@ class TimetablePresenter @Inject constructor(
|
||||
lesson = it,
|
||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||
onClick = ::onTimetableItemSelected
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(normalLesson)
|
||||
} else {
|
||||
val smallLesson = TimetableItem.Small(
|
||||
lesson = it,
|
||||
onClick = ::onTimetableItemSelected
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(smallLesson)
|
||||
}
|
||||
|
@ -46,11 +46,8 @@ class TimetableWidgetFactory(
|
||||
) : RemoteViewsService.RemoteViewsFactory {
|
||||
|
||||
private var items = emptyList<TimetableWidgetItem>()
|
||||
|
||||
private var timetableCanceledColor: Int? = null
|
||||
|
||||
private var textColor: Int? = null
|
||||
|
||||
private var timetableChangeColor: Int? = null
|
||||
|
||||
override fun getLoadingView() = null
|
||||
@ -81,7 +78,7 @@ class TimetableWidgetFactory(
|
||||
val lessons = getLessons(student, semester, date)
|
||||
val lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
|
||||
|
||||
createItems(lessons, lastSync)
|
||||
createItems(lessons, lastSync, !(student.isEduOne ?: false))
|
||||
}
|
||||
.onFailure {
|
||||
items = listOf(TimetableWidgetItem.Error(it))
|
||||
@ -106,12 +103,13 @@ class TimetableWidgetFactory(
|
||||
): List<Timetable> {
|
||||
val timetable = timetableRepository.getTimetable(student, semester, date, date, false)
|
||||
val lessons = timetable.toFirstResult().dataOrThrow.lessons
|
||||
return lessons.sortedBy { it.number }
|
||||
return lessons.sortedBy { it.start }
|
||||
}
|
||||
|
||||
private fun createItems(
|
||||
lessons: List<Timetable>,
|
||||
lastSync: Instant?,
|
||||
isEduOne: Boolean
|
||||
): List<TimetableWidgetItem> {
|
||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||
@ -127,7 +125,7 @@ class TimetableWidgetFactory(
|
||||
)
|
||||
add(emptyItem)
|
||||
}
|
||||
add(TimetableWidgetItem.Normal(it))
|
||||
add(TimetableWidgetItem.Normal(it, isEduOne))
|
||||
prevNum = it.number
|
||||
}
|
||||
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
|
||||
@ -155,9 +153,11 @@ class TimetableWidgetFactory(
|
||||
|
||||
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
|
||||
val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE)
|
||||
val lessonNumberVisibility = if (item.isLessonNumberVisible) VISIBLE else GONE
|
||||
|
||||
val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
||||
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
|
||||
setViewVisibility(R.id.timetableWidgetItemNumber, lessonNumberVisibility)
|
||||
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
|
||||
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
|
||||
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
|
||||
|
@ -7,6 +7,7 @@ sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) {
|
||||
|
||||
data class Normal(
|
||||
val lesson: Timetable,
|
||||
val isLessonNumberVisible: Boolean,
|
||||
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
|
||||
|
||||
data class Empty(
|
||||
|
@ -4,30 +4,31 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.core.os.BundleCompat
|
||||
import java.io.Serializable
|
||||
|
||||
// Even though API was introduced in 33, we use 34 as 33 is bugged in some scenarios.
|
||||
|
||||
inline fun <reified T : Serializable> Bundle.serializable(key: String): T = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)!!
|
||||
Build.VERSION.SDK_INT >= 34 -> getSerializable(key, T::class.java)!!
|
||||
else -> @Suppress("DEPRECATION") getSerializable(key) as T
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Bundle.nullableSerializable(key: String): T? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)
|
||||
Build.VERSION.SDK_INT >= 34 -> getSerializable(key, T::class.java)
|
||||
else -> @Suppress("DEPRECATION") getSerializable(key) as T?
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
inline fun <reified T : Parcelable> Bundle.parcelableArray(key: String): Array<T>? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java)
|
||||
else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array<T>?
|
||||
}
|
||||
inline fun <reified T : Parcelable> Bundle.parcelableArray(key: String): Array<T>? =
|
||||
BundleCompat.getParcelableArray(this, key, T::class.java) as Array<T>?
|
||||
|
||||
inline fun <reified T : Serializable> Intent.serializable(key: String): T = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)!!
|
||||
Build.VERSION.SDK_INT >= 34 -> getSerializableExtra(key, T::class.java)!!
|
||||
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T
|
||||
}
|
||||
|
||||
inline fun <reified T : Serializable> Intent.nullableSerializable(key: String): T? = when {
|
||||
Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)
|
||||
Build.VERSION.SDK_INT >= 34 -> getSerializableExtra(key, T::class.java)
|
||||
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T?
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ fun getRefreshKey(name: String, semester: Semester): String {
|
||||
}
|
||||
|
||||
fun getRefreshKey(name: String, student: Student): String {
|
||||
return "${name}_${student.userLoginId}"
|
||||
return "${name}_${student.studentId}"
|
||||
}
|
||||
|
||||
fun getRefreshKey(name: String, mailbox: Mailbox?, folder: MessageFolder): String {
|
||||
|
@ -1,42 +0,0 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import timber.log.Timber
|
||||
|
||||
fun Sdk.init(student: Student): Sdk {
|
||||
email = student.email
|
||||
password = student.password
|
||||
symbol = student.symbol
|
||||
schoolSymbol = student.schoolSymbol
|
||||
studentId = student.studentId
|
||||
classId = student.classId
|
||||
emptyCookieJarInterceptor = true
|
||||
|
||||
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
||||
mobileBaseUrl = student.mobileBaseUrl
|
||||
} else {
|
||||
scrapperBaseUrl = student.scrapperBaseUrl
|
||||
domainSuffix = student.scrapperDomainSuffix
|
||||
loginType = Sdk.ScrapperLoginType.valueOf(student.loginType)
|
||||
}
|
||||
|
||||
mode = Sdk.Mode.valueOf(student.loginMode)
|
||||
mobileBaseUrl = student.mobileBaseUrl
|
||||
keyId = student.certificateKey
|
||||
privatePem = student.privateKey
|
||||
|
||||
Timber.d("Sdk in ${student.loginMode} mode reinitialized")
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun Sdk.switchSemester(semester: Semester): Sdk {
|
||||
return switchDiary(
|
||||
diaryId = semester.diaryId,
|
||||
kindergartenDiaryId = semester.kindergartenDiaryId,
|
||||
schoolYear = semester.schoolYear,
|
||||
unitId = semester.unitId,
|
||||
)
|
||||
}
|
@ -18,7 +18,7 @@ fun Semester.isCurrent(now: LocalDate = now()): Boolean {
|
||||
}
|
||||
|
||||
fun List<Semester>.getCurrentOrLast(): Semester {
|
||||
if (isEmpty()) throw RuntimeException("Empty semester list")
|
||||
if (isEmpty()) throw IllegalStateException("Empty semester list")
|
||||
|
||||
// when there is only one current semester
|
||||
singleOrNull { it.isCurrent() }?.let { return it }
|
||||
|
@ -1,11 +1,5 @@
|
||||
Wersja 2.5.0
|
||||
Wersja 2.5.8
|
||||
|
||||
— dodaliśmy wyświetlanie ogłoszeń
|
||||
— dodaliśmy opcję przywracania wiadomości z kosza
|
||||
— dodaliśmy opcję wyciszania nadawców wiadomości
|
||||
— naprawiliśmy opcjonalne liczenie średniej arytmetycznej, kiedy brak ocen z wagą w drugim semestrze
|
||||
— usprawniliśmy ładowanie frekwencji i planu lekcji
|
||||
— naprawiliśmy usprawiedliwianie nieobecności i autoryzację u użytkowników eduOne
|
||||
— zmieniliśmy komunikat o zmienionym haśle
|
||||
— obeszliśmy próby blokowania Wulkanowego przez firmę VULCAN, o czymś pewnie zapomnieliśmy, ale nie miejcie nam tego za złe
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -45,13 +45,30 @@
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/gradeHeaderPointsSum"
|
||||
app:layout_constraintEnd_toStartOf="@id/gradeHeaderAverageAllYear"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="@id/gradeHeaderSubject"
|
||||
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
|
||||
tools:text="Average: 6,00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderAverageAllYear"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/gradeHeaderPointsSum"
|
||||
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverage"
|
||||
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
|
||||
tools:text="Roczna: 5,00"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeHeaderPointsSum"
|
||||
android:layout_width="wrap_content"
|
||||
@ -64,7 +81,7 @@
|
||||
android:textSize="12sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toStartOf="@id/gradeHeaderNumber"
|
||||
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverage"
|
||||
app:layout_constraintStart_toEndOf="@+id/gradeHeaderAverageAllYear"
|
||||
app:layout_constraintTop_toBottomOf="@+id/gradeHeaderSubject"
|
||||
tools:text="Points: 123/200 (61,5%)" />
|
||||
|
||||
|
@ -20,20 +20,80 @@
|
||||
android:id="@+id/gradeSummaryItemTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:layout_weight="1"
|
||||
android:textSize="17sp"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradeSummaryItemAverageContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="35dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/grade_summary_average_semester"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeSummaryItemAverage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:gravity="end"
|
||||
android:textSize="12sp"
|
||||
tools:text="4.74" />
|
||||
tools:text="2,50" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/gradeSummaryItemAverageDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/ic_all_divider" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradeSummaryItemAverageAllYearContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="35dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/grade_summary_average_year"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeSummaryItemAverageAllYear"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="25dp"
|
||||
android:gravity="end"
|
||||
android:textSize="12sp"
|
||||
tools:text="4,50" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/gradeSummaryItemAverageAllYearDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/ic_all_divider" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gradeSummaryItemPointsContainer"
|
||||
android:layout_width="match_parent"
|
||||
@ -63,9 +123,9 @@
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/gradeSummaryItemPointsDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:id="@+id/gradeSummaryItemPointsDivider"
|
||||
android:background="@drawable/ic_all_divider" />
|
||||
|
||||
<LinearLayout
|
||||
@ -97,8 +157,8 @@
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/gradeSummaryItemPredictedDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/ic_all_divider" />
|
||||
|
||||
@ -131,9 +191,9 @@
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/gradeSummaryItemFinalDivider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:id="@+id/gradeSummaryItemFinalDivider"
|
||||
android:background="@drawable/ic_all_divider" />
|
||||
|
||||
<LinearLayout
|
||||
|
@ -10,10 +10,11 @@
|
||||
tools:context=".ui.modules.grade.summary.GradeSummaryAdapter">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
@ -21,9 +22,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minLines="2"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/grade_summary_calculated_average"
|
||||
android:textSize="16sp" />
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
@ -61,9 +62,64 @@
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/gradeSummaryScrollableHeaderCalculatedAnnualContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="visible">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minLines="2"
|
||||
android:text="@string/grade_summary_calculated_average_annual"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeSummaryScrollableHeaderCalculatedAnnual"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textSize="21sp"
|
||||
tools:text="6,00" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/gradeSummaryCalculatedAverageHelpAnnual"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@+string/grade_summary_calculated_average_help_dialog_title"
|
||||
app:srcCompat="@drawable/ic_help"
|
||||
app:tint="?colorOnBackground" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gradeSummaryScrollableHeaderCalculatedSubjectCountAnnual"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="13sp"
|
||||
tools:text="from 8 subjects" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
@ -71,9 +127,9 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:minLines="2"
|
||||
android:textStyle="bold"
|
||||
android:text="@string/grade_summary_final_average"
|
||||
android:textSize="16sp" />
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
@ -103,8 +159,8 @@
|
||||
<TextView
|
||||
android:id="@+id/gradeSummaryScrollableHeaderFinalSubjectCount"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:textSize="13sp"
|
||||
tools:text="from 5 subjects" />
|
||||
|
@ -852,8 +852,8 @@
|
||||
<string name="auth_description">Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli</string>
|
||||
<string name="auth_button_skip">Zatím přeskočit</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">VULCAN\'s website requires verification</string>
|
||||
<string name="captcha_dialog_description"><b>Why am I seeing this?</b>\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it</string>
|
||||
<string name="captcha_dialog_title">Webová stránka deníku VULCAN vyžaduje ověření</string>
|
||||
<string name="captcha_dialog_description"><b>Proč se mi to zobrazuje?</b>\nWebová stránka deníku, ze které Wulkanowy stahuje data, zobrazuje stejnou obrazovku jako výše, takže Wulkanowy ji musí také zobrazit, aby bylo možné získávat data z této stránky. Nedá se to obejít</string>
|
||||
<string name="captcha_verified_message">Úspěšně ověřeno</string>
|
||||
<!--Errors-->
|
||||
<string name="error_no_internet">Žádné internetové připojení</string>
|
||||
|
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Komentarz</string>
|
||||
<string name="grade_number_new_items">Ilość nowych ocen: %1$d</string>
|
||||
<string name="grade_average">Średnia: %1$.2f</string>
|
||||
<string name="grade_average_year">Roczna: %1$.2f</string>
|
||||
<string name="grade_points_sum">Punkty: %s</string>
|
||||
<string name="grade_no_average">Brak średniej</string>
|
||||
<string name="grade_summary_average_semester">Średnia semestralna</string>
|
||||
<string name="grade_summary_average_year">Średnia roczna</string>
|
||||
<string name="grade_summary_points">Suma punktów</string>
|
||||
<string name="grade_summary_final_grade">Ocena końcowa</string>
|
||||
<string name="grade_summary_predicted_grade">Przewidywana ocena</string>
|
||||
<string name="grade_summary_descriptive">Ocena opisowa</string>
|
||||
<string name="grade_summary_calculated_average">Obliczona średnia</string>
|
||||
<string name="grade_summary_calculated_average">Obliczona średnia semestralna</string>
|
||||
<string name="grade_summary_calculated_average_annual">Obliczona średnia roczna</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Jak działa obliczona średnia?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen tylko z wybranego semestru</b>:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia ze średnich z obu semestrów</b>:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Jak działa końcowa średnia?</string>
|
||||
|
@ -852,8 +852,8 @@
|
||||
<string name="auth_description">Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli</string>
|
||||
<string name="auth_button_skip">Zatiaľ preskočiť</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">VULCAN\'s website requires verification</string>
|
||||
<string name="captcha_dialog_description"><b>Why am I seeing this?</b>\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it</string>
|
||||
<string name="captcha_dialog_title">Webová stránka denníka VULCAN vyžaduje overenie</string>
|
||||
<string name="captcha_dialog_description"><b>Prečo sa mi to zobrazuje?</b>\nWebová stránka denníka, z ktorej Wulkanowy sťahuje dáta, zobrazuje rovnakú obrazovku ako vyššie, takže Wulkanowy ju musí tiež zobraziť, aby bolo možné získavať dáta z tejto stránky. Nedá sa to obísť</string>
|
||||
<string name="captcha_verified_message">Úspešne overené</string>
|
||||
<!--Errors-->
|
||||
<string name="error_no_internet">Žiadne internetové pripojenie</string>
|
||||
|
@ -46,14 +46,15 @@
|
||||
|
||||
<color name="timetable_canceled_light">#d32f2f</color>
|
||||
<color name="timetable_canceled_dark">#e57373</color>
|
||||
|
||||
<color name="timetable_change_light">#ff8f00</color>
|
||||
<color name="timetable_change_dark">#ffd54f</color>
|
||||
|
||||
<color name="attendance_absence_light">#d32f2f</color>
|
||||
<color name="attendance_absence_dark">#e57373</color>
|
||||
|
||||
<color name="attendance_lateness_light">#cd2a01</color>
|
||||
<color name="attendance_lateness_dark">#f05d0e</color>
|
||||
<color name="attendance_lateness_light">#ff8f00</color>
|
||||
<color name="attendance_lateness_dark">#ffd54f</color>
|
||||
|
||||
<color name="colorDivider">#1f000000</color>
|
||||
<color name="colorDividerInverse">#1fffffff</color>
|
||||
|
@ -126,13 +126,17 @@
|
||||
<string name="grade_comment">Comment</string>
|
||||
<string name="grade_number_new_items">Number of new ratings: %1$d</string>
|
||||
<string name="grade_average">Average: %1$.2f</string>
|
||||
<string name="grade_average_year">Annual: %1$.2f</string>
|
||||
<string name="grade_points_sum">Points: %s</string>
|
||||
<string name="grade_no_average">No average</string>
|
||||
<string name="grade_summary_average_semester">Semester average</string>
|
||||
<string name="grade_summary_average_year">Annual average</string>
|
||||
<string name="grade_summary_points">Total points</string>
|
||||
<string name="grade_summary_final_grade">Final grade</string>
|
||||
<string name="grade_summary_predicted_grade">Predicted grade</string>
|
||||
<string name="grade_summary_descriptive">Descriptive grade</string>
|
||||
<string name="grade_summary_calculated_average">Calculated average</string>
|
||||
<string name="grade_summary_calculated_average">Calculated semester average</string>
|
||||
<string name="grade_summary_calculated_average_annual">Calculated annual average</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">How does Calculated Average work?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">The Calculated Average is the arithmetic average calculated from the subjects averages. It allows you to know the approximate final average. It is calculated in a way selected by the user in the application settings. It is recommended that you choose the appropriate option. This is because the calculation of school averages differs. Additionally, if your school reports the average of the subjects on the Vulcan page, the application downloads them and does not calculate these averages. This can be changed by forcing the calculation of the average in the application settings.\n\n<b>Average of grades only from selected semester</b>:\n1. Calculating the weighted average for each subject in a given semester\n2.Adding calculated averages\n3. Calculation of the arithmetic average of the summed averages\n\n<b>Average of averages from both semesters</b>:\n1.Calculating the weighted average for each subject in semester 1 and 2\n2. Calculating the arithmetic average of the calculated averages for semesters 1 and 2 for each subject.\n3. Adding calculated averages\n4. Calculation of the arithmetic average of the summed averages\n\n<b>Average of grades from the whole year:</b>\n1. Calculating weighted average over the year for each subject. The final average in the 1st semester is irrelevant.\n2. Adding calculated averages\n3. Calculating the arithmetic average of summed averages</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">How does the Final Average work?</string>
|
||||
|
@ -93,8 +93,12 @@ class AdsHelper @Inject constructor(
|
||||
private fun initializeMobileAds() {
|
||||
if (isMobileAdsInitializeCalled.getAndSet(true)) return
|
||||
|
||||
MobileAds.initialize(context) {
|
||||
isMobileAdsSdkInitialized.value = true
|
||||
try {
|
||||
MobileAds.initialize(context) {
|
||||
isMobileAdsSdkInitialized.value = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import java.time.LocalDate
|
||||
import java.time.Instant.now
|
||||
import java.time.LocalDate
|
||||
import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
|
||||
|
||||
fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate = LocalDate.now(), end: LocalDate = LocalDate.now(), semesterName: Int = 1) = Semester(
|
||||
@ -72,6 +72,8 @@ fun getStudentEntity(mode: Sdk.Mode = Sdk.Mode.HEBE) = Student(
|
||||
symbol = "",
|
||||
userLoginId = 1,
|
||||
userName = "",
|
||||
isEduOne = false,
|
||||
isAuthorized = false
|
||||
).apply {
|
||||
id = 1
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package io.github.wulkanowy
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
||||
fun createWulkanowySdkFactoryMock(sdk: Sdk) = mockk<WulkanowySdkFactory>()
|
||||
.apply {
|
||||
every { create() } returns sdk
|
||||
coEvery { create(any(), any()) } returns sdk
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import android.os.Build
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||
import io.github.wulkanowy.getStudentEntity
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
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
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
class WulkanowySdkFactoryTest {
|
||||
|
||||
private lateinit var wulkanowySdkFactory: WulkanowySdkFactory
|
||||
private lateinit var studentDao: StudentDao
|
||||
private lateinit var sdk: Sdk
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
sdk = mockk(relaxed = true)
|
||||
studentDao = mockk()
|
||||
wulkanowySdkFactory = spyk(
|
||||
WulkanowySdkFactory(
|
||||
chuckerInterceptor = mockk(),
|
||||
remoteConfig = mockk(relaxed = true),
|
||||
webkitCookieManagerProxy = mockk(),
|
||||
studentDb = studentDao
|
||||
)
|
||||
)
|
||||
|
||||
every { wulkanowySdkFactory.create() } returns sdk
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check sdk flag isEduOne when local student is eduone`() = runTest {
|
||||
val student = getStudentEntity().copy(isEduOne = true)
|
||||
|
||||
wulkanowySdkFactory.create(student)
|
||||
|
||||
verify { sdk.isEduOne = true }
|
||||
coVerify(exactly = 0) { sdk.getCurrentStudent() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check sdk flag isEduOne when local student is not eduone`() = runTest {
|
||||
val student = getStudentEntity().copy(isEduOne = false)
|
||||
|
||||
wulkanowySdkFactory.create(student)
|
||||
|
||||
verify { sdk.isEduOne = false }
|
||||
coVerify(exactly = 0) { sdk.getCurrentStudent() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check sdk flag isEduOne when local student is eduone null and remote student is eduone true`() =
|
||||
runTest {
|
||||
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||
val registerStudent = studentToProcess.toRegisterStudent(isEduOne = true)
|
||||
|
||||
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||
coEvery { sdk.getCurrentStudent() } returns registerStudent
|
||||
|
||||
wulkanowySdkFactory.create(studentToProcess)
|
||||
|
||||
verify { sdk.isEduOne = true }
|
||||
coVerify { sdk.getCurrentStudent() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check sdk flag isEduOne when local student is eduone null and remote student is eduone false`() =
|
||||
runTest {
|
||||
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||
val registerStudent = studentToProcess.toRegisterStudent(isEduOne = false)
|
||||
|
||||
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||
coEvery { sdk.getCurrentStudent() } returns registerStudent
|
||||
|
||||
wulkanowySdkFactory.create(studentToProcess)
|
||||
|
||||
verify { sdk.isEduOne = false }
|
||||
coVerify { sdk.getCurrentStudent() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check sdk flag isEduOne when sdk getCurrentStudent throws error`() =
|
||||
runTest {
|
||||
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||
|
||||
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||
coEvery { sdk.getCurrentStudent() } throws Exception()
|
||||
|
||||
wulkanowySdkFactory.create(studentToProcess)
|
||||
|
||||
verify { sdk.isEduOne = false }
|
||||
coVerify { sdk.getCurrentStudent() }
|
||||
}
|
||||
|
||||
private fun Student.toRegisterStudent(isEduOne: Boolean) = RegisterStudent(
|
||||
studentId = studentId,
|
||||
studentName = studentName,
|
||||
studentSecondName = studentName,
|
||||
studentSurname = studentName,
|
||||
className = className,
|
||||
classId = classId,
|
||||
isParent = isParent,
|
||||
isAuthorized = isAuthorized,
|
||||
semesters = emptyList(),
|
||||
isEduOne = isEduOne,
|
||||
)
|
||||
}
|
@ -21,10 +21,10 @@ abstract class AbstractMigrationTest {
|
||||
|
||||
@get:Rule
|
||||
val helper: MigrationTestHelper = MigrationTestHelper(
|
||||
InstrumentationRegistry.getInstrumentation(),
|
||||
AppDatabase::class.java,
|
||||
listOf(Migration55()),
|
||||
FrameworkSQLiteOpenHelperFactory()
|
||||
instrumentation = InstrumentationRegistry.getInstrumentation(),
|
||||
databaseClass = AppDatabase::class.java,
|
||||
specs = listOf(Migration63()),
|
||||
openFactory = FrameworkSQLiteOpenHelperFactory()
|
||||
)
|
||||
|
||||
fun runMigrationsAndValidate(migration: Migration) {
|
||||
|
@ -7,8 +7,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.ADFSLight
|
||||
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.ADFSLightScoped
|
||||
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.STANDARD
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@ -19,7 +20,6 @@ import kotlin.test.assertEquals
|
||||
|
||||
@HiltAndroidTest
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
class Migration54Test : AbstractMigrationTest() {
|
||||
|
||||
|
@ -0,0 +1,89 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.os.Build
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import dagger.hilt.android.testing.HiltAndroidTest
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.runner.RunWith
|
||||
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)
|
||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||
class Migration63Test : AbstractMigrationTest() {
|
||||
|
||||
@Test
|
||||
fun `update is_edu_one to null if 0`() = runTest {
|
||||
with(helper.createDatabase(dbName, 62)) {
|
||||
createStudent(1, 0)
|
||||
close()
|
||||
}
|
||||
|
||||
helper.runMigrationsAndValidate(dbName, 63, true)
|
||||
|
||||
val database = getMigratedRoomDatabase()
|
||||
val studentDb = database.studentDao
|
||||
val student = studentDb.loadById(1)
|
||||
|
||||
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")
|
||||
put("mobile_base_url", "")
|
||||
put("login_type", "SCRAPPER")
|
||||
put("login_mode", "SCRAPPER")
|
||||
put("certificate_key", "")
|
||||
put("private_key", "")
|
||||
put("is_parent", false)
|
||||
put("email", "jan@fakelog.cf")
|
||||
put("password", "******")
|
||||
put("symbol", "symbol")
|
||||
put("student_id", Random.nextInt())
|
||||
put("user_login_id", 123)
|
||||
put("user_name", "studentName")
|
||||
put("student_name", "studentName")
|
||||
put("school_id", "123")
|
||||
put("school_short", "")
|
||||
put("school_name", "")
|
||||
put("class_name", "")
|
||||
put("class_id", Random.nextInt())
|
||||
put("is_current", false)
|
||||
put("registration_date", "0")
|
||||
put("id", id)
|
||||
put("nick", "")
|
||||
put("avatar_color", "")
|
||||
put("is_edu_one", isEduOneValue)
|
||||
})
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
@ -16,8 +17,8 @@ import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@ -30,8 +31,8 @@ import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
||||
|
||||
class AttendanceRepositoryTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var attendanceDb: AttendanceDao
|
||||
@ -63,7 +64,8 @@ class AttendanceRepositoryTest {
|
||||
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
||||
coEvery { timetableDb.load(any(), any(), any(), any()) } returns emptyList()
|
||||
|
||||
attendanceRepository = AttendanceRepository(attendanceDb, timetableDb, sdk, refreshHelper)
|
||||
attendanceRepository =
|
||||
AttendanceRepository(attendanceDb, timetableDb, wulkanowySdkFactory, refreshHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
import io.github.wulkanowy.data.errorOrNull
|
||||
@ -15,8 +16,8 @@ import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
@ -28,8 +29,8 @@ import io.github.wulkanowy.sdk.pojo.CompletedLesson as SdkCompletedLesson
|
||||
|
||||
class CompletedLessonsRepositoryTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var completedLessonDb: CompletedLessonsDao
|
||||
@ -58,7 +59,7 @@ class CompletedLessonsRepositoryTest {
|
||||
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
||||
|
||||
completedLessonRepository =
|
||||
CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
|
||||
CompletedLessonsRepository(completedLessonDb, wulkanowySdkFactory, refreshHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||
import io.github.wulkanowy.data.errorOrNull
|
||||
@ -15,8 +16,8 @@ import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@ -28,8 +29,8 @@ import io.github.wulkanowy.sdk.pojo.Exam as SdkExam
|
||||
|
||||
class ExamRemoteTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var examDb: ExamDao
|
||||
@ -59,7 +60,7 @@ class ExamRemoteTest {
|
||||
MockKAnnotations.init(this)
|
||||
every { refreshHelper.shouldBeRefreshed(any()) } returns false
|
||||
|
||||
examRepository = ExamRepository(examDb, sdk, refreshHelper)
|
||||
examRepository = ExamRepository(examDb, wulkanowySdkFactory, refreshHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
||||
@ -18,8 +19,8 @@ import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@ -35,8 +36,8 @@ import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade
|
||||
|
||||
class GradeRepositoryTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var gradeDb: GradeDao
|
||||
@ -65,7 +66,7 @@ class GradeRepositoryTest {
|
||||
gradeDb = gradeDb,
|
||||
gradeSummaryDb = gradeSummaryDb,
|
||||
gradeDescriptiveDb = gradeDescriptiveDb,
|
||||
sdk = sdk,
|
||||
wulkanowySdkFactory = wulkanowySdkFactory,
|
||||
refreshHelper = refreshHelper,
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||
@ -13,9 +14,14 @@ import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
|
||||
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.mockk.*
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
@ -24,8 +30,8 @@ import org.junit.Test
|
||||
|
||||
class GradeStatisticsRepositoryTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var gradePartialStatisticsDb: GradePartialStatisticsDao
|
||||
@ -54,7 +60,7 @@ class GradeStatisticsRepositoryTest {
|
||||
gradePartialStatisticsDb = gradePartialStatisticsDb,
|
||||
gradePointsStatisticsDb = gradePointsStatisticsDb,
|
||||
gradeSemesterStatisticsDb = gradeSemesterStatisticsDb,
|
||||
sdk = sdk,
|
||||
wulkanowySdkFactory = wulkanowySdkFactory,
|
||||
refreshHelper = refreshHelper,
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.createWulkanowySdkFactoryMock
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||
import io.github.wulkanowy.data.errorOrNull
|
||||
@ -12,8 +13,8 @@ import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.impl.annotations.SpyK
|
||||
import io.mockk.just
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
@ -25,8 +26,8 @@ import io.github.wulkanowy.sdk.pojo.LuckyNumber as SdkLuckyNumber
|
||||
|
||||
class LuckyNumberRemoteTest {
|
||||
|
||||
@SpyK
|
||||
private var sdk = Sdk()
|
||||
private var sdk = spyk<Sdk>()
|
||||
private val wulkanowySdkFactory = createWulkanowySdkFactoryMock(sdk)
|
||||
|
||||
@MockK
|
||||
private lateinit var luckyNumberDb: LuckyNumberDao
|
||||
@ -43,7 +44,7 @@ class LuckyNumberRemoteTest {
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
|
||||
luckyNumberRepository = LuckyNumberRepository(luckyNumberDb, sdk)
|
||||
luckyNumberRepository = LuckyNumberRepository(luckyNumberDb, wulkanowySdkFactory)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user