Compare commits
73 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
7b2c839775 | |||
f455064b9d | |||
2bbc157d03 | |||
a0a0b8dea6 | |||
3bab883a56 | |||
b319bb03cd | |||
333306e7ba | |||
fb240938ed | |||
dc9af29a44 | |||
e9d64de0cb | |||
05bda598fc | |||
3564366a8f | |||
f2d26453ed | |||
ccba31f2e8 | |||
a7238e3f23 | |||
ea28fc783c | |||
c04752ed39 | |||
c198e6a2f7 | |||
2c1337bb51 | |||
7a4032dda4 | |||
1ab300d74f | |||
1b8c389984 | |||
74a20b2f65 | |||
d5c17285c1 | |||
e378b4c70a | |||
31854fc4b8 | |||
f52fe8306f | |||
3eae3a7667 | |||
b613b84469 | |||
2776d019b9 | |||
729e72cddb | |||
ec101c1f52 | |||
cfec79405f | |||
7d8be1b9fc | |||
c781159e75 | |||
90a5b9e20f | |||
3cf6c295b0 | |||
e757585bd3 | |||
736d16a7ab | |||
6f4a8d5534 | |||
b5e17c4ff7 | |||
cc01525f16 | |||
c2ec05662b |
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: wulkanowy
|
||||||
|
custom: https://www.paypal.com/paypalme/wulkanowy
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -67,6 +67,10 @@ captures/
|
|||||||
.idea/discord.xml
|
.idea/discord.xml
|
||||||
.idea/migrations.xml
|
.idea/migrations.xml
|
||||||
.idea/androidTestResultsUserPreferences.xml
|
.idea/androidTestResultsUserPreferences.xml
|
||||||
|
.idea/copilot
|
||||||
|
.idea/deploymentTargetDropDown.xml
|
||||||
|
.idea/deploymentTargetSelector.xml
|
||||||
|
.idea/kotlinc.xml
|
||||||
|
|
||||||
# Keystore files
|
# Keystore files
|
||||||
*.jks
|
*.jks
|
||||||
|
@ -27,8 +27,8 @@ android {
|
|||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 146
|
versionCode 154
|
||||||
versionName "2.4.0"
|
versionName "2.5.5"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
@ -164,7 +164,7 @@ play {
|
|||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||||
userFraction = 0.50d
|
userFraction = 0.99d
|
||||||
updatePriority = 1
|
updatePriority = 1
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
@ -187,19 +187,19 @@ huaweiPublish {
|
|||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.9.0"
|
work_manager = "2.9.0"
|
||||||
android_hilt = "1.1.0"
|
android_hilt = "1.2.0"
|
||||||
room = "2.6.1"
|
room = "2.6.1"
|
||||||
chucker = "4.0.0"
|
chucker = "4.0.0"
|
||||||
mockk = "1.13.9"
|
mockk = "1.13.10"
|
||||||
coroutines = "1.7.3"
|
coroutines = "1.8.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.github.wulkanowy:sdk:2.4.0'
|
implementation 'io.github.wulkanowy:sdk:2.5.5'
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.12.0'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
@ -246,13 +246,13 @@ dependencies {
|
|||||||
implementation 'com.github.Faierbel:slf4j-timber:2.0'
|
implementation 'com.github.Faierbel:slf4j-timber:2.0'
|
||||||
implementation 'com.github.bastienpaulfr:Treessence:1.1.2'
|
implementation 'com.github.bastienpaulfr:Treessence:1.1.2'
|
||||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||||
implementation 'io.coil-kt:coil:2.5.0'
|
implementation 'io.coil-kt:coil:2.6.0'
|
||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||||
implementation 'org.apache.commons:commons-text:1.11.0'
|
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:32.7.1')
|
playImplementation platform('com.google.firebase:firebase-bom:32.7.3')
|
||||||
playImplementation 'com.google.firebase:firebase-analytics'
|
playImplementation 'com.google.firebase:firebase-analytics'
|
||||||
playImplementation 'com.google.firebase:firebase-messaging'
|
playImplementation 'com.google.firebase:firebase-messaging'
|
||||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||||
@ -262,7 +262,7 @@ dependencies {
|
|||||||
playImplementation "com.google.android.play:integrity:1.3.0"
|
playImplementation "com.google.android.play:integrity:1.3.0"
|
||||||
playImplementation 'com.google.android.play:app-update-ktx:2.1.0'
|
playImplementation 'com.google.android.play:app-update-ktx:2.1.0'
|
||||||
playImplementation 'com.google.android.play:review-ktx:2.0.1'
|
playImplementation 'com.google.android.play:review-ktx:2.0.1'
|
||||||
playImplementation "com.google.android.ump:user-messaging-platform:2.2.0"
|
playImplementation "com.google.android.ump:user-messaging-platform:2.1.0"
|
||||||
|
|
||||||
hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.301'
|
hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.301'
|
||||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303'
|
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.303'
|
||||||
|
2527
app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json
Normal file
2527
app/schemas/io.github.wulkanowy.data.db.AppDatabase/60.json
Normal file
File diff suppressed because it is too large
Load Diff
2533
app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json
Normal file
2533
app/schemas/io.github.wulkanowy.data.db.AppDatabase/61.json
Normal file
File diff suppressed because it is too large
Load Diff
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
@ -54,5 +54,9 @@
|
|||||||
{
|
{
|
||||||
"displayName": "Antoni Paduch",
|
"displayName": "Antoni Paduch",
|
||||||
"githubUsername": "janAte1"
|
"githubUsername": "janAte1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"displayName": "Kamil Wąsik",
|
||||||
|
"githubUsername": "JestemKamil"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -18,17 +18,13 @@ import io.github.wulkanowy.data.api.SchoolsService
|
|||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
|
||||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.create
|
import retrofit2.create
|
||||||
import timber.log.Timber
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -36,20 +32,6 @@ import javax.inject.Singleton
|
|||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
internal class DataModule {
|
internal class DataModule {
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
|
|
||||||
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
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideChuckerCollector(
|
fun provideChuckerCollector(
|
||||||
@ -254,6 +236,10 @@ internal class DataModule {
|
|||||||
@Provides
|
@Provides
|
||||||
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
|
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Provides
|
||||||
|
fun provideMutesDao(database: AppDatabase) = database.mutedMessageSendersDao
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideGradeDescriptiveDao(database: AppDatabase) = database.gradeDescriptiveDao
|
fun provideGradeDescriptiveDao(database: AppDatabase) = database.gradeDescriptiveDao
|
||||||
|
@ -30,8 +30,15 @@ val <T> Resource<T>.dataOrNull: T?
|
|||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is Resource.Success -> this.data
|
is Resource.Success -> this.data
|
||||||
is Resource.Intermediate -> this.data
|
is Resource.Intermediate -> this.data
|
||||||
is Resource.Loading -> null
|
else -> null
|
||||||
is Resource.Error -> null
|
}
|
||||||
|
|
||||||
|
val <T> Resource<T>.dataOrThrow: T
|
||||||
|
get() = when (this) {
|
||||||
|
is Resource.Success -> this.data
|
||||||
|
is Resource.Intermediate -> this.data
|
||||||
|
is Resource.Loading -> throw IllegalStateException("Resource is in loading state")
|
||||||
|
is Resource.Error -> throw this.error
|
||||||
}
|
}
|
||||||
|
|
||||||
val <T> Resource<T>.errorOrNull: Throwable?
|
val <T> Resource<T>.errorOrNull: Throwable?
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ import io.github.wulkanowy.data.db.dao.MailboxDao
|
|||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
|
||||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||||
import io.github.wulkanowy.data.db.dao.NotificationDao
|
import io.github.wulkanowy.data.db.dao.NotificationDao
|
||||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||||
@ -56,6 +57,7 @@ import io.github.wulkanowy.data.db.entities.Mailbox
|
|||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
|
import io.github.wulkanowy.data.db.entities.MutedMessageSender
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
import io.github.wulkanowy.data.db.entities.Notification
|
import io.github.wulkanowy.data.db.entities.Notification
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
@ -118,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.Migration57
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration58
|
import io.github.wulkanowy.data.db.migrations.Migration58
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
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.Migration7
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||||
@ -157,6 +160,7 @@ import javax.inject.Singleton
|
|||||||
SchoolAnnouncement::class,
|
SchoolAnnouncement::class,
|
||||||
Notification::class,
|
Notification::class,
|
||||||
AdminMessage::class,
|
AdminMessage::class,
|
||||||
|
MutedMessageSender::class,
|
||||||
GradeDescriptive::class,
|
GradeDescriptive::class,
|
||||||
],
|
],
|
||||||
autoMigrations = [
|
autoMigrations = [
|
||||||
@ -169,6 +173,10 @@ import javax.inject.Singleton
|
|||||||
AutoMigration(from = 56, to = 57, spec = Migration57::class),
|
AutoMigration(from = 56, to = 57, spec = Migration57::class),
|
||||||
AutoMigration(from = 57, to = 58, spec = Migration58::class),
|
AutoMigration(from = 57, to = 58, spec = Migration58::class),
|
||||||
AutoMigration(from = 58, to = 59),
|
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),
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -177,7 +185,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 59
|
const val VERSION_SCHEMA = 63
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
@ -303,5 +311,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
|
|
||||||
abstract val adminMessagesDao: AdminMessageDao
|
abstract val adminMessagesDao: AdminMessageDao
|
||||||
|
|
||||||
|
abstract val mutedMessageSendersDao: MutedMessageSendersDao
|
||||||
|
|
||||||
abstract val gradeDescriptiveDao: GradeDescriptiveDao
|
abstract val gradeDescriptiveDao: GradeDescriptiveDao
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,14 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
|
||||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Dao
|
@Dao
|
||||||
abstract class AdminMessageDao : BaseDao<AdminMessage> {
|
interface AdminMessageDao : BaseDao<AdminMessage> {
|
||||||
|
|
||||||
@Query("SELECT * FROM AdminMessages")
|
@Query("SELECT * FROM AdminMessages")
|
||||||
abstract fun loadAll(): Flow<List<AdminMessage>>
|
fun loadAll(): Flow<List<AdminMessage>>
|
||||||
|
|
||||||
@Transaction
|
|
||||||
open suspend fun removeOldAndSaveNew(
|
|
||||||
oldMessages: List<AdminMessage>,
|
|
||||||
newMessages: List<AdminMessage>
|
|
||||||
) {
|
|
||||||
deleteAll(oldMessages)
|
|
||||||
insertAll(newMessages)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Transaction
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
|
||||||
interface BaseDao<T> {
|
interface BaseDao<T> {
|
||||||
@ -15,4 +16,10 @@ interface BaseDao<T> {
|
|||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun deleteAll(items: List<T>)
|
suspend fun deleteAll(items: List<T>)
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
suspend fun removeOldAndSaveNew(oldItems: List<T>, newItems: List<T>) {
|
||||||
|
deleteAll(oldItems)
|
||||||
|
insertAll(newItems)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,15 +5,23 @@ import androidx.room.Query
|
|||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface MessagesDao : BaseDao<Message> {
|
interface MessagesDao : BaseDao<Message> {
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
|
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
|
||||||
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
|
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
||||||
|
fun loadMessagesWithMutedAuthor(mailboxKey: String, folder: Int): Flow<List<MessageWithMutedAuthor>>
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
@Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
|
||||||
|
fun loadMessagesWithMutedAuthor(folder: Int, email: String): Flow<List<MessageWithMutedAuthor>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
||||||
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
||||||
|
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import io.github.wulkanowy.data.db.entities.MutedMessageSender
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface MutedMessageSendersDao : BaseDao<MutedMessageSender> {
|
||||||
|
|
||||||
|
@Query("SELECT COUNT(*) FROM MutedMessageSenders WHERE author = :author")
|
||||||
|
suspend fun checkMute(author: String): Boolean
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
|
suspend fun insertMute(mute: MutedMessageSender): Long
|
||||||
|
|
||||||
|
@Query("DELETE FROM MutedMessageSenders WHERE author = :author")
|
||||||
|
suspend fun deleteMute(author: String)
|
||||||
|
}
|
@ -14,6 +14,6 @@ interface SemesterDao : BaseDao<Semester> {
|
|||||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||||
suspend fun insertSemesters(items: List<Semester>): List<Long>
|
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>
|
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import androidx.room.Transaction
|
|||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.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.StudentName
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -23,6 +25,12 @@ abstract class StudentDao {
|
|||||||
@Delete
|
@Delete
|
||||||
abstract suspend fun delete(student: Student)
|
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)
|
@Update(entity = Student::class)
|
||||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||||
|
|
||||||
@ -39,11 +47,11 @@ abstract class StudentDao {
|
|||||||
abstract suspend fun loadAll(): List<Student>
|
abstract suspend fun loadAll(): List<Student>
|
||||||
|
|
||||||
@Transaction
|
@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>>
|
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
|
||||||
|
|
||||||
@Transaction
|
@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>>
|
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
|
||||||
|
|
||||||
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||||
|
@ -15,5 +15,5 @@ interface TimetableDao : BaseDao<Timetable> {
|
|||||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
suspend fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,15 @@ package io.github.wulkanowy.data.db.entities
|
|||||||
|
|
||||||
import androidx.room.Embedded
|
import androidx.room.Embedded
|
||||||
import androidx.room.Relation
|
import androidx.room.Relation
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
data class MessageWithAttachment(
|
data class MessageWithAttachment(
|
||||||
@Embedded
|
@Embedded
|
||||||
val message: Message,
|
val message: Message,
|
||||||
|
|
||||||
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
|
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
|
||||||
val attachments: List<MessageAttachment>
|
val attachments: List<MessageAttachment>,
|
||||||
)
|
|
||||||
|
@Relation(parentColumn = "correspondents", entityColumn = "author")
|
||||||
|
val mutedMessageSender: MutedMessageSender?,
|
||||||
|
) : Serializable
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.Embedded
|
||||||
|
import androidx.room.Relation
|
||||||
|
|
||||||
|
data class MessageWithMutedAuthor(
|
||||||
|
@Embedded
|
||||||
|
val message: Message,
|
||||||
|
|
||||||
|
@Relation(parentColumn = "correspondents", entityColumn = "author")
|
||||||
|
val mutedMessageSender: MutedMessageSender?,
|
||||||
|
)
|
@ -0,0 +1,15 @@
|
|||||||
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
@Entity(tableName = "MutedMessageSenders")
|
||||||
|
data class MutedMessageSender(
|
||||||
|
@ColumnInfo(name = "author")
|
||||||
|
val author: String,
|
||||||
|
) : Serializable {
|
||||||
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
var id: Long = 0
|
||||||
|
}
|
@ -16,7 +16,9 @@ data class SchoolAnnouncement(
|
|||||||
|
|
||||||
val subject: String,
|
val subject: String,
|
||||||
|
|
||||||
val content: String
|
val content: String,
|
||||||
|
|
||||||
|
val author: String? = null,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -78,6 +78,13 @@ data class Student(
|
|||||||
|
|
||||||
@ColumnInfo(name = "registration_date")
|
@ColumnInfo(name = "registration_date")
|
||||||
val registrationDate: Instant,
|
val registrationDate: Instant,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
||||||
|
val isAuthorized: Boolean,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||||
|
val isEduOne: Boolean?,
|
||||||
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
@ -88,3 +95,22 @@ data class Student(
|
|||||||
@ColumnInfo(name = "avatar_color")
|
@ColumnInfo(name = "avatar_color")
|
||||||
var avatarColor = 0L
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -3,5 +3,10 @@ package io.github.wulkanowy.data.enums
|
|||||||
enum class MessageFolder(val id: Int = 1) {
|
enum class MessageFolder(val id: Int = 1) {
|
||||||
RECEIVED(1),
|
RECEIVED(1),
|
||||||
SENT(2),
|
SENT(2),
|
||||||
TRASHED(3)
|
TRASHED(3),
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun byId(id: Int) = entries.first { it.id == id }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,26 @@ package io.github.wulkanowy.data.mappers
|
|||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
|
import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformation
|
||||||
|
import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
|
||||||
|
|
||||||
|
@JvmName("mapDirectorInformationToEntities")
|
||||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||||
SchoolAnnouncement(
|
SchoolAnnouncement(
|
||||||
userLoginId = student.userLoginId,
|
userLoginId = student.userLoginId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
content = it.content,
|
content = it.content,
|
||||||
|
author = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmName("mapLastAnnouncementsToEntities")
|
||||||
|
fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map {
|
||||||
|
SchoolAnnouncement(
|
||||||
|
userLoginId = student.userLoginId,
|
||||||
|
date = it.date,
|
||||||
|
subject = it.subject,
|
||||||
|
content = it.content,
|
||||||
|
author = it.author,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -34,17 +34,19 @@ fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
|||||||
error = it.error,
|
error = it.error,
|
||||||
students = it.subjects
|
students = it.subjects
|
||||||
.filterIsInstance<SdkRegisterStudent>()
|
.filterIsInstance<SdkRegisterStudent>()
|
||||||
.map { registerSubject ->
|
.map { registerStudent ->
|
||||||
RegisterStudent(
|
RegisterStudent(
|
||||||
studentId = registerSubject.studentId,
|
studentId = registerStudent.studentId,
|
||||||
studentName = registerSubject.studentName,
|
studentName = registerStudent.studentName,
|
||||||
studentSecondName = registerSubject.studentSecondName,
|
studentSecondName = registerStudent.studentSecondName,
|
||||||
studentSurname = registerSubject.studentSurname,
|
studentSurname = registerStudent.studentSurname,
|
||||||
className = registerSubject.className,
|
className = registerStudent.className,
|
||||||
classId = registerSubject.classId,
|
classId = registerStudent.classId,
|
||||||
isParent = registerSubject.isParent,
|
isParent = registerStudent.isParent,
|
||||||
semesters = registerSubject.semesters
|
isAuthorized = registerStudent.isAuthorized,
|
||||||
.mapToEntities(registerSubject.studentId),
|
isEduOne = registerStudent.isEduOne,
|
||||||
|
semesters = registerStudent.semesters
|
||||||
|
.mapToEntities(registerStudent.studentId),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -84,6 +86,8 @@ fun RegisterStudent.mapToStudentWithSemesters(
|
|||||||
password = user.password.orEmpty(),
|
password = user.password.orEmpty(),
|
||||||
isCurrent = false,
|
isCurrent = false,
|
||||||
registrationDate = Instant.now(),
|
registrationDate = Instant.now(),
|
||||||
|
isAuthorized = this.isAuthorized,
|
||||||
|
isEduOne = this.isEduOne,
|
||||||
).apply {
|
).apply {
|
||||||
avatarColor = colors.random()
|
avatarColor = colors.random()
|
||||||
},
|
},
|
||||||
|
@ -45,4 +45,6 @@ data class RegisterStudent(
|
|||||||
val classId: Int,
|
val classId: Int,
|
||||||
val isParent: Boolean,
|
val isParent: Boolean,
|
||||||
val semesters: List<Semester>,
|
val semesters: List<Semester>,
|
||||||
|
val isAuthorized: Boolean,
|
||||||
|
val isEduOne: Boolean
|
||||||
) : java.io.Serializable
|
) : java.io.Serializable
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.AttendanceDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
@ -7,19 +8,14 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.sdk.pojo.Absent
|
import io.github.wulkanowy.sdk.pojo.Absent
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
@ -30,7 +26,7 @@ import javax.inject.Singleton
|
|||||||
class AttendanceRepository @Inject constructor(
|
class AttendanceRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceDao,
|
private val attendanceDb: AttendanceDao,
|
||||||
private val timetableDb: TimetableDao,
|
private val timetableDb: TimetableDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -58,23 +54,21 @@ class AttendanceRepository @Inject constructor(
|
|||||||
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val lessons = withContext(Dispatchers.IO) {
|
val lessons = timetableDb.load(
|
||||||
timetableDb.load(
|
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||||
semester.diaryId, semester.studentId, start.monday, end.sunday
|
)
|
||||||
)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
}
|
|
||||||
sdk.init(student)
|
|
||||||
.switchSemester(semester)
|
|
||||||
.getAttendance(start.monday, end.sunday)
|
.getAttendance(start.monday, end.sunday)
|
||||||
.mapToEntities(semester, lessons)
|
.mapToEntities(semester, lessons)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
|
||||||
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
||||||
newAttendance.apply { if (notify) isNotified = false }
|
newAttendance.apply { if (notify) isNotified = false }
|
||||||
}
|
}
|
||||||
attendanceDb.insertAll(attendanceToAdd)
|
attendanceDb.removeOldAndSaveNew(
|
||||||
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = attendanceToAdd,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
@ -93,8 +87,10 @@ class AttendanceRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun excuseForAbsence(
|
suspend fun excuseForAbsence(
|
||||||
student: Student, semester: Semester,
|
student: Student,
|
||||||
absenceList: List<Attendance>, reason: String? = null
|
semester: Semester,
|
||||||
|
absenceList: List<Attendance>,
|
||||||
|
reason: String? = null
|
||||||
) {
|
) {
|
||||||
val items = absenceList.map { attendance ->
|
val items = absenceList.map { attendance ->
|
||||||
Absent(
|
Absent(
|
||||||
@ -102,8 +98,7 @@ class AttendanceRepository @Inject constructor(
|
|||||||
timeId = attendance.timeId
|
timeId = attendance.timeId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.excuseForAbsence(items, reason)
|
.excuseForAbsence(items, reason)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.AttendanceSummaryDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,8 +18,9 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class AttendanceSummaryRepository @Inject constructor(
|
class AttendanceSummaryRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceSummaryDao,
|
private val attendanceDb: AttendanceSummaryDao,
|
||||||
private val sdk: Sdk,
|
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
|
private val appDatabase: AppDatabase,
|
||||||
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -40,14 +41,15 @@ class AttendanceSummaryRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getAttendanceSummary(subjectId)
|
.getAttendanceSummary(subjectId)
|
||||||
.mapToEntities(semester, subjectId)
|
.mapToEntities(semester, subjectId)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
appDatabase.withTransaction {
|
||||||
attendanceDb.insertAll(new uniqueSubtract old)
|
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||||
|
attendanceDb.insertAll(new uniqueSubtract old)
|
||||||
|
}
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.CompletedLessonsDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.monday
|
||||||
|
import io.github.wulkanowy.utils.sunday
|
||||||
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -15,7 +19,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class CompletedLessonsRepository @Inject constructor(
|
class CompletedLessonsRepository @Inject constructor(
|
||||||
private val completedLessonsDb: CompletedLessonsDao,
|
private val completedLessonsDb: CompletedLessonsDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -47,14 +51,15 @@ class CompletedLessonsRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getCompletedLessons(start.monday, end.sunday)
|
.getCompletedLessons(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
completedLessonsDb.deleteAll(old uniqueSubtract new)
|
completedLessonsDb.removeOldAndSaveNew(
|
||||||
completedLessonsDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.ConferenceDao
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class ConferenceRepository @Inject constructor(
|
class ConferenceRepository @Inject constructor(
|
||||||
private val conferenceDb: ConferenceDao,
|
private val conferenceDb: ConferenceDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -46,19 +44,18 @@ class ConferenceRepository @Inject constructor(
|
|||||||
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getConferences()
|
.getConferences()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
.filter { it.date >= startDate }
|
.filter { it.date >= startDate }
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
val conferencesToSave = (new uniqueSubtract old).onEach {
|
conferenceDb.removeOldAndSaveNew(
|
||||||
if (notify) it.isNotified = false
|
oldItems = old uniqueSubtract new,
|
||||||
}
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
|
if (notify) it.isNotified = false
|
||||||
conferenceDb.deleteAll(old uniqueSubtract new)
|
},
|
||||||
conferenceDb.insertAll(conferencesToSave)
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.ExamDao
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.endExamsDay
|
import io.github.wulkanowy.utils.endExamsDay
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.startExamsDay
|
import io.github.wulkanowy.utils.startExamsDay
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -23,7 +21,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class ExamRepository @Inject constructor(
|
class ExamRepository @Inject constructor(
|
||||||
private val examDb: ExamDao,
|
private val examDb: ExamDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -56,18 +54,17 @@ class ExamRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getExams(start.startExamsDay, start.endExamsDay)
|
.getExams(start.startExamsDay, start.endExamsDay)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
val examsToSave = (new uniqueSubtract old).onEach {
|
examDb.removeOldAndSaveNew(
|
||||||
if (notify) it.isNotified = false
|
oldItems = old uniqueSubtract new,
|
||||||
}
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
|
if (notify) it.isNotified = false
|
||||||
examDb.deleteAll(old uniqueSubtract new)
|
},
|
||||||
examDb.insertAll(examsToSave)
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
},
|
},
|
||||||
filterResult = { it.filter { item -> item.date in start..end } }
|
filterResult = { it.filter { item -> item.date in start..end } }
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.GradeDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
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.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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.toLocalDate
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -30,7 +28,7 @@ class GradeRepository @Inject constructor(
|
|||||||
private val gradeDb: GradeDao,
|
private val gradeDb: GradeDao,
|
||||||
private val gradeSummaryDb: GradeSummaryDao,
|
private val gradeSummaryDb: GradeSummaryDao,
|
||||||
private val gradeDescriptiveDb: GradeDescriptiveDao,
|
private val gradeDescriptiveDb: GradeDescriptiveDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -63,8 +61,7 @@ class GradeRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val (details, summary, descriptive) = sdk.init(student)
|
val (details, summary, descriptive) = wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getGrades(semester.semesterId)
|
.getGrades(semester.semesterId)
|
||||||
|
|
||||||
Triple(
|
Triple(
|
||||||
@ -87,10 +84,12 @@ class GradeRepository @Inject constructor(
|
|||||||
new: List<GradeDescriptive>,
|
new: List<GradeDescriptive>,
|
||||||
notify: Boolean
|
notify: Boolean
|
||||||
) {
|
) {
|
||||||
gradeDescriptiveDb.deleteAll(old uniqueSubtract new)
|
gradeDescriptiveDb.removeOldAndSaveNew(
|
||||||
gradeDescriptiveDb.insertAll((new uniqueSubtract old).onEach {
|
oldItems = old uniqueSubtract new,
|
||||||
if (notify) it.isNotified = false
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
})
|
if (notify) it.isNotified = false
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshGradeDetails(
|
private suspend fun refreshGradeDetails(
|
||||||
@ -101,13 +100,16 @@ class GradeRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
||||||
?: student.registrationDate.toLocalDate()
|
?: student.registrationDate.toLocalDate()
|
||||||
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
|
|
||||||
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
|
gradeDb.removeOldAndSaveNew(
|
||||||
if (it.date >= notifyBreakDate) it.apply {
|
oldItems = oldGrades uniqueSubtract newDetails,
|
||||||
isRead = false
|
newItems = (newDetails uniqueSubtract oldGrades).onEach {
|
||||||
if (notify) isNotified = false
|
if (it.date >= notifyBreakDate) it.apply {
|
||||||
}
|
isRead = false
|
||||||
})
|
if (notify) isNotified = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshGradeSummaries(
|
private suspend fun refreshGradeSummaries(
|
||||||
@ -115,31 +117,43 @@ class GradeRepository @Inject constructor(
|
|||||||
newSummary: List<GradeSummary>,
|
newSummary: List<GradeSummary>,
|
||||||
notify: Boolean
|
notify: Boolean
|
||||||
) {
|
) {
|
||||||
gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
|
gradeSummaryDb.removeOldAndSaveNew(
|
||||||
gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
|
oldItems = oldSummaries uniqueSubtract newSummary,
|
||||||
val oldSummary = oldSummaries.find { old -> old.subject == summary.subject }
|
newItems = (newSummary uniqueSubtract oldSummaries).onEach { summary ->
|
||||||
summary.isPredictedGradeNotified = when {
|
getGradeSummaryWithUpdatedNotificationState(
|
||||||
summary.predictedGrade.isEmpty() -> true
|
summary = summary,
|
||||||
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
|
oldSummary = oldSummaries.find { it.subject == summary.subject },
|
||||||
else -> true
|
notify = notify,
|
||||||
}
|
)
|
||||||
summary.isFinalGradeNotified = when {
|
},
|
||||||
summary.finalGrade.isEmpty() -> true
|
)
|
||||||
notify && oldSummary?.finalGrade != summary.finalGrade -> false
|
}
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
summary.predictedGradeLastChange = when {
|
private fun getGradeSummaryWithUpdatedNotificationState(
|
||||||
oldSummary == null -> Instant.now()
|
summary: GradeSummary,
|
||||||
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
oldSummary: GradeSummary?,
|
||||||
else -> oldSummary.predictedGradeLastChange
|
notify: Boolean,
|
||||||
}
|
) {
|
||||||
summary.finalGradeLastChange = when {
|
summary.isPredictedGradeNotified = when {
|
||||||
oldSummary == null -> Instant.now()
|
summary.predictedGrade.isEmpty() -> true
|
||||||
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
|
||||||
else -> oldSummary.finalGradeLastChange
|
else -> true
|
||||||
}
|
}
|
||||||
})
|
summary.isFinalGradeNotified = when {
|
||||||
|
summary.finalGrade.isEmpty() -> true
|
||||||
|
notify && oldSummary?.finalGrade != summary.finalGrade -> false
|
||||||
|
else -> true
|
||||||
|
}
|
||||||
|
summary.predictedGradeLastChange = when {
|
||||||
|
oldSummary == null -> Instant.now()
|
||||||
|
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
||||||
|
else -> oldSummary.predictedGradeLastChange
|
||||||
|
}
|
||||||
|
summary.finalGradeLastChange = when {
|
||||||
|
oldSummary == null -> Instant.now()
|
||||||
|
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
||||||
|
else -> oldSummary.finalGradeLastChange
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
|
fun getUnreadGrades(semester: Semester): Flow<List<Grade>> {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.GradePartialStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||||
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
||||||
@ -12,14 +13,11 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
|
|||||||
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.util.*
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -28,7 +26,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
|
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
|
||||||
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
|
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
|
||||||
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
|
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -56,14 +54,15 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getGradesPartialStatistics(semester.semesterId)
|
.getGradesPartialStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradePartialStatisticsDb.deleteAll(old uniqueSubtract new)
|
gradePartialStatisticsDb.removeOldAndSaveNew(
|
||||||
gradePartialStatisticsDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
@ -80,6 +79,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
listOf(summaryItem) + items
|
listOf(summaryItem) + items
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> items.filter { it.subject == subjectName }
|
else -> items.filter { it.subject == subjectName }
|
||||||
}.mapPartialToStatisticItems()
|
}.mapPartialToStatisticItems()
|
||||||
}
|
}
|
||||||
@ -101,14 +101,15 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getGradesSemesterStatistics(semester.semesterId)
|
.getGradesSemesterStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new)
|
gradeSemesterStatisticsDb.removeOldAndSaveNew(
|
||||||
gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
@ -138,6 +139,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
listOf(summaryItem) + itemsWithAverage
|
listOf(summaryItem) + itemsWithAverage
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> itemsWithAverage.filter { it.subject == subjectName }
|
else -> itemsWithAverage.filter { it.subject == subjectName }
|
||||||
}.mapSemesterToStatisticItems()
|
}.mapSemesterToStatisticItems()
|
||||||
}
|
}
|
||||||
@ -157,14 +159,15 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getGradesPointsStatistics(semester.semesterId)
|
.getGradesPointsStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
gradePointsStatisticsDb.deleteAll(old uniqueSubtract new)
|
gradePointsStatisticsDb.removeOldAndSaveNew(
|
||||||
gradePointsStatisticsDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
|
||||||
},
|
},
|
||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.HomeworkDao
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -22,7 +20,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class HomeworkRepository @Inject constructor(
|
class HomeworkRepository @Inject constructor(
|
||||||
private val homeworkDb: HomeworkDao,
|
private val homeworkDb: HomeworkDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -55,20 +53,19 @@ class HomeworkRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getHomework(start.monday, end.sunday)
|
.getHomework(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
val homeWorkToSave = (new uniqueSubtract old).onEach {
|
|
||||||
if (notify) it.isNotified = false
|
|
||||||
}
|
|
||||||
val filteredOld = old.filterNot { it.isAddedByUser }
|
val filteredOld = old.filterNot { it.isAddedByUser }
|
||||||
|
|
||||||
homeworkDb.deleteAll(filteredOld uniqueSubtract new)
|
homeworkDb.removeOldAndSaveNew(
|
||||||
homeworkDb.insertAll(homeWorkToSave)
|
oldItems = filteredOld uniqueSubtract new,
|
||||||
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
|
if (notify) it.isNotified = false
|
||||||
|
},
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.LuckyNumberDao
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
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.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -18,7 +17,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class LuckyNumberRepository @Inject constructor(
|
class LuckyNumberRepository @Inject constructor(
|
||||||
private val luckyNumberDb: LuckyNumberDao,
|
private val luckyNumberDb: LuckyNumberDao,
|
||||||
private val sdk: Sdk
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -33,17 +32,18 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student)
|
wulkanowySdkFactory.create(student)
|
||||||
|
.getLuckyNumber(student.schoolShortName)
|
||||||
|
?.mapToEntity(student)
|
||||||
},
|
},
|
||||||
saveFetchResult = { oldLuckyNumber, newLuckyNumber ->
|
saveFetchResult = { oldLuckyNumber, newLuckyNumber ->
|
||||||
newLuckyNumber ?: return@networkBoundResource
|
newLuckyNumber ?: return@networkBoundResource
|
||||||
|
|
||||||
if (newLuckyNumber != oldLuckyNumber) {
|
if (newLuckyNumber != oldLuckyNumber) {
|
||||||
val updatedLuckNumberList =
|
luckyNumberDb.removeOldAndSaveNew(
|
||||||
listOf(newLuckyNumber.apply { if (notify) isNotified = false })
|
oldItems = listOfNotNull(oldLuckyNumber),
|
||||||
|
newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
|
||||||
oldLuckyNumber?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) }
|
)
|
||||||
luckyNumberDb.insertAll(updatedLuckNumberList)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -4,17 +4,22 @@ import android.content.Context
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.Resource
|
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.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.MutedMessageSendersDao
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
|
||||||
|
import io.github.wulkanowy.data.db.entities.MutedMessageSender
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder
|
import io.github.wulkanowy.data.enums.MessageFolder
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||||
|
import io.github.wulkanowy.data.enums.MessageFolder.SENT
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
|
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
|
||||||
import io.github.wulkanowy.data.mappers.mapFromEntities
|
import io.github.wulkanowy.data.mappers.mapFromEntities
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
@ -22,16 +27,14 @@ import io.github.wulkanowy.data.networkBoundResource
|
|||||||
import io.github.wulkanowy.data.onResourceError
|
import io.github.wulkanowy.data.onResourceError
|
||||||
import io.github.wulkanowy.data.onResourceSuccess
|
import io.github.wulkanowy.data.onResourceSuccess
|
||||||
import io.github.wulkanowy.data.pojos.MessageDraft
|
import io.github.wulkanowy.data.pojos.MessageDraft
|
||||||
|
import io.github.wulkanowy.data.toFirstResult
|
||||||
import io.github.wulkanowy.data.waitForResult
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
|
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.sdk.pojo.Folder
|
import io.github.wulkanowy.sdk.pojo.Folder
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -42,8 +45,9 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class MessageRepository @Inject constructor(
|
class MessageRepository @Inject constructor(
|
||||||
private val messagesDb: MessagesDao,
|
private val messagesDb: MessagesDao,
|
||||||
|
private val mutedMessageSendersDao: MutedMessageSendersDao,
|
||||||
private val messageAttachmentDao: MessageAttachmentDao,
|
private val messageAttachmentDao: MessageAttachmentDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val sharedPrefProvider: SharedPrefProvider,
|
private val sharedPrefProvider: SharedPrefProvider,
|
||||||
@ -51,7 +55,6 @@ class MessageRepository @Inject constructor(
|
|||||||
private val mailboxDao: MailboxDao,
|
private val mailboxDao: MailboxDao,
|
||||||
private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
|
private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
private val messagesCacheKey = "message"
|
private val messagesCacheKey = "message"
|
||||||
@ -63,7 +66,7 @@ class MessageRepository @Inject constructor(
|
|||||||
folder: MessageFolder,
|
folder: MessageFolder,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
): Flow<Resource<List<Message>>> = networkBoundResource(
|
): Flow<Resource<List<MessageWithMutedAuthor>>> = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
isResultEmpty = { it.isEmpty() },
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
@ -74,21 +77,30 @@ class MessageRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = {
|
query = {
|
||||||
if (mailbox == null) {
|
if (mailbox == null) {
|
||||||
messagesDb.loadAll(folder.id, student.email)
|
messagesDb.loadMessagesWithMutedAuthor(folder.id, student.email)
|
||||||
} else messagesDb.loadAll(mailbox.globalKey, folder.id)
|
} else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getMessages(
|
wulkanowySdkFactory.create(student)
|
||||||
folder = Folder.valueOf(folder.name),
|
.getMessages(
|
||||||
mailboxKey = mailbox?.globalKey,
|
folder = Folder.valueOf(folder.name),
|
||||||
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
|
mailboxKey = mailbox?.globalKey,
|
||||||
|
)
|
||||||
|
.mapToEntities(
|
||||||
|
student = student,
|
||||||
|
mailbox = mailbox,
|
||||||
|
allMailboxes = mailboxDao.loadAll(student.email)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { oldWithAuthors, new ->
|
||||||
messagesDb.deleteAll(old uniqueSubtract new)
|
val old = oldWithAuthors.map { it.message }
|
||||||
messagesDb.insertAll((new uniqueSubtract old).onEach {
|
messagesDb.removeOldAndSaveNew(
|
||||||
it.isNotified = !notify
|
oldItems = old uniqueSubtract new,
|
||||||
})
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
|
val muted = isMuted(it.correspondents)
|
||||||
|
it.isNotified = !notify || muted
|
||||||
|
},
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(
|
refreshHelper.updateLastRefreshTimestamp(
|
||||||
getRefreshKey(messagesCacheKey, mailbox, folder)
|
getRefreshKey(messagesCacheKey, mailbox, folder)
|
||||||
)
|
)
|
||||||
@ -106,14 +118,13 @@ class MessageRepository @Inject constructor(
|
|||||||
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
|
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
|
||||||
(it.message.unread && markAsRead) || it.message.content.isBlank()
|
(it.message.unread && markAsRead) || it.message.content.isBlank()
|
||||||
},
|
},
|
||||||
query = {
|
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
||||||
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
|
|
||||||
},
|
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getMessageDetails(
|
wulkanowySdkFactory.create(student)
|
||||||
messageKey = it!!.message.messageGlobalKey,
|
.getMessageDetails(
|
||||||
markAsRead = message.unread && markAsRead,
|
messageKey = it!!.message.messageGlobalKey,
|
||||||
)
|
markAsRead = message.unread && markAsRead,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||||
@ -152,22 +163,36 @@ class MessageRepository @Inject constructor(
|
|||||||
subject: String,
|
subject: String,
|
||||||
content: String,
|
content: String,
|
||||||
recipients: List<Recipient>,
|
recipients: List<Recipient>,
|
||||||
mailboxId: String,
|
mailbox: Mailbox,
|
||||||
) {
|
) {
|
||||||
sdk.init(student).sendMessage(
|
wulkanowySdkFactory.create(student)
|
||||||
subject = subject,
|
.sendMessage(
|
||||||
content = content,
|
subject = subject,
|
||||||
recipients = recipients.mapFromEntities(),
|
content = content,
|
||||||
mailboxId = mailboxId,
|
recipients = recipients.mapFromEntities(),
|
||||||
)
|
mailboxId = mailbox.globalKey,
|
||||||
|
)
|
||||||
|
refreshFolders(student, mailbox, listOf(SENT))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
||||||
|
wulkanowySdkFactory.create(student)
|
||||||
|
.restoreMessages(messages = messages.map { it.messageGlobalKey })
|
||||||
|
|
||||||
|
refreshFolders(student, mailbox)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteMessage(student: Student, message: Message) {
|
||||||
|
deleteMessages(student, listOf(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||||
val firstMessage = messages.first()
|
val firstMessage = messages.first()
|
||||||
sdk.init(student).deleteMessages(
|
wulkanowySdkFactory.create(student)
|
||||||
messages = messages.map { it.messageGlobalKey },
|
.deleteMessages(
|
||||||
removeForever = firstMessage.folderId == TRASHED.id,
|
messages = messages.map { it.messageGlobalKey },
|
||||||
)
|
removeForever = firstMessage.folderId == TRASHED.id,
|
||||||
|
)
|
||||||
|
|
||||||
if (firstMessage.folderId != TRASHED.id) {
|
if (firstMessage.folderId != TRASHED.id) {
|
||||||
val deletedMessages = messages.map {
|
val deletedMessages = messages.map {
|
||||||
@ -181,18 +206,24 @@ class MessageRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
messagesDb.updateAll(deletedMessages)
|
messagesDb.updateAll(deletedMessages)
|
||||||
} else messagesDb.deleteAll(messages)
|
} else {
|
||||||
|
messagesDb.deleteAll(messages)
|
||||||
getMessages(
|
}
|
||||||
student = student,
|
|
||||||
mailbox = mailbox,
|
|
||||||
folder = TRASHED,
|
|
||||||
forceRefresh = true,
|
|
||||||
).first()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) {
|
private suspend fun refreshFolders(
|
||||||
deleteMessages(student, mailbox, listOf(message))
|
student: Student,
|
||||||
|
mailbox: Mailbox?,
|
||||||
|
folders: List<MessageFolder> = MessageFolder.entries
|
||||||
|
) {
|
||||||
|
folders.forEach {
|
||||||
|
getMessages(
|
||||||
|
student = student,
|
||||||
|
mailbox = mailbox,
|
||||||
|
folder = it,
|
||||||
|
forceRefresh = true,
|
||||||
|
).toFirstResult()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
||||||
@ -206,7 +237,9 @@ class MessageRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getMailboxes().mapToEntities(student)
|
wulkanowySdkFactory.create(student)
|
||||||
|
.getMailboxes()
|
||||||
|
.mapToEntities(student)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||||
@ -236,4 +269,18 @@ class MessageRepository @Inject constructor(
|
|||||||
context.getString(R.string.pref_key_message_draft),
|
context.getString(R.string.pref_key_message_draft),
|
||||||
value?.let { json.encodeToString(it) }
|
value?.let { json.encodeToString(it) }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private suspend fun isMuted(author: String): Boolean {
|
||||||
|
return mutedMessageSendersDao.checkMute(author)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun muteMessage(author: String) {
|
||||||
|
if (isMuted(author)) return
|
||||||
|
mutedMessageSendersDao.insertMute(MutedMessageSender(author))
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun unmuteMessage(author: String) {
|
||||||
|
if (!isMuted(author)) return
|
||||||
|
mutedMessageSendersDao.deleteMute(author)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.MobileDeviceDao
|
||||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
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.mappers.mapToMobileDeviceToken
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -21,7 +19,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class MobileDeviceRepository @Inject constructor(
|
class MobileDeviceRepository @Inject constructor(
|
||||||
private val mobileDb: MobileDeviceDao,
|
private val mobileDb: MobileDeviceDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -42,30 +40,28 @@ class MobileDeviceRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { mobileDb.loadAll(student.userLoginId) },
|
query = { mobileDb.loadAll(student.userLoginId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getRegisteredDevices()
|
.getRegisteredDevices()
|
||||||
.mapToEntities(student)
|
.mapToEntities(student)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
mobileDb.deleteAll(old uniqueSubtract new)
|
mobileDb.removeOldAndSaveNew(
|
||||||
mobileDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.unregisterDevice(device.deviceId)
|
.unregisterDevice(device.deviceId)
|
||||||
|
|
||||||
mobileDb.deleteAll(listOf(device))
|
mobileDb.deleteAll(listOf(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
||||||
return sdk.init(student)
|
return wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getToken()
|
.getToken()
|
||||||
.mapToMobileDeviceToken()
|
.mapToMobileDeviceToken()
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.NoteDao
|
||||||
import io.github.wulkanowy.data.db.entities.Note
|
import io.github.wulkanowy.data.db.entities.Note
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.toLocalDate
|
||||||
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -16,7 +19,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class NoteRepository @Inject constructor(
|
class NoteRepository @Inject constructor(
|
||||||
private val noteDb: NoteDao,
|
private val noteDb: NoteDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -40,20 +43,21 @@ class NoteRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { noteDb.loadAll(student.studentId) },
|
query = { noteDb.loadAll(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getNotes()
|
.getNotes()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
noteDb.deleteAll(old uniqueSubtract new)
|
val notesToAdd = (new uniqueSubtract old).onEach {
|
||||||
noteDb.insertAll((new uniqueSubtract old).onEach {
|
|
||||||
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
|
if (it.date >= student.registrationDate.toLocalDate()) it.apply {
|
||||||
isRead = false
|
isRead = false
|
||||||
if (notify) isNotified = false
|
if (notify) isNotified = false
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
noteDb.removeOldAndSaveNew(
|
||||||
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = notesToAdd,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.RecipientDao
|
||||||
import io.github.wulkanowy.data.db.entities.*
|
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||||
|
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||||
|
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.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -14,19 +17,22 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class RecipientRepository @Inject constructor(
|
class RecipientRepository @Inject constructor(
|
||||||
private val recipientDb: RecipientDao,
|
private val recipientDb: RecipientDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val cacheKey = "recipient"
|
private val cacheKey = "recipient"
|
||||||
|
|
||||||
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
|
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)
|
.mapToEntities(mailbox.globalKey)
|
||||||
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
||||||
|
|
||||||
recipientDb.deleteAll(old uniqueSubtract new)
|
recipientDb.removeOldAndSaveNew(
|
||||||
recipientDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
@ -54,7 +60,7 @@ class RecipientRepository @Inject constructor(
|
|||||||
): List<Recipient> {
|
): List<Recipient> {
|
||||||
mailbox ?: return emptyList()
|
mailbox ?: return emptyList()
|
||||||
|
|
||||||
return sdk.init(student)
|
return wulkanowySdkFactory.create(student)
|
||||||
.getMessageReplayDetails(message.messageGlobalKey)
|
.getMessageReplayDetails(message.messageGlobalKey)
|
||||||
.sender
|
.sender
|
||||||
.let(::listOf)
|
.let(::listOf)
|
||||||
|
@ -1,17 +1,23 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@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> {
|
suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair<String, String> =
|
||||||
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
wulkanowySdkFactory.create()
|
||||||
}
|
.getPasswordResetCaptchaCode(host, symbol)
|
||||||
|
|
||||||
suspend fun sendRecoverRequest(
|
suspend fun sendRecoverRequest(
|
||||||
url: String, symbol: String, email: String, reCaptchaResponse: String
|
url: String,
|
||||||
): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
symbol: String,
|
||||||
|
email: String,
|
||||||
|
reCaptchaResponse: String
|
||||||
|
): String = wulkanowySdkFactory.create()
|
||||||
|
.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.SchoolAnnouncementDao
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -18,7 +17,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SchoolAnnouncementRepository @Inject constructor(
|
class SchoolAnnouncementRepository @Inject constructor(
|
||||||
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
private val schoolAnnouncementDb: SchoolAnnouncementDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -41,17 +40,18 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||||||
schoolAnnouncementDb.loadAll(student.userLoginId)
|
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
val sdk = wulkanowySdkFactory.create(student)
|
||||||
.getDirectorInformation()
|
val lastAnnouncements = sdk.getLastAnnouncements().mapToEntities(student)
|
||||||
.mapToEntities(student)
|
val directorInformation = sdk.getDirectorInformation().mapToEntities(student)
|
||||||
|
lastAnnouncements + directorInformation
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
val schoolAnnouncementsToSave = (new uniqueSubtract old).onEach {
|
schoolAnnouncementDb.removeOldAndSaveNew(
|
||||||
if (notify) it.isNotified = false
|
oldItems = old uniqueSubtract new,
|
||||||
}
|
newItems = (new uniqueSubtract old).onEach {
|
||||||
|
if (notify) it.isNotified = false
|
||||||
schoolAnnouncementDb.deleteAll(old uniqueSubtract new)
|
},
|
||||||
schoolAnnouncementDb.insertAll(schoolAnnouncementsToSave)
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.SchoolDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -17,7 +15,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SchoolRepository @Inject constructor(
|
class SchoolRepository @Inject constructor(
|
||||||
private val schoolDb: SchoolDao,
|
private val schoolDb: SchoolDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -40,17 +38,16 @@ class SchoolRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getSchool()
|
.getSchool()
|
||||||
.mapToEntity(semester)
|
.mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
if (old != null && new != old) {
|
if (old != null && new != old) {
|
||||||
with(schoolDb) {
|
schoolDb.removeOldAndSaveNew(
|
||||||
deleteAll(listOf(old))
|
oldItems = listOf(old),
|
||||||
insertAll(listOf(new))
|
newItems = listOf(new)
|
||||||
}
|
)
|
||||||
} else if (old == null) {
|
} else if (old == null) {
|
||||||
schoolDb.insertAll(listOf(new))
|
schoolDb.insertAll(listOf(new))
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||||
import io.github.wulkanowy.data.api.SchoolsService
|
import io.github.wulkanowy.data.api.SchoolsService
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
||||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
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.ui.modules.login.LoginData
|
||||||
import io.github.wulkanowy.utils.IntegrityHelper
|
import io.github.wulkanowy.utils.IntegrityHelper
|
||||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@ -23,7 +21,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
class SchoolsRepository @Inject constructor(
|
class SchoolsRepository @Inject constructor(
|
||||||
private val integrityHelper: IntegrityHelper,
|
private val integrityHelper: IntegrityHelper,
|
||||||
private val schoolsService: SchoolsService,
|
private val schoolsService: SchoolsService,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun logSchoolLogin(loginData: LoginData, students: List<StudentWithSemesters>) {
|
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) {
|
private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) {
|
||||||
val requestId = UUID.randomUUID().toString()
|
val requestId = UUID.randomUUID().toString()
|
||||||
val token = integrityHelper.getIntegrityToken(requestId) ?: return
|
val token = integrityHelper.getIntegrityToken(requestId) ?: return
|
||||||
|
val updatedStudent = student.copy(password = loginData.password)
|
||||||
|
|
||||||
val schoolInfo = sdk
|
val schoolInfo = wulkanowySdkFactory.create(updatedStudent, semester)
|
||||||
.init(student.copy(password = loginData.password))
|
|
||||||
.switchSemester(semester)
|
|
||||||
.getSchool()
|
.getSchool()
|
||||||
|
|
||||||
schoolsService.logLoginEvent(
|
schoolsService.logLoginEvent(
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
|
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||||
|
import io.github.wulkanowy.utils.isCurrent
|
||||||
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -14,8 +18,8 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SemesterRepository @Inject constructor(
|
class SemesterRepository @Inject constructor(
|
||||||
private val semesterDb: SemesterDao,
|
private val semesterDb: SemesterDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val dispatchers: DispatchersProvider
|
private val dispatchers: DispatchersProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getSemesters(
|
suspend fun getSemesters(
|
||||||
@ -45,6 +49,7 @@ class SemesterRepository @Inject constructor(
|
|||||||
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||||
} == true
|
} == true
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,12 +60,20 @@ class SemesterRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshSemesters(student: Student) {
|
private suspend fun refreshSemesters(student: Student) {
|
||||||
val new = sdk.init(student).getSemesters().mapToEntities(student.studentId)
|
val new = wulkanowySdkFactory.create(student)
|
||||||
if (new.isEmpty()) return Timber.i("Empty semester list!")
|
.getSemesters()
|
||||||
|
.mapToEntities(student.studentId)
|
||||||
|
|
||||||
|
if (new.isEmpty()) {
|
||||||
|
Timber.i("Empty semester list from SDK!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val old = semesterDb.loadAll(student.studentId, student.classId)
|
val old = semesterDb.loadAll(student.studentId, student.classId)
|
||||||
semesterDb.deleteAll(old.uniqueSubtract(new))
|
semesterDb.removeOldAndSaveNew(
|
||||||
semesterDb.insertSemesters(new.uniqueSubtract(old))
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
|
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.StudentInfoDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
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 kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -15,7 +13,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class StudentInfoRepository @Inject constructor(
|
class StudentInfoRepository @Inject constructor(
|
||||||
private val studentInfoDao: StudentInfoDao,
|
private val studentInfoDao: StudentInfoDao,
|
||||||
private val sdk: Sdk
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
@ -30,16 +28,16 @@ class StudentInfoRepository @Inject constructor(
|
|||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
.getStudentInfo()
|
||||||
.getStudentInfo().mapToEntity(semester)
|
.mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
if (old != null && new != old) {
|
if (old != null && new != old) {
|
||||||
with(studentInfoDao) {
|
studentInfoDao.removeOldAndSaveNew(
|
||||||
deleteAll(listOf(old))
|
oldItems = listOf(old),
|
||||||
insertAll(listOf(new))
|
newItems = listOf(new),
|
||||||
}
|
)
|
||||||
} else if (old == null) {
|
} else if (old == null) {
|
||||||
studentInfoDao.insertAll(listOf(new))
|
studentInfoDao.insertAll(listOf(new))
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
|
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||||
import io.github.wulkanowy.data.db.AppDatabase
|
import io.github.wulkanowy.data.db.AppDatabase
|
||||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
||||||
import io.github.wulkanowy.data.db.entities.StudentName
|
import io.github.wulkanowy.data.db.entities.StudentName
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
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.mappers.mapToPojo
|
||||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.security.Scrambler
|
import io.github.wulkanowy.utils.security.Scrambler
|
||||||
import io.github.wulkanowy.utils.switchSemester
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ class StudentRepository @Inject constructor(
|
|||||||
private val dispatchers: DispatchersProvider,
|
private val dispatchers: DispatchersProvider,
|
||||||
private val studentDb: StudentDao,
|
private val studentDb: StudentDao,
|
||||||
private val semesterDb: SemesterDao,
|
private val semesterDb: SemesterDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val appDatabase: AppDatabase,
|
private val appDatabase: AppDatabase,
|
||||||
private val scrambler: Scrambler,
|
private val scrambler: Scrambler,
|
||||||
) {
|
) {
|
||||||
@ -37,9 +39,10 @@ class StudentRepository @Inject constructor(
|
|||||||
pin: String,
|
pin: String,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
token: String
|
token: String
|
||||||
): RegisterUser = sdk
|
): RegisterUser = wulkanowySdkFactory.create()
|
||||||
.getStudentsFromHebe(token, pin, symbol, "")
|
.getStudentsFromHebe(token, pin, symbol, "")
|
||||||
.mapToPojo(null)
|
.mapToPojo(null)
|
||||||
|
.also { it.logErrors() }
|
||||||
|
|
||||||
suspend fun getUserSubjectsFromScrapper(
|
suspend fun getUserSubjectsFromScrapper(
|
||||||
email: String,
|
email: String,
|
||||||
@ -47,18 +50,20 @@ class StudentRepository @Inject constructor(
|
|||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
domainSuffix: String,
|
domainSuffix: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): RegisterUser = sdk
|
): RegisterUser = wulkanowySdkFactory.create()
|
||||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
||||||
.mapToPojo(password)
|
.mapToPojo(password)
|
||||||
|
.also { it.logErrors() }
|
||||||
|
|
||||||
suspend fun getStudentsHybrid(
|
suspend fun getStudentsHybrid(
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): RegisterUser = sdk
|
): RegisterUser = wulkanowySdkFactory.create()
|
||||||
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||||
.mapToPojo(password)
|
.mapToPojo(password)
|
||||||
|
.also { it.logErrors() }
|
||||||
|
|
||||||
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
||||||
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
||||||
@ -100,6 +105,46 @@ class StudentRepository @Inject constructor(
|
|||||||
return student
|
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 {
|
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
@ -149,20 +194,24 @@ class StudentRepository @Inject constructor(
|
|||||||
.distinctBy { it.student.studentName }.size == 1
|
.distinctBy { it.student.studentName }.size == 1
|
||||||
|
|
||||||
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) =
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.authorizePermission(pesel)
|
.authorizePermission(pesel)
|
||||||
|
|
||||||
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
|
||||||
val newCurrentApiStudent = sdk.init(student)
|
val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
|
||||||
.getCurrentStudent() ?: return
|
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
|
||||||
|
.getOrNull() ?: return
|
||||||
|
|
||||||
val studentName = StudentName(
|
val studentName = StudentName(
|
||||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||||
).apply { id = student.id }
|
).apply { id = student.id }
|
||||||
|
|
||||||
studentDb.update(studentName)
|
studentDb.update(studentName)
|
||||||
|
semesterDb.removeOldAndSaveNew(
|
||||||
|
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
|
||||||
|
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
||||||
@ -175,4 +224,18 @@ class StudentRepository @Inject constructor(
|
|||||||
appDatabase.clearAllTables()
|
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
|
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.dao.SubjectDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,7 +16,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class SubjectRepository @Inject constructor(
|
class SubjectRepository @Inject constructor(
|
||||||
private val subjectDao: SubjectDao,
|
private val subjectDao: SubjectDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -39,15 +37,15 @@ class SubjectRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getSubjects()
|
.getSubjects()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
subjectDao.deleteAll(old uniqueSubtract new)
|
subjectDao.removeOldAndSaveNew(
|
||||||
subjectDao.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.dao.TeacherDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
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 io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,7 +16,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class TeacherRepository @Inject constructor(
|
class TeacherRepository @Inject constructor(
|
||||||
private val teacherDb: TeacherDao,
|
private val teacherDb: TeacherDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -39,15 +37,15 @@ class TeacherRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getTeachers()
|
.getTeachers()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
teacherDb.deleteAll(old uniqueSubtract new)
|
teacherDb.removeOldAndSaveNew(
|
||||||
teacherDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
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.TimetableAdditionalDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||||
import io.github.wulkanowy.data.db.entities.*
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
|
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||||
|
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.networkBoundResource
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
|
import io.github.wulkanowy.utils.monday
|
||||||
|
import io.github.wulkanowy.utils.sunday
|
||||||
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -23,7 +31,7 @@ class TimetableRepository @Inject constructor(
|
|||||||
private val timetableDb: TimetableDao,
|
private val timetableDb: TimetableDao,
|
||||||
private val timetableAdditionalDb: TimetableAdditionalDao,
|
private val timetableAdditionalDb: TimetableAdditionalDao,
|
||||||
private val timetableHeaderDb: TimetableHeaderDao,
|
private val timetableHeaderDb: TimetableHeaderDao,
|
||||||
private val sdk: Sdk,
|
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
@ -64,8 +72,7 @@ class TimetableRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
||||||
fetch = {
|
fetch = {
|
||||||
val timetableFull = sdk.init(student)
|
val timetableFull = wulkanowySdkFactory.create(student, semester)
|
||||||
.switchSemester(semester)
|
|
||||||
.getTimetable(start.monday, end.sunday)
|
.getTimetable(start.monday, end.sunday)
|
||||||
|
|
||||||
timetableFull.mapToEntities(semester)
|
timetableFull.mapToEntities(semester)
|
||||||
@ -121,12 +128,12 @@ class TimetableRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTimetableFromDatabase(
|
suspend fun getTimetableFromDatabase(
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
from: LocalDate,
|
start: LocalDate,
|
||||||
end: LocalDate
|
end: LocalDate
|
||||||
): Flow<List<Timetable>> {
|
): List<Timetable> {
|
||||||
return timetableDb.loadAll(semester.diaryId, semester.studentId, from, end)
|
return timetableDb.load(semester.diaryId, semester.studentId, start, end)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateTimetable(timetable: List<Timetable>) {
|
suspend fun updateTimetable(timetable: List<Timetable>) {
|
||||||
@ -144,8 +151,10 @@ class TimetableRepository @Inject constructor(
|
|||||||
new.apply { if (notify) isNotified = false }
|
new.apply { if (notify) isNotified = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
timetableDb.deleteAll(lessonsToRemove)
|
timetableDb.removeOldAndSaveNew(
|
||||||
timetableDb.insertAll(lessonsToAdd)
|
oldItems = lessonsToRemove,
|
||||||
|
newItems = lessonsToAdd,
|
||||||
|
)
|
||||||
|
|
||||||
schedulerHelper.cancelScheduled(lessonsToRemove, student)
|
schedulerHelper.cancelScheduled(lessonsToRemove, student)
|
||||||
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
|
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
|
||||||
@ -156,13 +165,17 @@ class TimetableRepository @Inject constructor(
|
|||||||
new: List<TimetableAdditional>
|
new: List<TimetableAdditional>
|
||||||
) {
|
) {
|
||||||
val oldFiltered = old.filter { !it.isAddedByUser }
|
val oldFiltered = old.filter { !it.isAddedByUser }
|
||||||
timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
|
timetableAdditionalDb.removeOldAndSaveNew(
|
||||||
timetableAdditionalDb.insertAll(new uniqueSubtract old)
|
oldItems = oldFiltered uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
|
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
|
||||||
timetableHeaderDb.deleteAll(old uniqueSubtract new)
|
timetableHeaderDb.removeOldAndSaveNew(
|
||||||
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
oldItems = old uniqueSubtract new,
|
||||||
|
newItems = new uniqueSubtract old,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package io.github.wulkanowy.domain.timetable
|
package io.github.wulkanowy.domain.timetable
|
||||||
|
|
||||||
import io.github.wulkanowy.data.dataOrNull
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||||
import io.github.wulkanowy.data.toFirstResult
|
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.monday
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.sunday
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -16,18 +13,14 @@ class IsStudentHasLessonsOnWeekendUseCase @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
student: Student,
|
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
currentDate: LocalDate = LocalDate.now(),
|
currentDate: LocalDate = LocalDate.now(),
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val lessons = timetableRepository.getTimetable(
|
val lessons = timetableRepository.getTimetableFromDatabase(
|
||||||
student = student,
|
|
||||||
semester = semester,
|
semester = semester,
|
||||||
start = currentDate.monday,
|
start = currentDate.monday,
|
||||||
end = currentDate.sunday,
|
end = currentDate.sunday,
|
||||||
forceRefresh = false,
|
)
|
||||||
timetableType = TimetableRepository.TimetableType.NORMAL
|
|
||||||
).toFirstResult().dataOrNull?.lessons.orEmpty()
|
|
||||||
return isWeekendHasLessonsUseCase(lessons)
|
return isWeekendHasLessonsUseCase(lessons)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
|
|||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
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.channels.DebugChannel
|
||||||
import io.github.wulkanowy.services.sync.works.Work
|
import io.github.wulkanowy.services.sync.works.Work
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
@ -48,6 +49,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||||
student to semester
|
student to semester
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e)
|
||||||
return@withContext getResultFromErrors(listOf(e))
|
return@withContext getResultFromErrors(listOf(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
null
|
null
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException) {
|
if (e is FeatureDisabledException || e is FeatureNotAvailableException || e is FeatureUnavailableException) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
|
@ -6,7 +6,6 @@ import io.github.wulkanowy.data.repositories.TimetableRepository
|
|||||||
import io.github.wulkanowy.data.waitForResult
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
|
import io.github.wulkanowy.services.sync.notifications.ChangeTimetableNotification
|
||||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.now
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -31,10 +30,9 @@ class TimetableWork @Inject constructor(
|
|||||||
|
|
||||||
timetableRepository.getTimetableFromDatabase(
|
timetableRepository.getTimetableFromDatabase(
|
||||||
semester = semester,
|
semester = semester,
|
||||||
from = startDate,
|
start = startDate,
|
||||||
end = endDate,
|
end = endDate,
|
||||||
)
|
)
|
||||||
.first()
|
|
||||||
.filterNot { it.isNotified }
|
.filterNot { it.isNotified }
|
||||||
.let {
|
.let {
|
||||||
if (it.isNotEmpty()) changeTimetableNotification.notify(it, student)
|
if (it.isNotEmpty()) changeTimetableNotification.notify(it, student)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
import android.app.ActivityManager
|
import android.app.ActivityManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -17,6 +18,8 @@ import io.github.wulkanowy.utils.FragmentLifecycleLogger
|
|||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
import io.github.wulkanowy.utils.openInternetBrowser
|
import io.github.wulkanowy.utils.openInternetBrowser
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
||||||
@ -36,16 +39,26 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
|
|
||||||
abstract var presenter: T
|
abstract var presenter: T
|
||||||
|
|
||||||
|
private var lastDialogOpenTime = mutableMapOf<String, Instant>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
inject()
|
inject()
|
||||||
themeManager.applyActivityTheme(this)
|
themeManager.applyActivityTheme(this)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true)
|
||||||
|
applyCustomTaskDescription()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
setTaskDescription(
|
private fun applyCustomTaskDescription() {
|
||||||
ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface))
|
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) {
|
override fun showError(text: String, error: Throwable) {
|
||||||
@ -70,6 +83,8 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showExpiredCredentialsDialog() {
|
override fun showExpiredCredentialsDialog() {
|
||||||
|
if (!shouldShowDialog(DIALOG_ERROR_BAD_CREDENTIALS)) return
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_expired_credentials_title)
|
.setTitle(R.string.main_expired_credentials_title)
|
||||||
.setMessage(R.string.main_expired_credentials_description)
|
.setMessage(R.string.main_expired_credentials_description)
|
||||||
@ -83,6 +98,8 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showDecryptionFailedDialog() {
|
override fun showDecryptionFailedDialog() {
|
||||||
|
if (!shouldShowDialog(DIALOG_ERROR_DECRYPTION_FAILED)) return
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_session_expired)
|
.setTitle(R.string.main_session_expired)
|
||||||
.setMessage(R.string.main_session_relogin)
|
.setMessage(R.string.main_session_relogin)
|
||||||
@ -119,4 +136,21 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
protected open fun inject() {
|
protected open fun inject() {
|
||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun shouldShowDialog(name: String): Boolean {
|
||||||
|
val lastOpenTime = lastDialogOpenTime[name]
|
||||||
|
val now = Instant.now()
|
||||||
|
|
||||||
|
if (lastOpenTime != null && now.isBefore(lastOpenTime.plusSeconds(1))) {
|
||||||
|
Timber.i("Dialog $name was shown less than a second ago. Skip")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
lastDialogOpenTime[name] = Instant.now()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val DIALOG_ERROR_BAD_CREDENTIALS = "dialog_error_bad_credentials"
|
||||||
|
private const val DIALOG_ERROR_DECRYPTION_FAILED = "dialog_error_decryption_failed"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.base
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
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.exception.CloudflareVerificationException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||||
@ -34,17 +34,21 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected open fun proceed(error: Throwable) {
|
protected open fun proceed(error: Throwable) {
|
||||||
showErrorMessage(context.resources.getErrorString(error), error)
|
showDefaultMessage(error)
|
||||||
when (error) {
|
when (error) {
|
||||||
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
|
is PasswordChangeRequiredException -> onPasswordChangeRequired(error.redirectUrl)
|
||||||
is ScramblerException -> onDecryptionFailed()
|
is ScramblerException -> onDecryptionFailed()
|
||||||
is BadCredentialsException -> onExpiredCredentials()
|
is BadCredentialsException -> onExpiredCredentials()
|
||||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||||
is AuthorizationRequiredException -> onAuthorizationRequired()
|
is NoAuthorizationException -> onAuthorizationRequired()
|
||||||
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showDefaultMessage(error: Throwable) {
|
||||||
|
showErrorMessage(context.resources.getErrorString(error), error)
|
||||||
|
}
|
||||||
|
|
||||||
open fun clear() {
|
open fun clear() {
|
||||||
showErrorMessage = { _, _ -> }
|
showErrorMessage = { _, _ -> }
|
||||||
onExpiredCredentials = {}
|
onExpiredCredentials = {}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.attendance
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -33,17 +34,17 @@ class AttendanceAdapter @Inject constructor() :
|
|||||||
)
|
)
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||||
|
val context = holder.binding.root.context
|
||||||
val item = items[position]
|
val item = items[position]
|
||||||
|
|
||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
attendanceItemNumber.text = item.number.toString()
|
attendanceItemNumber.text = item.number.toString()
|
||||||
attendanceItemSubject.text = item.subject.ifBlank {
|
attendanceItemSubject.text = item.subject
|
||||||
root.context.getString(R.string.all_no_data)
|
.ifBlank { context.getString(R.string.all_no_data) }
|
||||||
}
|
|
||||||
attendanceItemDescription.setText(item.descriptionRes)
|
attendanceItemDescription.setText(item.descriptionRes)
|
||||||
|
|
||||||
attendanceItemDescription.setTextColor(
|
attendanceItemDescription.setTextColor(
|
||||||
root.context.getThemeAttrColor(
|
context.getThemeAttrColor(
|
||||||
when {
|
when {
|
||||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||||
@ -61,13 +62,15 @@ class AttendanceAdapter @Inject constructor() :
|
|||||||
attendanceItemAlert.isVisible =
|
attendanceItemAlert.isVisible =
|
||||||
item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
|
item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) }
|
||||||
|
|
||||||
attendanceItemAlert.setColorFilter(root.context.getThemeAttrColor(
|
attendanceItemAlert.imageTintList = ColorStateList.valueOf(
|
||||||
when{
|
context.getThemeAttrColor(
|
||||||
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
when {
|
||||||
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
item.absence && !item.excused -> R.attr.colorAttendanceAbsence
|
||||||
else -> android.R.attr.colorPrimary
|
item.lateness && !item.excused -> R.attr.colorAttendanceLateness
|
||||||
}
|
else -> android.R.attr.colorPrimary
|
||||||
))
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
attendanceItemNumber.visibility = View.GONE
|
attendanceItemNumber.visibility = View.GONE
|
||||||
attendanceItemExcuseInfo.visibility = View.GONE
|
attendanceItemExcuseInfo.visibility = View.GONE
|
||||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||||
|
@ -4,18 +4,14 @@ import android.annotation.SuppressLint
|
|||||||
import io.github.wulkanowy.data.*
|
import io.github.wulkanowy.data.*
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
|
||||||
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.*
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.firstOrNull
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.time.DayOfWeek
|
import java.time.DayOfWeek
|
||||||
@ -210,7 +206,7 @@ class AttendancePresenter @Inject constructor(
|
|||||||
|
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
|
||||||
checkInitialAndCurrentDate(student, semester)
|
checkInitialAndCurrentDate(semester)
|
||||||
attendanceRepository.getAttendance(
|
attendanceRepository.getAttendance(
|
||||||
student = student,
|
student = student,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
@ -266,15 +262,13 @@ class AttendancePresenter @Inject constructor(
|
|||||||
.launch()
|
.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
|
private suspend fun checkInitialAndCurrentDate(semester: Semester) {
|
||||||
if (initialDate == null) {
|
if (initialDate == null) {
|
||||||
val lessons = attendanceRepository.getAttendance(
|
val lessons = attendanceRepository.getAttendanceFromDatabase(
|
||||||
student = student,
|
|
||||||
semester = semester,
|
semester = semester,
|
||||||
start = now().monday,
|
start = now().monday,
|
||||||
end = now().sunday,
|
end = now().sunday,
|
||||||
forceRefresh = false,
|
).firstOrNull().orEmpty()
|
||||||
).toFirstResult().dataOrNull.orEmpty()
|
|
||||||
isWeekendHasLessons = isWeekendHasLessons(lessons)
|
isWeekendHasLessons = isWeekendHasLessons(lessons)
|
||||||
initialDate = getInitialDate(semester)
|
initialDate = getInitialDate(semester)
|
||||||
}
|
}
|
||||||
@ -316,6 +310,7 @@ class AttendancePresenter @Inject constructor(
|
|||||||
showContent(false)
|
showContent(false)
|
||||||
showExcuseButton(false)
|
showExcuseButton(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
Timber.i("Excusing for absence result: Success")
|
Timber.i("Excusing for absence result: Success")
|
||||||
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
|
analytics.logEvent("excuse_absence", "items" to attendanceToExcuseList.size)
|
||||||
@ -328,6 +323,7 @@ class AttendancePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
loadData(forceRefresh = true)
|
loadData(forceRefresh = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
Timber.i("Excusing for absence result: An exception occurred")
|
Timber.i("Excusing for absence result: An exception occurred")
|
||||||
errorHandler.dispatch(it.error)
|
errorHandler.dispatch(it.error)
|
||||||
|
@ -78,4 +78,9 @@ class AuthDialog : BaseDialogFragment<DialogAuthBinding>(), AuthView {
|
|||||||
override fun showDescriptionWithName(name: String) {
|
override fun showDescriptionWithName(name: String) {
|
||||||
binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml()
|
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.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AuthPresenter @Inject constructor(
|
class AuthPresenter @Inject constructor(
|
||||||
@ -57,13 +58,19 @@ class AuthPresenter @Inject constructor(
|
|||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
|
||||||
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
||||||
|
Timber.d("Auth succeed: $isSuccess")
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
studentRepository.refreshStudentName(student, semester)
|
studentRepository.refreshStudentAfterAuthorize(student, semester)
|
||||||
}
|
}
|
||||||
isSuccess
|
isSuccess
|
||||||
}
|
}
|
||||||
.onFailure { errorHandler.dispatch(it) }
|
.onFailure {
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
view?.showProgress(false)
|
||||||
|
view?.showContent(true)
|
||||||
|
}
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
|
Timber.d("Auth fully succeed: $it")
|
||||||
if (it) {
|
if (it) {
|
||||||
view?.showSuccess(true)
|
view?.showSuccess(true)
|
||||||
view?.showContent(false)
|
view?.showContent(false)
|
||||||
|
@ -10,9 +10,10 @@ import android.webkit.WebViewClient
|
|||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
|
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||||
import io.github.wulkanowy.databinding.DialogCaptchaBinding
|
import io.github.wulkanowy.databinding.DialogCaptchaBinding
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
|
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -20,7 +21,10 @@ import javax.inject.Inject
|
|||||||
class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var sdk: Sdk
|
lateinit var wulkanowySdkFactory: WulkanowySdkFactory
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var webkitCookieManagerProxy: WebkitCookieManagerProxy
|
||||||
|
|
||||||
private var webView: WebView? = null
|
private var webView: WebView? = null
|
||||||
|
|
||||||
@ -55,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
|||||||
webView = this
|
webView = this
|
||||||
with(settings) {
|
with(settings) {
|
||||||
javaScriptEnabled = true
|
javaScriptEnabled = true
|
||||||
userAgentString = sdk.userAgent
|
userAgentString = wulkanowySdkFactory.create().userAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
webViewClient = object : WebViewClient() {
|
webViewClient = object : WebViewClient() {
|
||||||
@ -80,6 +84,7 @@ class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
|
webkitCookieManagerProxy.webkitCookieManager?.flush()
|
||||||
webView?.destroy()
|
webView?.destroy()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
@ -304,6 +304,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
forceRefresh = forceRefresh
|
forceRefresh = forceRefresh
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.mapResourceData { it.map { messageWithAuthor -> messageWithAuthor.message } }
|
||||||
.onResourceError { errorHandler.dispatch(it) }
|
.onResourceError { errorHandler.dispatch(it) }
|
||||||
.takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
.takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
||||||
|
|
||||||
@ -438,7 +439,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
private fun loadLessons(student: Student, forceRefresh: Boolean) {
|
private fun loadLessons(student: Student, forceRefresh: Boolean) {
|
||||||
flatResourceFlow {
|
flatResourceFlow {
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
val date = when (isStudentHasLessonsOnWeekendUseCase(student, semester)) {
|
val date = when (isStudentHasLessonsOnWeekendUseCase(semester)) {
|
||||||
true -> LocalDate.now()
|
true -> LocalDate.now()
|
||||||
else -> LocalDate.now().nextOrSameSchoolDay
|
else -> LocalDate.now().nextOrSameSchoolDay
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
?.updateModifiers(student, config).orEmpty()
|
?.updateModifiers(student, config).orEmpty()
|
||||||
|
|
||||||
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
||||||
config.isOptionalArithmeticAverage
|
isOptionalArithmeticAverage = config.isOptionalArithmeticAverage,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
secondSemesterSubject.average
|
secondSemesterSubject.average
|
||||||
@ -173,13 +173,21 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
config: AverageCalcParams,
|
config: AverageCalcParams,
|
||||||
): Double {
|
): Double {
|
||||||
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
val isSecondSemesterHasWeightGrade = secondSemesterSubject.grades
|
||||||
|
.any { it.weightValue > .0 }
|
||||||
|
val isSecondSemesterHasArithmeticGrade = secondSemesterSubject.grades
|
||||||
|
.all { it.weightValue == .0 } && config.isOptionalArithmeticAverage
|
||||||
|
val isSecondSemesterHaveAverage =
|
||||||
|
isSecondSemesterHasWeightGrade || isSecondSemesterHasArithmeticGrade
|
||||||
|
|
||||||
|
val divider = if (isSecondSemesterHaveAverage) 2 else 1
|
||||||
val secondSemesterAverage = secondSemesterSubject.grades
|
val secondSemesterAverage = secondSemesterSubject.grades
|
||||||
.updateModifiers(student, config)
|
.updateModifiers(student, config)
|
||||||
.calcAverage(config.isOptionalArithmeticAverage)
|
.calcAverage(isOptionalArithmeticAverage = config.isOptionalArithmeticAverage)
|
||||||
val firstSemesterAverage = firstSemesterSubject?.grades
|
val firstSemesterAverage = firstSemesterSubject?.grades
|
||||||
?.updateModifiers(student, config)
|
?.updateModifiers(student, config)
|
||||||
?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
|
?.calcAverage(isOptionalArithmeticAverage = config.isOptionalArithmeticAverage)
|
||||||
|
?: secondSemesterAverage
|
||||||
|
|
||||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||||
} else {
|
} else {
|
||||||
@ -225,7 +233,7 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
subject = summary.subject,
|
subject = summary.subject,
|
||||||
average = if (!isAnyAverage || params.forceAverageCalc) {
|
average = if (!isAnyAverage || params.forceAverageCalc) {
|
||||||
grades.updateModifiers(student, params)
|
grades.updateModifiers(student, params)
|
||||||
.calcAverage(params.isOptionalArithmeticAverage)
|
.calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
|
||||||
} else summary.average,
|
} else summary.average,
|
||||||
points = summary.pointsSum,
|
points = summary.pointsSum,
|
||||||
summary = summary,
|
summary = summary,
|
||||||
@ -286,8 +294,13 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
proposedPoints = "",
|
proposedPoints = "",
|
||||||
finalPoints = "",
|
finalPoints = "",
|
||||||
pointsSum = "",
|
pointsSum = "",
|
||||||
average = if (calcAverage) details.updateModifiers(student, params)
|
average = when {
|
||||||
.calcAverage(params.isOptionalArithmeticAverage) else .0
|
calcAverage -> details
|
||||||
|
.updateModifiers(student, params)
|
||||||
|
.calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
|
||||||
|
|
||||||
|
else -> .0
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@ -31,14 +30,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: GradePresenter
|
lateinit var presenter: GradePresenter
|
||||||
|
|
||||||
private val pagerAdapter by lazy {
|
|
||||||
BaseFragmentPagerAdapter(
|
|
||||||
fragmentManager = childFragmentManager,
|
|
||||||
pagesCount = 3,
|
|
||||||
lifecycle = lifecycle,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var semesterSwitchMenu: MenuItem? = null
|
private var semesterSwitchMenu: MenuItem? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -52,6 +43,8 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
|
|
||||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||||
|
|
||||||
|
private var pagerAdapter: BaseFragmentPagerAdapter? = null
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -71,13 +64,26 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
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) {
|
with(binding.gradeViewPager) {
|
||||||
adapter = pagerAdapter
|
adapter = pagerAdapter
|
||||||
offscreenPageLimit = 3
|
offscreenPageLimit = 3
|
||||||
setOnSelectPageListener(presenter::onPageSelected)
|
setOnSelectPageListener(presenter::onPageSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(pagerAdapter) {
|
with(pagerAdapter!!) {
|
||||||
containerId = binding.gradeViewPager.id
|
containerId = binding.gradeViewPager.id
|
||||||
titleFactory = {
|
titleFactory = {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -99,11 +105,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
||||||
|
|
||||||
with(binding) {
|
|
||||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
|
||||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
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) {
|
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||||
?.onParentLoadData(semesterId, forceRefresh)
|
?.onParentLoadData(semesterId, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChildParentReselected(index: Int) {
|
override fun notifyChildParentReselected(index: Int) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChildSemesterChange(index: Int) {
|
override fun notifyChildSemesterChange(index: Int) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
pagerAdapter = null
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,8 @@ class GradePresenter @Inject constructor(
|
|||||||
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
private var selectedIndex = 0
|
private var selectedIndex = 0
|
||||||
|
|
||||||
private var schoolYear = 0
|
private var schoolYear = 0
|
||||||
|
private var availableSemesters = emptyList<Semester>()
|
||||||
private var semesters = emptyList<Semester>()
|
|
||||||
|
|
||||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
@ -40,7 +37,7 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onCreateMenu() {
|
fun onCreateMenu() {
|
||||||
if (semesters.isEmpty()) view?.showSemesterSwitch(false)
|
if (availableSemesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onViewReselected() {
|
fun onViewReselected() {
|
||||||
@ -49,8 +46,8 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onSemesterSwitch(): Boolean {
|
fun onSemesterSwitch(): Boolean {
|
||||||
if (semesters.isNotEmpty()) {
|
if (availableSemesters.isNotEmpty()) {
|
||||||
view?.showSemesterDialog(selectedIndex - 1, semesters.take(2))
|
view?.showSemesterDialog(selectedIndex - 1, availableSemesters.take(2))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -83,7 +80,7 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onPageSelected(index: Int) {
|
fun onPageSelected(index: Int) {
|
||||||
if (semesters.isNotEmpty()) loadChild(index)
|
if (availableSemesters.isNotEmpty()) loadChild(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRetry() {
|
fun onRetry() {
|
||||||
@ -101,16 +98,24 @@ class GradePresenter @Inject constructor(
|
|||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
resourceFlow {
|
resourceFlow {
|
||||||
val student = studentRepository.getCurrentStudent()
|
val student = studentRepository.getCurrentStudent()
|
||||||
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
val semesters = semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||||
|
|
||||||
|
student to semesters
|
||||||
}
|
}
|
||||||
.logResourceStatus("load grade data")
|
.logResourceStatus("load grade data")
|
||||||
.onResourceData {
|
.onResourceData { (student, semesters) ->
|
||||||
val current = it.getCurrentOrLast()
|
val currentSemester = semesters.getCurrentOrLast()
|
||||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
selectedIndex =
|
||||||
schoolYear = current.schoolYear
|
if (selectedIndex == 0) currentSemester.semesterName else selectedIndex
|
||||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
schoolYear = currentSemester.schoolYear
|
||||||
view?.setCurrentSemesterName(current.semesterName, schoolYear)
|
availableSemesters = semesters.filter { semester ->
|
||||||
|
semester.diaryId == currentSemester.diaryId
|
||||||
|
}
|
||||||
|
|
||||||
view?.run {
|
view?.run {
|
||||||
|
initTabs(if (student.isEduOne == true) 2 else 3)
|
||||||
|
setCurrentSemesterName(currentSemester.semesterName, schoolYear)
|
||||||
|
|
||||||
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
||||||
loadChild(currentPageIndex)
|
loadChild(currentPageIndex)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
@ -131,10 +136,10 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
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 {
|
val newSelectedSemesterId = try {
|
||||||
semesters.first { it.semesterName == selectedIndex }.semesterId
|
availableSemesters.first { it.semesterName == selectedIndex }.semesterId
|
||||||
} catch (e: NoSuchElementException) {
|
} catch (e: NoSuchElementException) {
|
||||||
Timber.e(e, "Selected semester no exists")
|
Timber.e(e, "Selected semester no exists")
|
||||||
return
|
return
|
||||||
|
@ -9,6 +9,8 @@ interface GradeView : BaseView {
|
|||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
|
fun initTabs(pageCount: Int)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
@ -98,7 +98,9 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
|
|||||||
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
||||||
onDateSelected = {
|
onDateSelected = {
|
||||||
date = it
|
date = it
|
||||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
if (isAdded) {
|
||||||
|
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,7 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||||||
loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() }
|
loginFormUsername.doOnTextChanged { _, _, _, _ -> presenter.onUsernameTextChanged() }
|
||||||
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
loginFormPass.doOnTextChanged { _, _, _, _ -> presenter.onPassTextChanged() }
|
||||||
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
loginFormHost.setOnItemClickListener { _, _, _, _ -> presenter.onHostSelected() }
|
||||||
|
loginFormDomainSuffix.doOnTextChanged { _, _, _, _ -> presenter.onDomainSuffixChanged() }
|
||||||
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
loginFormSignIn.setOnClickListener { presenter.onSignInClick() }
|
||||||
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
|
loginFormAdvancedButton.setOnClickListener { presenter.onAdvancedLoginClick() }
|
||||||
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
loginFormPrivacyLink.setOnClickListener { presenter.onPrivacyLinkClick() }
|
||||||
@ -188,6 +189,12 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setDomainSuffixInvalid() {
|
||||||
|
with(binding.loginFormDomainSuffixLayout) {
|
||||||
|
error = getString(R.string.login_invalid_domain_suffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun clearUsernameError() {
|
override fun clearUsernameError() {
|
||||||
binding.loginFormUsernameLayout.error = null
|
binding.loginFormUsernameLayout.error = null
|
||||||
binding.loginFormErrorBox.isVisible = false
|
binding.loginFormErrorBox.isVisible = false
|
||||||
@ -206,6 +213,10 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||||||
binding.loginFormErrorBox.isVisible = false
|
binding.loginFormErrorBox.isVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clearDomainSuffixError() {
|
||||||
|
binding.loginFormDomainSuffixLayout.error = null
|
||||||
|
}
|
||||||
|
|
||||||
override fun showSoftKeyboard() {
|
override fun showSoftKeyboard() {
|
||||||
activity?.showSoftInput()
|
activity?.showSoftInput()
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.data.resourceFlow
|
import io.github.wulkanowy.data.resourceFlow
|
||||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
|
||||||
@ -101,6 +102,12 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onDomainSuffixChanged() {
|
||||||
|
view?.apply {
|
||||||
|
clearDomainSuffixError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun updateCustomDomainSuffixVisibility() {
|
fun updateCustomDomainSuffixVisibility() {
|
||||||
view?.run {
|
view?.run {
|
||||||
showDomainSuffixInput("customSuffix" in formHostValue)
|
showDomainSuffixInput("customSuffix" in formHostValue)
|
||||||
@ -159,7 +166,7 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
fun onSignInClick() {
|
fun onSignInClick() {
|
||||||
val loginData = getLoginData()
|
val loginData = getLoginData()
|
||||||
|
|
||||||
if (!validateCredentials(loginData.login, loginData.password, loginData.baseUrl)) return
|
if (!validateCredentials(loginData)) return
|
||||||
|
|
||||||
resourceFlow {
|
resourceFlow {
|
||||||
studentRepository.getUserSubjectsFromScrapper(
|
studentRepository.getUserSubjectsFromScrapper(
|
||||||
@ -198,6 +205,9 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
.onResourceError {
|
.onResourceError {
|
||||||
loginErrorHandler.dispatch(it)
|
loginErrorHandler.dispatch(it)
|
||||||
|
if (it is InvalidSymbolException) {
|
||||||
|
loginErrorHandler.showDefaultMessage(it)
|
||||||
|
}
|
||||||
lastError = it
|
lastError = it
|
||||||
view?.showContact(true)
|
view?.showContact(true)
|
||||||
analytics.logEvent(
|
analytics.logEvent(
|
||||||
@ -229,24 +239,29 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
view?.onRecoverClick()
|
view?.onRecoverClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateCredentials(login: String, password: String, host: String): Boolean {
|
private fun validateCredentials(loginData: LoginData): Boolean {
|
||||||
var isCorrect = true
|
var isCorrect = true
|
||||||
|
|
||||||
if (login.isEmpty()) {
|
if (loginData.login.isEmpty()) {
|
||||||
view?.setErrorUsernameRequired()
|
view?.setErrorUsernameRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
} else {
|
} else {
|
||||||
if ("@" in login && "login" in host) {
|
if ("@" in loginData.login && "login" in loginData.baseUrl) {
|
||||||
view?.setErrorLoginRequired()
|
view?.setErrorLoginRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
if ("@" !in login && "email" in host) {
|
if ("@" !in loginData.login && "email" in loginData.baseUrl) {
|
||||||
view?.setErrorEmailRequired()
|
view?.setErrorEmailRequired()
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
if ("@" in login && "||" !in login && "login" !in host && "email" !in host) {
|
|
||||||
val emailHost = login.substringAfter("@")
|
val isEmailLogin = "@" in loginData.login
|
||||||
val emailDomain = URL(host).host
|
val isEmailWithLogin = "||" !in loginData.login
|
||||||
|
val isLoginNotRequired = "login" !in loginData.baseUrl
|
||||||
|
val isEmailNotRequired = "email" !in loginData.baseUrl
|
||||||
|
if (isEmailLogin && isEmailWithLogin && isLoginNotRequired && isEmailNotRequired) {
|
||||||
|
val emailHost = loginData.login.substringAfter("@")
|
||||||
|
val emailDomain = URL(loginData.baseUrl).host
|
||||||
if (!emailHost.equals(emailDomain, true)) {
|
if (!emailHost.equals(emailDomain, true)) {
|
||||||
view?.setErrorEmailInvalid(domain = emailDomain)
|
view?.setErrorEmailInvalid(domain = emailDomain)
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
@ -254,16 +269,21 @@ class LoginFormPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.isEmpty()) {
|
if (loginData.password.isEmpty()) {
|
||||||
view?.setErrorPassRequired(focus = isCorrect)
|
view?.setErrorPassRequired(focus = isCorrect)
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.length < 6 && password.isNotEmpty()) {
|
if (loginData.password.length < 6 && loginData.password.isNotEmpty()) {
|
||||||
view?.setErrorPassInvalid(focus = isCorrect)
|
view?.setErrorPassInvalid(focus = isCorrect)
|
||||||
isCorrect = false
|
isCorrect = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (loginData.domainSuffix !in listOf("", "rc", "kurs")) {
|
||||||
|
view?.setDomainSuffixInvalid()
|
||||||
|
isCorrect = false
|
||||||
|
}
|
||||||
|
|
||||||
return isCorrect
|
return isCorrect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,16 @@ interface LoginFormView : BaseView {
|
|||||||
|
|
||||||
fun setErrorEmailInvalid(domain: String)
|
fun setErrorEmailInvalid(domain: String)
|
||||||
|
|
||||||
|
fun setDomainSuffixInvalid()
|
||||||
|
|
||||||
fun clearUsernameError()
|
fun clearUsernameError()
|
||||||
|
|
||||||
fun clearPassError()
|
fun clearPassError()
|
||||||
|
|
||||||
fun clearHostError()
|
fun clearHostError()
|
||||||
|
|
||||||
|
fun clearDomainSuffixError()
|
||||||
|
|
||||||
fun showSoftKeyboard()
|
fun showSoftKeyboard()
|
||||||
|
|
||||||
fun hideSoftKeyboard()
|
fun hideSoftKeyboard()
|
||||||
|
@ -96,10 +96,7 @@ class LoginSymbolPresenter @Inject constructor(
|
|||||||
?.takeIf { it.symbol == loginData.userEnteredSymbol }
|
?.takeIf { it.symbol == loginData.userEnteredSymbol }
|
||||||
|
|
||||||
if (enteredSymbolDetails?.error is InvalidSymbolException) {
|
if (enteredSymbolDetails?.error is InvalidSymbolException) {
|
||||||
view?.run {
|
showInvalidSymbolError()
|
||||||
setErrorSymbolInvalid()
|
|
||||||
showContact(true)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Timber.i("Login with symbol result: Success")
|
Timber.i("Login with symbol result: Success")
|
||||||
view?.navigateToStudentSelect(loginData, requireNotNull(user.data))
|
view?.navigateToStudentSelect(loginData, requireNotNull(user.data))
|
||||||
@ -128,6 +125,9 @@ class LoginSymbolPresenter @Inject constructor(
|
|||||||
loginErrorHandler.dispatch(user.error)
|
loginErrorHandler.dispatch(user.error)
|
||||||
lastError = user.error
|
lastError = user.error
|
||||||
view?.showContact(true)
|
view?.showContact(true)
|
||||||
|
if (user.error is InvalidSymbolException) {
|
||||||
|
showInvalidSymbolError()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onResourceNotLoading {
|
}.onResourceNotLoading {
|
||||||
@ -145,6 +145,13 @@ class LoginSymbolPresenter @Inject constructor(
|
|||||||
return normalizedSymbol in definitelyInvalidSymbols
|
return normalizedSymbol in definitelyInvalidSymbols
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showInvalidSymbolError() {
|
||||||
|
view?.run {
|
||||||
|
setErrorSymbolInvalid()
|
||||||
|
showContact(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onFaqClick() {
|
fun onFaqClick() {
|
||||||
view?.openFaqPage()
|
view?.openFaqPage()
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@ import android.view.View
|
|||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.Resource
|
import io.github.wulkanowy.data.dataOrThrow
|
||||||
import io.github.wulkanowy.data.dataOrNull
|
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
|
||||||
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
|
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.data.toFirstResult
|
import io.github.wulkanowy.data.toFirstResult
|
||||||
@ -69,8 +67,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
|||||||
|
|
||||||
appWidgetIds?.forEach { widgetId ->
|
appWidgetIds?.forEach { widgetId ->
|
||||||
val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0)
|
val studentId = sharedPref.getLong(getStudentWidgetKey(widgetId), 0)
|
||||||
val luckyNumberResource = getLuckyNumber(studentId, widgetId)
|
val luckyNumber = getLuckyNumber(studentId, widgetId)?.luckyNumber?.toString()
|
||||||
val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber?.toString()
|
|
||||||
val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber)
|
val remoteView = RemoteViews(context.packageName, R.layout.widget_luckynumber)
|
||||||
.apply {
|
.apply {
|
||||||
setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-")
|
setTextViewText(R.id.luckyNumberWidgetValue, luckyNumber ?: "-")
|
||||||
@ -143,18 +140,18 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
|||||||
sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id)
|
sharedPref.putLong(getStudentWidgetKey(appWidgetId), it.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentStudent != null) {
|
if (currentStudent != null) {
|
||||||
luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false)
|
luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false)
|
||||||
.toFirstResult()
|
.toFirstResult()
|
||||||
} else {
|
.dataOrThrow
|
||||||
Resource.Success<LuckyNumber?>(null)
|
} else null
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "An error has occurred in lucky number provider")
|
Timber.e(e, "An error has occurred in lucky number provider")
|
||||||
Resource.Error(e)
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +73,7 @@ class MainPresenter @Inject constructor(
|
|||||||
syncManager.startPeriodicSyncWorker()
|
syncManager.startPeriodicSyncWorker()
|
||||||
|
|
||||||
checkAppSupport()
|
checkAppSupport()
|
||||||
|
updateCurrentStudentAuthStatus()
|
||||||
|
|
||||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||||
Timber.i("Main view was initialized with $initDestination")
|
Timber.i("Main view was initialized with $initDestination")
|
||||||
@ -191,4 +192,11 @@ class MainPresenter @Inject constructor(
|
|||||||
|
|
||||||
view?.showStudentAvatar(currentStudent)
|
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?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
presenter.onAttachView(
|
presenter.onAttachView(
|
||||||
|
@ -50,12 +50,15 @@ class MessagePreviewAdapter @Inject constructor() :
|
|||||||
ViewType.MESSAGE.id -> MessageViewHolder(
|
ViewType.MESSAGE.id -> MessageViewHolder(
|
||||||
ItemMessagePreviewBinding.inflate(inflater, parent, false)
|
ItemMessagePreviewBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
ViewType.DIVIDER.id -> DividerViewHolder(
|
ViewType.DIVIDER.id -> DividerViewHolder(
|
||||||
ItemMessageDividerBinding.inflate(inflater, parent, false)
|
ItemMessageDividerBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
ViewType.ATTACHMENT.id -> AttachmentViewHolder(
|
ViewType.ATTACHMENT.id -> AttachmentViewHolder(
|
||||||
ItemMessageAttachmentBinding.inflate(inflater, parent, false)
|
ItemMessageAttachmentBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> throw IllegalStateException()
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,6 +69,7 @@ class MessagePreviewAdapter @Inject constructor() :
|
|||||||
holder,
|
holder,
|
||||||
requireNotNull(messageWithAttachment).message
|
requireNotNull(messageWithAttachment).message
|
||||||
)
|
)
|
||||||
|
|
||||||
is AttachmentViewHolder -> bindAttachment(
|
is AttachmentViewHolder -> bindAttachment(
|
||||||
holder,
|
holder,
|
||||||
requireNotNull(messageWithAttachment).attachments[position - 2]
|
requireNotNull(messageWithAttachment).attachments[position - 2]
|
||||||
@ -82,9 +86,11 @@ class MessagePreviewAdapter @Inject constructor() :
|
|||||||
recipientCount > 1 -> {
|
recipientCount > 1 -> {
|
||||||
context.getString(R.string.message_read_by, message.readBy, recipientCount)
|
context.getString(R.string.message_read_by, message.readBy, recipientCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
message.readBy == 1 || (isReceived && !message.unread) -> {
|
message.readBy == 1 || (isReceived && !message.unread) -> {
|
||||||
context.getString(R.string.message_read, context.getString(R.string.all_yes))
|
context.getString(R.string.message_read, context.getString(R.string.all_yes))
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
|
else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,18 +44,33 @@ class MessagePreviewFragment :
|
|||||||
|
|
||||||
private var menuForwardButton: MenuItem? = null
|
private var menuForwardButton: MenuItem? = null
|
||||||
|
|
||||||
|
private var menuRestoreButton: MenuItem? = null
|
||||||
|
|
||||||
private var menuDeleteButton: MenuItem? = null
|
private var menuDeleteButton: MenuItem? = null
|
||||||
|
|
||||||
|
private var menuDeleteForeverButton: MenuItem? = null
|
||||||
|
|
||||||
private var menuShareButton: MenuItem? = null
|
private var menuShareButton: MenuItem? = null
|
||||||
|
|
||||||
private var menuPrintButton: MenuItem? = null
|
private var menuPrintButton: MenuItem? = null
|
||||||
|
|
||||||
|
private var menuMuteButton: MenuItem? = null
|
||||||
|
|
||||||
override val titleStringId: Int
|
override val titleStringId: Int
|
||||||
get() = R.string.message_title
|
get() = R.string.message_title
|
||||||
|
|
||||||
override val deleteMessageSuccessString: String
|
override val deleteMessageSuccessString: String
|
||||||
get() = getString(R.string.message_delete_success)
|
get() = getString(R.string.message_delete_success)
|
||||||
|
|
||||||
|
override val muteMessageSuccessString: String
|
||||||
|
get() = getString(R.string.message_mute_success)
|
||||||
|
|
||||||
|
override val unmuteMessageSuccessString: String
|
||||||
|
get() = getString(R.string.message_unmute_success)
|
||||||
|
|
||||||
|
override val restoreMessageSuccessString: String
|
||||||
|
get() = getString(R.string.message_restore_success)
|
||||||
|
|
||||||
override val messageNoSubjectString: String
|
override val messageNoSubjectString: String
|
||||||
get() = getString(R.string.message_no_subject)
|
get() = getString(R.string.message_no_subject)
|
||||||
|
|
||||||
@ -67,10 +82,10 @@ class MessagePreviewFragment :
|
|||||||
get() = getString(R.string.message_not_exists)
|
get() = getString(R.string.message_not_exists)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MESSAGE_ID_KEY = "message_id"
|
private const val MESSAGE_ARG_KEY = "message"
|
||||||
|
|
||||||
fun newInstance(message: Message) = MessagePreviewFragment().apply {
|
fun newInstance(message: Message) = MessagePreviewFragment().apply {
|
||||||
arguments = bundleOf(MESSAGE_ID_KEY to message)
|
arguments = bundleOf(MESSAGE_ARG_KEY to message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +101,7 @@ class MessagePreviewFragment :
|
|||||||
messageContainer = binding.messagePreviewContainer
|
messageContainer = binding.messagePreviewContainer
|
||||||
presenter.onAttachView(
|
presenter.onAttachView(
|
||||||
view = this,
|
view = this,
|
||||||
message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY),
|
message = requireArguments().serializable(MESSAGE_ARG_KEY),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,9 +118,12 @@ class MessagePreviewFragment :
|
|||||||
inflater.inflate(R.menu.action_menu_message_preview, menu)
|
inflater.inflate(R.menu.action_menu_message_preview, menu)
|
||||||
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
|
menuReplyButton = menu.findItem(R.id.messagePreviewMenuReply)
|
||||||
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
|
menuForwardButton = menu.findItem(R.id.messagePreviewMenuForward)
|
||||||
|
menuRestoreButton = menu.findItem(R.id.messagePreviewMenuRestore)
|
||||||
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
|
menuDeleteButton = menu.findItem(R.id.messagePreviewMenuDelete)
|
||||||
|
menuDeleteForeverButton = menu.findItem(R.id.messagePreviewMenuDeleteForever)
|
||||||
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
|
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
|
||||||
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
|
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
|
||||||
|
menuMuteButton = menu.findItem(R.id.messagePreviewMenuMute)
|
||||||
presenter.onCreateOptionsMenu()
|
presenter.onCreateOptionsMenu()
|
||||||
|
|
||||||
menu.findItem(R.id.mainMenuAccount).isVisible = false
|
menu.findItem(R.id.mainMenuAccount).isVisible = false
|
||||||
@ -115,9 +133,12 @@ class MessagePreviewFragment :
|
|||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.messagePreviewMenuReply -> presenter.onReply()
|
R.id.messagePreviewMenuReply -> presenter.onReply()
|
||||||
R.id.messagePreviewMenuForward -> presenter.onForward()
|
R.id.messagePreviewMenuForward -> presenter.onForward()
|
||||||
|
R.id.messagePreviewMenuRestore -> presenter.onMessageRestore()
|
||||||
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
|
R.id.messagePreviewMenuDelete -> presenter.onMessageDelete()
|
||||||
|
R.id.messagePreviewMenuDeleteForever -> presenter.onMessageDelete()
|
||||||
R.id.messagePreviewMenuShare -> presenter.onShare()
|
R.id.messagePreviewMenuShare -> presenter.onShare()
|
||||||
R.id.messagePreviewMenuPrint -> presenter.onPrint()
|
R.id.messagePreviewMenuPrint -> presenter.onPrint()
|
||||||
|
R.id.messagePreviewMenuMute -> presenter.onMute()
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,6 +150,11 @@ class MessagePreviewFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun updateMuteToggleButton(isMuted: Boolean) {
|
||||||
|
menuMuteButton?.setTitle(if (isMuted) R.string.message_unmute else R.string.message_mute)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
override fun showProgress(show: Boolean) {
|
override fun showProgress(show: Boolean) {
|
||||||
binding.messagePreviewProgress.visibility = if (show) VISIBLE else GONE
|
binding.messagePreviewProgress.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
@ -137,20 +163,15 @@ class MessagePreviewFragment :
|
|||||||
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
|
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showOptions(show: Boolean, isReplayable: Boolean) {
|
override fun showOptions(show: Boolean, isReplayable: Boolean, isRestorable: Boolean) {
|
||||||
menuReplyButton?.isVisible = isReplayable
|
menuReplyButton?.isVisible = show && isReplayable
|
||||||
menuForwardButton?.isVisible = show
|
menuForwardButton?.isVisible = show
|
||||||
menuDeleteButton?.isVisible = show
|
menuRestoreButton?.isVisible = show && isRestorable
|
||||||
|
menuDeleteButton?.isVisible = show && !isRestorable
|
||||||
|
menuDeleteForeverButton?.isVisible = show && isRestorable
|
||||||
menuShareButton?.isVisible = show
|
menuShareButton?.isVisible = show
|
||||||
menuPrintButton?.isVisible = show
|
menuPrintButton?.isVisible = show
|
||||||
}
|
menuMuteButton?.isVisible = show && isReplayable
|
||||||
|
|
||||||
override fun setDeletedOptionsLabels() {
|
|
||||||
menuDeleteButton?.setTitle(R.string.message_delete_forever)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setNotDeletedOptionsLabels() {
|
|
||||||
menuDeleteButton?.setTitle(R.string.message_move_to_trash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showErrorView(show: Boolean) {
|
override fun showErrorView(show: Boolean) {
|
||||||
@ -212,11 +233,6 @@ class MessagePreviewFragment :
|
|||||||
(activity as MainActivity).popView()
|
(activity as MainActivity).popView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
outState.putSerializable(MESSAGE_ID_KEY, presenter.message)
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -3,10 +3,15 @@ package io.github.wulkanowy.ui.modules.message.preview
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import androidx.core.text.parseAsHtml
|
import androidx.core.text.parseAsHtml
|
||||||
import io.github.wulkanowy.R
|
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.Message
|
||||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder
|
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.MessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
@ -14,9 +19,11 @@ import io.github.wulkanowy.ui.base.BasePresenter
|
|||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class MessagePreviewPresenter @Inject constructor(
|
class MessagePreviewPresenter @Inject constructor(
|
||||||
errorHandler: ErrorHandler,
|
errorHandler: ErrorHandler,
|
||||||
@ -26,20 +33,17 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
private val analytics: AnalyticsHelper
|
private val analytics: AnalyticsHelper
|
||||||
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {
|
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
var message: Message? = null
|
private var messageWithAttachments: MessageWithAttachment? = null
|
||||||
|
|
||||||
var attachments: List<MessageAttachment>? = null
|
|
||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
private var retryCallback: () -> Unit = {}
|
private var retryCallback: () -> Unit = {}
|
||||||
|
|
||||||
fun onAttachView(view: MessagePreviewView, message: Message?) {
|
fun onAttachView(view: MessagePreviewView, message: Message) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
errorHandler.showErrorMessage = ::showErrorViewOnError
|
errorHandler.showErrorMessage = ::showErrorViewOnError
|
||||||
this.message = message
|
loadData(message)
|
||||||
loadData(requireNotNull(message))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMessageLoadRetry(message: Message) {
|
private fun onMessageLoadRetry(message: Message) {
|
||||||
@ -66,25 +70,24 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
.logResourceStatus("message ${messageToLoad.messageId} preview")
|
.logResourceStatus("message ${messageToLoad.messageId} preview")
|
||||||
.onResourceData {
|
.onResourceData {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
message = it.message
|
messageWithAttachments = it
|
||||||
attachments = it.attachments
|
|
||||||
view?.apply {
|
view?.apply {
|
||||||
setMessageWithAttachment(it)
|
setMessageWithAttachment(it)
|
||||||
showContent(true)
|
showContent(true)
|
||||||
initOptions()
|
initOptions()
|
||||||
|
updateMuteToggleButton(isMuted = it.mutedMessageSender != null)
|
||||||
if (preferencesRepository.isIncognitoMode && it.message.unread) {
|
if (preferencesRepository.isIncognitoMode && it.message.unread) {
|
||||||
showMessage(R.string.message_incognito_description)
|
showMessage(R.string.message_incognito_description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
delay(1.seconds)
|
||||||
view?.run {
|
view?.run {
|
||||||
showMessage(messageNotExists)
|
showMessage(messageNotExists)
|
||||||
popView()
|
popView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}.onResourceSuccess {
|
||||||
.onResourceSuccess {
|
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
analytics.logEvent(
|
analytics.logEvent(
|
||||||
"load_item",
|
"load_item",
|
||||||
@ -92,31 +95,28 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
"length" to it.message.content.length
|
"length" to it.message.content.length
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}.onResourceNotLoading { view?.showProgress(false) }.onResourceError {
|
||||||
.onResourceNotLoading { view?.showProgress(false) }
|
|
||||||
.onResourceError {
|
|
||||||
retryCallback = { onMessageLoadRetry(messageToLoad) }
|
retryCallback = { onMessageLoadRetry(messageToLoad) }
|
||||||
errorHandler.dispatch(it)
|
errorHandler.dispatch(it)
|
||||||
}
|
}.launch()
|
||||||
.launch()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onReply(): Boolean {
|
fun onReply(): Boolean {
|
||||||
return if (message != null) {
|
return if (messageWithAttachments?.message != null) {
|
||||||
view?.openMessageReply(message)
|
view?.openMessageReply(messageWithAttachments?.message)
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onForward(): Boolean {
|
fun onForward(): Boolean {
|
||||||
return if (message != null) {
|
return if (messageWithAttachments?.message != null) {
|
||||||
view?.openMessageForward(message)
|
view?.openMessageForward(messageWithAttachments?.message)
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onShare(): Boolean {
|
fun onShare(): Boolean {
|
||||||
val message = message ?: return false
|
val message = messageWithAttachments?.message ?: return false
|
||||||
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
||||||
|
|
||||||
val text = buildString {
|
val text = buildString {
|
||||||
@ -129,13 +129,15 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
|
|
||||||
appendLine(message.content.parseAsHtml())
|
appendLine(message.content.parseAsHtml())
|
||||||
|
|
||||||
if (!attachments.isNullOrEmpty()) {
|
if (!messageWithAttachments?.attachments.isNullOrEmpty()) {
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("Załączniki:")
|
appendLine("Załączniki:")
|
||||||
|
|
||||||
append(attachments.orEmpty().joinToString(separator = "\n") { attachment ->
|
append(
|
||||||
"${attachment.filename}: ${attachment.url}"
|
messageWithAttachments?.attachments.orEmpty()
|
||||||
})
|
.joinToString(separator = "\n") { attachment ->
|
||||||
|
"${attachment.filename}: ${attachment.url}"
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
fun onPrint(): Boolean {
|
fun onPrint(): Boolean {
|
||||||
val message = message ?: return false
|
val message = messageWithAttachments?.message ?: return false
|
||||||
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
||||||
|
|
||||||
val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
|
val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
|
||||||
@ -159,8 +161,7 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
append("<div><h4>Od</h4>${message.sender}</div>")
|
append("<div><h4>Od</h4>${message.sender}</div>")
|
||||||
append("<div><h4>DO</h4>${message.recipients}</div>")
|
append("<div><h4>DO</h4>${message.recipients}</div>")
|
||||||
}
|
}
|
||||||
val messageContent = "<p>${message.content}</p>"
|
val messageContent = "<p>${message.content}</p>".replace(Regex("[\\n\\r]{2,}"), "</p><p>")
|
||||||
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
|
|
||||||
.replace(Regex("[\\n\\r]"), "<br>")
|
.replace(Regex("[\\n\\r]"), "<br>")
|
||||||
|
|
||||||
val jobName = buildString {
|
val jobName = buildString {
|
||||||
@ -171,9 +172,7 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
view?.apply {
|
view?.apply {
|
||||||
val html = printHTML
|
val html = printHTML.replace("%SUBJECT%", subject).replace("%CONTENT%", messageContent)
|
||||||
.replace("%SUBJECT%", subject)
|
|
||||||
.replace("%CONTENT%", messageContent)
|
|
||||||
.replace("%INFO%", infoContent)
|
.replace("%INFO%", infoContent)
|
||||||
printDocument(html, jobName)
|
printDocument(html, jobName)
|
||||||
}
|
}
|
||||||
@ -181,34 +180,69 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteMessage() {
|
private fun restoreMessage() {
|
||||||
message ?: return
|
val message = messageWithAttachments?.message ?: return
|
||||||
|
|
||||||
view?.run {
|
view?.run {
|
||||||
showContent(false)
|
showContent(false)
|
||||||
showProgress(true)
|
showProgress(true)
|
||||||
showOptions(show = false, isReplayable = false)
|
showOptions(
|
||||||
|
show = false,
|
||||||
|
isReplayable = false,
|
||||||
|
isRestorable = false,
|
||||||
|
)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
}
|
}
|
||||||
|
Timber.i("Restore message ${message.messageGlobalKey}")
|
||||||
Timber.i("Delete message ${message?.messageGlobalKey}")
|
|
||||||
|
|
||||||
presenterScope.launch {
|
presenterScope.launch {
|
||||||
runCatching {
|
runCatching {
|
||||||
val student = studentRepository.getCurrentStudent(decryptPass = true)
|
val student = studentRepository.getCurrentStudent(decryptPass = true)
|
||||||
val mailbox = messageRepository.getMailboxByStudent(student)
|
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||||
messageRepository.deleteMessage(student, mailbox, message!!)
|
messageRepository.restoreMessages(student, mailbox, listOfNotNull(message))
|
||||||
}
|
}
|
||||||
.onFailure {
|
.onFailure {
|
||||||
retryCallback = { onMessageDelete() }
|
retryCallback = { onMessageRestore() }
|
||||||
errorHandler.dispatch(it)
|
errorHandler.dispatch(it)
|
||||||
}
|
}
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
view?.run {
|
view?.run {
|
||||||
showMessage(deleteMessageSuccessString)
|
showMessage(restoreMessageSuccessString)
|
||||||
popView()
|
popView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
view?.showProgress(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteMessage() {
|
||||||
|
messageWithAttachments?.message ?: return
|
||||||
|
|
||||||
|
view?.run {
|
||||||
|
showContent(false)
|
||||||
|
showProgress(true)
|
||||||
|
showOptions(
|
||||||
|
show = false,
|
||||||
|
isReplayable = false,
|
||||||
|
isRestorable = false,
|
||||||
|
)
|
||||||
|
showErrorView(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.i("Delete message ${messageWithAttachments?.message?.messageGlobalKey}")
|
||||||
|
|
||||||
|
presenterScope.launch {
|
||||||
|
runCatching {
|
||||||
|
val student = studentRepository.getCurrentStudent(decryptPass = true)
|
||||||
|
messageRepository.deleteMessage(student, messageWithAttachments?.message!!)
|
||||||
|
}.onFailure {
|
||||||
|
retryCallback = { onMessageDelete() }
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}.onSuccess {
|
||||||
|
view?.run {
|
||||||
|
showMessage(deleteMessageSuccessString)
|
||||||
|
popView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
view?.showProgress(false)
|
view?.showProgress(false)
|
||||||
}
|
}
|
||||||
@ -224,6 +258,11 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onMessageRestore(): Boolean {
|
||||||
|
restoreMessage()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
fun onMessageDelete(): Boolean {
|
fun onMessageDelete(): Boolean {
|
||||||
deleteMessage()
|
deleteMessage()
|
||||||
return true
|
return true
|
||||||
@ -232,20 +271,39 @@ class MessagePreviewPresenter @Inject constructor(
|
|||||||
private fun initOptions() {
|
private fun initOptions() {
|
||||||
view?.apply {
|
view?.apply {
|
||||||
showOptions(
|
showOptions(
|
||||||
show = message != null,
|
show = messageWithAttachments?.message != null,
|
||||||
isReplayable = message?.folderId != MessageFolder.SENT.id,
|
isReplayable = messageWithAttachments?.message?.folderId == MessageFolder.RECEIVED.id,
|
||||||
|
isRestorable = messageWithAttachments?.message?.folderId == MessageFolder.TRASHED.id,
|
||||||
)
|
)
|
||||||
message?.let {
|
|
||||||
when (it.folderId == MessageFolder.TRASHED.id) {
|
|
||||||
true -> setDeletedOptionsLabels()
|
|
||||||
false -> setNotDeletedOptionsLabels()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onCreateOptionsMenu() {
|
fun onCreateOptionsMenu() {
|
||||||
initOptions()
|
initOptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onMute(): Boolean {
|
||||||
|
val message = messageWithAttachments?.message ?: return false
|
||||||
|
val isMuted = messageWithAttachments?.mutedMessageSender != null
|
||||||
|
|
||||||
|
presenterScope.launch {
|
||||||
|
runCatching {
|
||||||
|
when (isMuted) {
|
||||||
|
true -> {
|
||||||
|
messageRepository.unmuteMessage(message.correspondents)
|
||||||
|
view?.run { showMessage(unmuteMessageSuccessString) }
|
||||||
|
}
|
||||||
|
|
||||||
|
false -> {
|
||||||
|
messageRepository.muteMessage(message.correspondents)
|
||||||
|
view?.run { showMessage(muteMessageSuccessString) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.onFailure {
|
||||||
|
errorHandler.dispatch(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view?.updateMuteToggleButton(isMuted)
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,12 @@ interface MessagePreviewView : BaseView {
|
|||||||
|
|
||||||
val deleteMessageSuccessString: String
|
val deleteMessageSuccessString: String
|
||||||
|
|
||||||
|
val muteMessageSuccessString: String
|
||||||
|
|
||||||
|
val unmuteMessageSuccessString: String
|
||||||
|
|
||||||
|
val restoreMessageSuccessString: String
|
||||||
|
|
||||||
val messageNoSubjectString: String
|
val messageNoSubjectString: String
|
||||||
|
|
||||||
val printHTML: String
|
val printHTML: String
|
||||||
@ -19,6 +25,8 @@ interface MessagePreviewView : BaseView {
|
|||||||
|
|
||||||
fun setMessageWithAttachment(item: MessageWithAttachment)
|
fun setMessageWithAttachment(item: MessageWithAttachment)
|
||||||
|
|
||||||
|
fun updateMuteToggleButton(isMuted: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
@ -29,11 +37,7 @@ interface MessagePreviewView : BaseView {
|
|||||||
|
|
||||||
fun setErrorRetryCallback(callback: () -> Unit)
|
fun setErrorRetryCallback(callback: () -> Unit)
|
||||||
|
|
||||||
fun showOptions(show: Boolean, isReplayable: Boolean)
|
fun showOptions(show: Boolean, isReplayable: Boolean, isRestorable: Boolean)
|
||||||
|
|
||||||
fun setDeletedOptionsLabels()
|
|
||||||
|
|
||||||
fun setNotDeletedOptionsLabels()
|
|
||||||
|
|
||||||
fun openMessageReply(message: Message?)
|
fun openMessageReply(message: Message?)
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ class SendMessagePresenter @Inject constructor(
|
|||||||
subject = subject,
|
subject = subject,
|
||||||
content = content,
|
content = content,
|
||||||
recipients = recipients,
|
recipients = recipients,
|
||||||
mailboxId = mailbox.globalKey,
|
mailbox = mailbox,
|
||||||
)
|
)
|
||||||
}.logResourceStatus("sending message").onEach {
|
}.logResourceStatus("sending message").onEach {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
@ -18,8 +18,7 @@ import io.github.wulkanowy.utils.getThemeAttrColor
|
|||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class MessageTabAdapter @Inject constructor() :
|
class MessageTabAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|
||||||
|
|
||||||
lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit
|
lateinit var onItemClickListener: (MessageTabDataItem.MessageItem, position: Int) -> Unit
|
||||||
|
|
||||||
@ -52,10 +51,11 @@ class MessageTabAdapter @Inject constructor() :
|
|||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
return when (MessageItemViewType.values()[viewType]) {
|
return when (MessageItemViewType.entries[viewType]) {
|
||||||
MessageItemViewType.FILTERS -> HeaderViewHolder(
|
MessageItemViewType.FILTERS -> HeaderViewHolder(
|
||||||
ItemMessageChipsBinding.inflate(inflater, parent, false)
|
ItemMessageChipsBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
|
|
||||||
MessageItemViewType.MESSAGE -> ItemViewHolder(
|
MessageItemViewType.MESSAGE -> ItemViewHolder(
|
||||||
ItemMessageBinding.inflate(inflater, parent, false)
|
ItemMessageBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
@ -137,7 +137,12 @@ class MessageTabAdapter @Inject constructor() :
|
|||||||
ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor))
|
ImageViewCompat.setImageTintList(this, ColorStateList.valueOf(currentTextColor))
|
||||||
isVisible = message.hasAttachments
|
isVisible = message.hasAttachments
|
||||||
}
|
}
|
||||||
messageItemUnreadIndicator.isVisible = message.unread
|
messageItemUnreadIndicator.isVisible = message.unread || item.isMuted
|
||||||
|
|
||||||
|
when (item.isMuted) {
|
||||||
|
true -> messageItemUnreadIndicator.setImageResource(R.drawable.ic_notifications_off)
|
||||||
|
else -> messageItemUnreadIndicator.setImageResource(R.drawable.ic_circle_notification)
|
||||||
|
}
|
||||||
|
|
||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
holder.bindingAdapterPosition.let {
|
holder.bindingAdapterPosition.let {
|
||||||
@ -165,8 +170,7 @@ class MessageTabAdapter @Inject constructor() :
|
|||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
private class MessageTabDiffUtil(
|
private class MessageTabDiffUtil(
|
||||||
private val old: List<MessageTabDataItem>,
|
private val old: List<MessageTabDataItem>, private val new: List<MessageTabDataItem>
|
||||||
private val new: List<MessageTabDataItem>
|
|
||||||
) : DiffUtil.Callback() {
|
) : DiffUtil.Callback() {
|
||||||
|
|
||||||
override fun getOldListSize(): Int = old.size
|
override fun getOldListSize(): Int = old.size
|
||||||
|
@ -6,6 +6,7 @@ sealed class MessageTabDataItem(val viewType: MessageItemViewType) {
|
|||||||
|
|
||||||
data class MessageItem(
|
data class MessageItem(
|
||||||
val message: Message,
|
val message: Message,
|
||||||
|
val isMuted: Boolean,
|
||||||
val isSelected: Boolean,
|
val isSelected: Boolean,
|
||||||
val isActionMode: Boolean
|
val isActionMode: Boolean
|
||||||
) : MessageTabDataItem(MessageItemViewType.MESSAGE)
|
) : MessageTabDataItem(MessageItemViewType.MESSAGE)
|
||||||
|
@ -5,7 +5,9 @@ import android.view.Menu
|
|||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.*
|
import android.view.View.GONE
|
||||||
|
import android.view.View.INVISIBLE
|
||||||
|
import android.view.View.VISIBLE
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
@ -64,10 +66,12 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
if (presenter.folder == MessageFolder.TRASHED) {
|
val isTrashFolder = presenter.folder == MessageFolder.TRASHED
|
||||||
val menuItem = menu.findItem(R.id.messageTabContextMenuDelete)
|
|
||||||
menuItem.setTitle(R.string.message_delete_forever)
|
menu.findItem(R.id.messageTabContextMenuDelete).setVisible(!isTrashFolder)
|
||||||
}
|
menu.findItem(R.id.messageTabContextMenuDeleteForever).setVisible(isTrashFolder)
|
||||||
|
menu.findItem(R.id.messageTabContextMenuRestore).setVisible(isTrashFolder)
|
||||||
|
|
||||||
return presenter.onPrepareActionMode()
|
return presenter.onPrepareActionMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +83,8 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
|||||||
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
override fun onActionItemClicked(mode: ActionMode, menu: MenuItem): Boolean {
|
||||||
when (menu.itemId) {
|
when (menu.itemId) {
|
||||||
R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete()
|
R.id.messageTabContextMenuDelete -> presenter.onActionModeSelectDelete()
|
||||||
|
R.id.messageTabContextMenuRestore -> presenter.onActionModeSelectRestore()
|
||||||
|
R.id.messageTabContextMenuDeleteForever -> presenter.onActionModeSelectDelete()
|
||||||
R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll()
|
R.id.messageTabContextMenuSelectAll -> presenter.onActionModeSelectCheckAll()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -4,6 +4,7 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.data.*
|
import io.github.wulkanowy.data.*
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.Message
|
||||||
|
import io.github.wulkanowy.data.db.entities.MessageWithMutedAuthor
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder
|
import io.github.wulkanowy.data.enums.MessageFolder
|
||||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
@ -39,7 +40,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
private var mailboxes: List<Mailbox> = emptyList()
|
private var mailboxes: List<Mailbox> = emptyList()
|
||||||
private var selectedMailbox: Mailbox? = null
|
private var selectedMailbox: Mailbox? = null
|
||||||
|
|
||||||
private var messages = emptyList<Message>()
|
private var messages = emptyList<MessageWithMutedAuthor>()
|
||||||
|
|
||||||
private val searchChannel = Channel<String>()
|
private val searchChannel = Channel<String>()
|
||||||
|
|
||||||
@ -120,8 +121,27 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onActionModeSelectRestore() {
|
||||||
|
Timber.i("Restore ${messagesToDelete.size} messages")
|
||||||
|
val messageList = messagesToDelete.toList()
|
||||||
|
|
||||||
|
presenterScope.launch {
|
||||||
|
view?.run {
|
||||||
|
showProgress(true)
|
||||||
|
showContent(false)
|
||||||
|
showActionMode(false)
|
||||||
|
}
|
||||||
|
runCatching {
|
||||||
|
val student = studentRepository.getCurrentStudent(true)
|
||||||
|
messageRepository.restoreMessages(student, selectedMailbox, messageList)
|
||||||
|
}
|
||||||
|
.onFailure(errorHandler::dispatch)
|
||||||
|
.onSuccess { view?.showMessage(R.string.message_messages_restored) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onActionModeSelectDelete() {
|
fun onActionModeSelectDelete() {
|
||||||
Timber.i("Delete ${messagesToDelete.size} messages)")
|
Timber.i("Delete ${messagesToDelete.size} messages")
|
||||||
val messageList = messagesToDelete.toList()
|
val messageList = messagesToDelete.toList()
|
||||||
|
|
||||||
presenterScope.launch {
|
presenterScope.launch {
|
||||||
@ -133,7 +153,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
|
|
||||||
runCatching {
|
runCatching {
|
||||||
val student = studentRepository.getCurrentStudent(true)
|
val student = studentRepository.getCurrentStudent(true)
|
||||||
messageRepository.deleteMessages(student, selectedMailbox, messageList)
|
messageRepository.deleteMessages(student, messageList)
|
||||||
}
|
}
|
||||||
.onFailure(errorHandler::dispatch)
|
.onFailure(errorHandler::dispatch)
|
||||||
.onSuccess { view?.showMessage(R.string.message_messages_deleted) }
|
.onSuccess { view?.showMessage(R.string.message_messages_deleted) }
|
||||||
@ -141,7 +161,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onActionModeSelectCheckAll() {
|
fun onActionModeSelectCheckAll() {
|
||||||
val messagesToSelect = getFilteredData()
|
val messagesToSelect = getFilteredData().map { it.message }
|
||||||
val isAllSelected = messagesToDelete.containsAll(messagesToSelect)
|
val isAllSelected = messagesToDelete.containsAll(messagesToSelect)
|
||||||
|
|
||||||
if (isAllSelected) {
|
if (isAllSelected) {
|
||||||
@ -188,7 +208,7 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
view?.showActionMode(false)
|
view?.showActionMode(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val filteredData = getFilteredData()
|
val filteredData = getFilteredData().map { it.message }
|
||||||
|
|
||||||
view?.run {
|
view?.run {
|
||||||
updateActionModeTitle(messagesToDelete.size)
|
updateActionModeTitle(messagesToDelete.size)
|
||||||
@ -320,25 +340,31 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getFilteredData(): List<Message> {
|
private fun getFilteredData(): List<MessageWithMutedAuthor> {
|
||||||
if (lastSearchQuery.trim().isEmpty()) {
|
if (lastSearchQuery.trim().isEmpty()) {
|
||||||
val sortedMessages = messages.sortedByDescending { it.date }
|
val sortedMessages = messages.sortedByDescending { it.message.date }
|
||||||
return when {
|
return when {
|
||||||
(onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
(onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter {
|
||||||
(onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
|
it.message.unread == onlyUnread && it.message.hasAttachments == onlyWithAttachments
|
||||||
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
}
|
||||||
|
|
||||||
|
(onlyUnread == true) -> sortedMessages.filter { it.message.unread == onlyUnread }
|
||||||
|
onlyWithAttachments -> sortedMessages.filter { it.message.hasAttachments == onlyWithAttachments }
|
||||||
else -> sortedMessages
|
else -> sortedMessages
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val sortedMessages = messages
|
val sortedMessages = messages
|
||||||
.map { it to calculateMatchRatio(it, lastSearchQuery) }
|
.map { it to calculateMatchRatio(it.message, lastSearchQuery) }
|
||||||
.sortedWith(compareBy<Pair<Message, Int>> { -it.second }.thenByDescending { it.first.date })
|
.sortedWith(compareBy<Pair<MessageWithMutedAuthor, Int>> { -it.second }.thenByDescending { it.first.message.date })
|
||||||
.filter { it.second > 6000 }
|
.filter { it.second > 6000 }
|
||||||
.map { it.first }
|
.map { it.first }
|
||||||
return when {
|
return when {
|
||||||
(onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter { it.unread == onlyUnread && it.hasAttachments == onlyWithAttachments }
|
(onlyUnread == true) && onlyWithAttachments -> sortedMessages.filter {
|
||||||
(onlyUnread == true) -> sortedMessages.filter { it.unread == onlyUnread }
|
it.message.unread == onlyUnread && it.message.hasAttachments == onlyWithAttachments
|
||||||
onlyWithAttachments -> sortedMessages.filter { it.hasAttachments == onlyWithAttachments }
|
}
|
||||||
|
|
||||||
|
(onlyUnread == true) -> sortedMessages.filter { it.message.unread == onlyUnread }
|
||||||
|
onlyWithAttachments -> sortedMessages.filter { it.message.hasAttachments == onlyWithAttachments }
|
||||||
else -> sortedMessages
|
else -> sortedMessages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -367,8 +393,9 @@ class MessageTabPresenter @Inject constructor(
|
|||||||
|
|
||||||
addAll(data.map { message ->
|
addAll(data.map { message ->
|
||||||
MessageTabDataItem.MessageItem(
|
MessageTabDataItem.MessageItem(
|
||||||
message = message,
|
message = message.message,
|
||||||
isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey },
|
isMuted = message.mutedMessageSender != null,
|
||||||
|
isSelected = messagesToDelete.any { it.messageGlobalKey == message.message.messageGlobalKey },
|
||||||
isActionMode = isActionMode
|
isActionMode = isActionMode
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.schoolannouncement
|
|||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding
|
import io.github.wulkanowy.databinding.ItemSchoolAnnouncementBinding
|
||||||
@ -29,6 +30,10 @@ class SchoolAnnouncementAdapter @Inject constructor() :
|
|||||||
schoolAnnouncementItemDate.text = item.date.toFormattedString()
|
schoolAnnouncementItemDate.text = item.date.toFormattedString()
|
||||||
schoolAnnouncementItemType.text = item.subject
|
schoolAnnouncementItemType.text = item.subject
|
||||||
schoolAnnouncementItemContent.text = item.content.parseUonetHtml()
|
schoolAnnouncementItemContent.text = item.content.parseUonetHtml()
|
||||||
|
with(schoolAnnouncementItemAuthor) {
|
||||||
|
text = item.author
|
||||||
|
isVisible = !item.author.isNullOrBlank()
|
||||||
|
}
|
||||||
|
|
||||||
root.setOnClickListener { onItemClickListener(item) }
|
root.setOnClickListener { onItemClickListener(item) }
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
return when (TimetableItemType.values()[viewType]) {
|
return when (TimetableItemType.entries[viewType]) {
|
||||||
TimetableItemType.SMALL -> SmallViewHolder(
|
TimetableItemType.SMALL -> SmallViewHolder(
|
||||||
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
@ -79,6 +79,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableSmallItemNumber.text = lesson.number.toString()
|
timetableSmallItemNumber.text = lesson.number.toString()
|
||||||
|
timetableSmallItemNumber.isVisible = item.isLessonNumberVisible
|
||||||
timetableSmallItemSubject.text = lesson.subject
|
timetableSmallItemSubject.text = lesson.subject
|
||||||
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
||||||
timetableSmallItemRoom.text = lesson.room
|
timetableSmallItemRoom.text = lesson.room
|
||||||
@ -97,6 +98,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableItemNumber.text = lesson.number.toString()
|
timetableItemNumber.text = lesson.number.toString()
|
||||||
|
timetableItemNumber.isVisible = item.isLessonNumberVisible
|
||||||
timetableItemSubject.text = lesson.subject
|
timetableItemSubject.text = lesson.subject
|
||||||
timetableItemGroup.text = lesson.group
|
timetableItemGroup.text = lesson.group
|
||||||
timetableItemRoom.text = lesson.room
|
timetableItemRoom.text = lesson.room
|
||||||
|
@ -7,12 +7,14 @@ sealed class TimetableItem(val type: TimetableItemType) {
|
|||||||
|
|
||||||
data class Small(
|
data class Small(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
val onClick: (Timetable) -> Unit,
|
val onClick: (Timetable) -> Unit,
|
||||||
) : TimetableItem(TimetableItemType.SMALL)
|
) : TimetableItem(TimetableItemType.SMALL)
|
||||||
|
|
||||||
data class Normal(
|
data class Normal(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
val showGroupsInPlan: Boolean,
|
val showGroupsInPlan: Boolean,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
val timeLeft: TimeLeft?,
|
val timeLeft: TimeLeft?,
|
||||||
val onClick: (Timetable) -> Unit,
|
val onClick: (Timetable) -> Unit,
|
||||||
) : TimetableItem(TimetableItemType.NORMAL)
|
) : TimetableItem(TimetableItemType.NORMAL)
|
||||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.timetable
|
|||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
|
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
|
||||||
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
|
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
|
||||||
@ -58,6 +57,7 @@ class TimetablePresenter @Inject constructor(
|
|||||||
|
|
||||||
private var initialDate: LocalDate? = null
|
private var initialDate: LocalDate? = null
|
||||||
private var isWeekendHasLessons: Boolean = false
|
private var isWeekendHasLessons: Boolean = false
|
||||||
|
private var isEduOne: Boolean = false
|
||||||
|
|
||||||
var currentDate: LocalDate? = null
|
var currentDate: LocalDate? = null
|
||||||
private set
|
private set
|
||||||
@ -150,7 +150,8 @@ class TimetablePresenter @Inject constructor(
|
|||||||
val student = studentRepository.getCurrentStudent()
|
val student = studentRepository.getCurrentStudent()
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
|
||||||
checkInitialAndCurrentDate(student, semester)
|
isEduOne = student.isEduOne == true
|
||||||
|
checkInitialAndCurrentDate(semester)
|
||||||
timetableRepository.getTimetable(
|
timetableRepository.getTimetable(
|
||||||
student = student,
|
student = student,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
@ -194,9 +195,9 @@ class TimetablePresenter @Inject constructor(
|
|||||||
.launch()
|
.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
|
private suspend fun checkInitialAndCurrentDate(semester: Semester) {
|
||||||
if (initialDate == null) {
|
if (initialDate == null) {
|
||||||
isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(student, semester)
|
isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(semester)
|
||||||
initialDate = getInitialDate(semester)
|
initialDate = getInitialDate(semester)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,9 +236,8 @@ class TimetablePresenter @Inject constructor(
|
|||||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||||
it.isStudentPlan
|
it.isStudentPlan
|
||||||
} else true
|
} else true
|
||||||
}.sortedWith(
|
}
|
||||||
compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
|
.sortedWith(compareBy({ item -> item.start }, { item -> !item.isStudentPlan }))
|
||||||
)
|
|
||||||
|
|
||||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||||
@ -258,13 +258,15 @@ class TimetablePresenter @Inject constructor(
|
|||||||
lesson = it,
|
lesson = it,
|
||||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||||
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||||
onClick = ::onTimetableItemSelected
|
onClick = ::onTimetableItemSelected,
|
||||||
|
isLessonNumberVisible = !isEduOne
|
||||||
)
|
)
|
||||||
add(normalLesson)
|
add(normalLesson)
|
||||||
} else {
|
} else {
|
||||||
val smallLesson = TimetableItem.Small(
|
val smallLesson = TimetableItem.Small(
|
||||||
lesson = it,
|
lesson = it,
|
||||||
onClick = ::onTimetableItemSelected
|
onClick = ::onTimetableItemSelected,
|
||||||
|
isLessonNumberVisible = !isEduOne
|
||||||
)
|
)
|
||||||
add(smallLesson)
|
add(smallLesson)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import android.view.View.VISIBLE
|
|||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import android.widget.RemoteViewsService
|
import android.widget.RemoteViewsService
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.dataOrNull
|
import io.github.wulkanowy.data.dataOrThrow
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -27,6 +27,7 @@ import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Co
|
|||||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getStudentWidgetKey
|
||||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
|
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.getTodayLastLessonEndDateTimeWidgetKey
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
|
import io.github.wulkanowy.utils.getErrorString
|
||||||
import io.github.wulkanowy.utils.getPlural
|
import io.github.wulkanowy.utils.getPlural
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@ -45,11 +46,8 @@ class TimetableWidgetFactory(
|
|||||||
) : RemoteViewsService.RemoteViewsFactory {
|
) : RemoteViewsService.RemoteViewsFactory {
|
||||||
|
|
||||||
private var items = emptyList<TimetableWidgetItem>()
|
private var items = emptyList<TimetableWidgetItem>()
|
||||||
|
|
||||||
private var timetableCanceledColor: Int? = null
|
private var timetableCanceledColor: Int? = null
|
||||||
|
|
||||||
private var textColor: Int? = null
|
private var textColor: Int? = null
|
||||||
|
|
||||||
private var timetableChangeColor: Int? = null
|
private var timetableChangeColor: Int? = null
|
||||||
|
|
||||||
override fun getLoadingView() = null
|
override fun getLoadingView() = null
|
||||||
@ -67,25 +65,31 @@ class TimetableWidgetFactory(
|
|||||||
override fun onDestroy() {}
|
override fun onDestroy() {}
|
||||||
|
|
||||||
override fun onDataSetChanged() {
|
override fun onDataSetChanged() {
|
||||||
intent?.extras?.getInt(EXTRA_APPWIDGET_ID)?.let { appWidgetId ->
|
val appWidgetId = intent?.extras?.getInt(EXTRA_APPWIDGET_ID) ?: return
|
||||||
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
|
val date = LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(appWidgetId), 0))
|
||||||
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
|
val studentId = sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0)
|
||||||
|
|
||||||
|
items = emptyList()
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
runCatching {
|
runCatching {
|
||||||
runBlocking {
|
val student = getStudent(studentId) ?: return@runBlocking
|
||||||
val student = getStudent(studentId) ?: return@runBlocking
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val lessons = getLessons(student, semester, date)
|
||||||
items = createItems(
|
val lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
|
||||||
lessons = getLessons(student, semester, date),
|
|
||||||
lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
|
createItems(lessons, lastSync, !(student.isEduOne ?: false))
|
||||||
)
|
}
|
||||||
|
.onFailure {
|
||||||
|
items = listOf(TimetableWidgetItem.Error(it))
|
||||||
|
Timber.e(it, "An error has occurred in timetable widget factory")
|
||||||
|
}
|
||||||
|
.onSuccess {
|
||||||
|
items = it
|
||||||
if (date == LocalDate.now()) {
|
if (date == LocalDate.now()) {
|
||||||
updateTodayLastLessonEnd(appWidgetId)
|
updateTodayLastLessonEnd(appWidgetId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.onFailure {
|
|
||||||
Timber.e(it, "An error has occurred in timetable widget factory")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,18 +102,20 @@ class TimetableWidgetFactory(
|
|||||||
student: Student, semester: Semester, date: LocalDate
|
student: Student, semester: Semester, date: LocalDate
|
||||||
): List<Timetable> {
|
): List<Timetable> {
|
||||||
val timetable = timetableRepository.getTimetable(student, semester, date, date, false)
|
val timetable = timetableRepository.getTimetable(student, semester, date, date, false)
|
||||||
val lessons = timetable.toFirstResult().dataOrNull?.lessons.orEmpty()
|
val lessons = timetable.toFirstResult().dataOrThrow.lessons
|
||||||
return lessons.sortedBy { it.number }
|
return lessons.sortedBy { it.start }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createItems(
|
private fun createItems(
|
||||||
lessons: List<Timetable>,
|
lessons: List<Timetable>,
|
||||||
lastSync: Instant?,
|
lastSync: Instant?,
|
||||||
|
isEduOne: Boolean
|
||||||
): List<TimetableWidgetItem> {
|
): List<TimetableWidgetItem> {
|
||||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
return buildList {
|
return buildList {
|
||||||
lessons.forEach {
|
lessons.forEach {
|
||||||
if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
|
if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
|
||||||
@ -119,7 +125,7 @@ class TimetableWidgetFactory(
|
|||||||
)
|
)
|
||||||
add(emptyItem)
|
add(emptyItem)
|
||||||
}
|
}
|
||||||
add(TimetableWidgetItem.Normal(it))
|
add(TimetableWidgetItem.Normal(it, isEduOne))
|
||||||
prevNum = it.number
|
prevNum = it.number
|
||||||
}
|
}
|
||||||
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
|
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
|
||||||
@ -133,15 +139,12 @@ class TimetableWidgetFactory(
|
|||||||
sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true)
|
sharedPref.putLong(key, todayLastLessonEnd.epochSecond, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val TIME_FORMAT_STYLE = "HH:mm"
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getViewAt(position: Int): RemoteViews? {
|
override fun getViewAt(position: Int): RemoteViews? {
|
||||||
return when (val item = items.getOrNull(position) ?: return null) {
|
return when (val item = items.getOrNull(position) ?: return null) {
|
||||||
is TimetableWidgetItem.Normal -> getNormalItemRemoteView(item)
|
is TimetableWidgetItem.Normal -> getNormalItemRemoteView(item)
|
||||||
is TimetableWidgetItem.Empty -> getEmptyItemRemoteView(item)
|
is TimetableWidgetItem.Empty -> getEmptyItemRemoteView(item)
|
||||||
is TimetableWidgetItem.Synchronized -> getSynchronizedItemRemoteView(item)
|
is TimetableWidgetItem.Synchronized -> getSynchronizedItemRemoteView(item)
|
||||||
|
is TimetableWidgetItem.Error -> getErrorItemRemoteView(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,9 +153,11 @@ class TimetableWidgetFactory(
|
|||||||
|
|
||||||
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
|
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
|
||||||
val lessonEndTime = lesson.end.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 {
|
val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
||||||
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
|
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
|
||||||
|
setViewVisibility(R.id.timetableWidgetItemNumber, lessonNumberVisibility)
|
||||||
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
|
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
|
||||||
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
|
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
|
||||||
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
|
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
|
||||||
@ -213,6 +218,18 @@ class TimetableWidgetFactory(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getErrorItemRemoteView(item: TimetableWidgetItem.Error): RemoteViews {
|
||||||
|
return RemoteViews(
|
||||||
|
context.packageName,
|
||||||
|
R.layout.item_widget_timetable_error
|
||||||
|
).apply {
|
||||||
|
setTextViewText(
|
||||||
|
R.id.timetable_widget_item_error_message,
|
||||||
|
context.resources.getErrorString(item.error)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateTheme() {
|
private fun updateTheme() {
|
||||||
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||||
Configuration.UI_MODE_NIGHT_YES -> {
|
Configuration.UI_MODE_NIGHT_YES -> {
|
||||||
@ -300,4 +317,8 @@ class TimetableWidgetFactory(
|
|||||||
synchronizationTime,
|
synchronizationTime,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private const val TIME_FORMAT_STYLE = "HH:mm"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) {
|
|||||||
|
|
||||||
data class Normal(
|
data class Normal(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
|
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
|
||||||
|
|
||||||
data class Empty(
|
data class Empty(
|
||||||
@ -17,10 +18,15 @@ sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) {
|
|||||||
data class Synchronized(
|
data class Synchronized(
|
||||||
val timestamp: Instant,
|
val timestamp: Instant,
|
||||||
) : TimetableWidgetItem(TimetableWidgetItemType.SYNCHRONIZED)
|
) : TimetableWidgetItem(TimetableWidgetItemType.SYNCHRONIZED)
|
||||||
|
|
||||||
|
data class Error(
|
||||||
|
val error: Throwable
|
||||||
|
) : TimetableWidgetItem(TimetableWidgetItemType.ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class TimetableWidgetItemType {
|
enum class TimetableWidgetItemType {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
EMPTY,
|
EMPTY,
|
||||||
SYNCHRONIZED,
|
SYNCHRONIZED,
|
||||||
|
ERROR,
|
||||||
}
|
}
|
||||||
|
@ -4,30 +4,31 @@ import android.content.Intent
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
|
import androidx.core.os.BundleCompat
|
||||||
import java.io.Serializable
|
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 {
|
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
|
else -> @Suppress("DEPRECATION") getSerializable(key) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Serializable> Bundle.nullableSerializable(key: String): T? = when {
|
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?
|
else -> @Suppress("DEPRECATION") getSerializable(key) as T?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <reified T : Parcelable> Bundle.parcelableArray(key: String): Array<T>? = when {
|
inline fun <reified T : Parcelable> Bundle.parcelableArray(key: String): Array<T>? =
|
||||||
Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java)
|
BundleCompat.getParcelableArray(this, key, T::class.java) as Array<T>?
|
||||||
else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array<T>?
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T : Serializable> Intent.serializable(key: String): T = when {
|
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
|
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified T : Serializable> Intent.nullableSerializable(key: String): T? = when {
|
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?
|
else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T?
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,13 @@ package io.github.wulkanowy.utils
|
|||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.AccountInactiveException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
|
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
|
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
|
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
|
||||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||||
import okhttp3.internal.http2.StreamResetException
|
import okhttp3.internal.http2.StreamResetException
|
||||||
@ -33,6 +35,8 @@ fun Resources.getErrorString(error: Throwable): String = when (error) {
|
|||||||
is ServiceUnavailableException -> R.string.error_service_unavailable
|
is ServiceUnavailableException -> R.string.error_service_unavailable
|
||||||
is FeatureDisabledException -> R.string.error_feature_disabled
|
is FeatureDisabledException -> R.string.error_feature_disabled
|
||||||
is FeatureNotAvailableException -> R.string.error_feature_not_available
|
is FeatureNotAvailableException -> R.string.error_feature_not_available
|
||||||
|
is BadCredentialsException -> R.string.error_password_invalid
|
||||||
|
is AccountInactiveException -> R.string.error_account_inactive
|
||||||
is VulcanException -> R.string.error_unknown_uonet
|
is VulcanException -> R.string.error_unknown_uonet
|
||||||
is ScrapperException -> R.string.error_unknown_app
|
is ScrapperException -> R.string.error_unknown_app
|
||||||
is CloudflareVerificationException -> R.string.error_cloudflare_captcha
|
is CloudflareVerificationException -> R.string.error_cloudflare_captcha
|
||||||
|
@ -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 {
|
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
|
// when there is only one current semester
|
||||||
singleOrNull { it.isCurrent() }?.let { return it }
|
singleOrNull { it.isCurrent() }?.let { return it }
|
||||||
|
@ -1,15 +1,31 @@
|
|||||||
package io.github.wulkanowy.utils
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import android.util.AndroidRuntimeException
|
||||||
import java.net.CookiePolicy
|
import java.net.CookiePolicy
|
||||||
import java.net.CookieStore
|
import java.net.CookieStore
|
||||||
import java.net.HttpCookie
|
import java.net.HttpCookie
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
import android.webkit.CookieManager as WebkitCookieManager
|
import android.webkit.CookieManager as WebkitCookieManager
|
||||||
import java.net.CookieManager as JavaCookieManager
|
import java.net.CookieManager as JavaCookieManager
|
||||||
|
|
||||||
class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL) {
|
@Singleton
|
||||||
|
class WebkitCookieManagerProxy @Inject constructor() :
|
||||||
|
JavaCookieManager(null, CookiePolicy.ACCEPT_ALL) {
|
||||||
|
|
||||||
private val webkitCookieManager: WebkitCookieManager = WebkitCookieManager.getInstance()
|
val webkitCookieManager: WebkitCookieManager? = getCookieManager()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [https://stackoverflow.com/a/70354583/6695449]
|
||||||
|
*/
|
||||||
|
private fun getCookieManager(): WebkitCookieManager? {
|
||||||
|
return try {
|
||||||
|
WebkitCookieManager.getInstance()
|
||||||
|
} catch (e: AndroidRuntimeException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun put(uri: URI?, responseHeaders: Map<String?, List<String?>>?) {
|
override fun put(uri: URI?, responseHeaders: Map<String?, List<String?>>?) {
|
||||||
if (uri == null || responseHeaders == null) return
|
if (uri == null || responseHeaders == null) return
|
||||||
@ -23,7 +39,7 @@ class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL
|
|||||||
|
|
||||||
// process each of the headers
|
// process each of the headers
|
||||||
for (headerValue in responseHeaders[headerKey].orEmpty()) {
|
for (headerValue in responseHeaders[headerKey].orEmpty()) {
|
||||||
webkitCookieManager.setCookie(url, headerValue)
|
webkitCookieManager?.setCookie(url, headerValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -34,7 +50,7 @@ class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL
|
|||||||
): Map<String, List<String>> {
|
): Map<String, List<String>> {
|
||||||
require(!(uri == null || requestHeaders == null)) { "Argument is null" }
|
require(!(uri == null || requestHeaders == null)) { "Argument is null" }
|
||||||
val res = mutableMapOf<String, List<String>>()
|
val res = mutableMapOf<String, List<String>>()
|
||||||
val cookie = webkitCookieManager.getCookie(uri.toString())
|
val cookie = webkitCookieManager?.getCookie(uri.toString())
|
||||||
if (cookie != null) res["Cookie"] = listOf(cookie)
|
if (cookie != null) res["Cookie"] = listOf(cookie)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
@ -50,7 +66,7 @@ class WebkitCookieManagerProxy : JavaCookieManager(null, CookiePolicy.ACCEPT_ALL
|
|||||||
cookies.remove(uri, cookie)
|
cookies.remove(uri, cookie)
|
||||||
|
|
||||||
override fun removeAll(): Boolean {
|
override fun removeAll(): Boolean {
|
||||||
webkitCookieManager.removeAllCookies(null)
|
webkitCookieManager?.removeAllCookies(null) ?: return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
Wersja 2.4.0
|
Wersja 2.5.5
|
||||||
|
|
||||||
— naprawiliśmy logowanie do aplikacji na odmianie standardowej
|
— naprawiliśmy migrację informacji o tym, czy szkoła ucznia używa eduOne
|
||||||
— naprawiliśmy wyświetlanie lekcji na kolejny dzień w kafelku na ekranie Start
|
— naprawiliśmy w końcu (teraz naprawdę mamy taką nadzieję) ten komunikat o braku uprawnień
|
||||||
— dodaliśmy oceny opisowe
|
|
||||||
— dodaliśmy kolorowe opisy we frekwencji we wpisach innych niż obecność
|
|
||||||
|
|
||||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||||
|
10
app/src/main/res/drawable/ic_circle_notification.xml
Normal file
10
app/src/main/res/drawable/ic_circle_notification.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
|
||||||
|
<solid android:color="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<size
|
||||||
|
android:width="10dp"
|
||||||
|
android:height="10dp" />
|
||||||
|
</shape>
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M14.12,10.47L12,12.59l-2.13,-2.12 -1.41,1.41L10.59,14l-2.12,2.12 1.41,1.41L12,15.41l2.12,2.12 1.41,-1.41L13.41,14l2.12,-2.12zM15.5,4l-1,-1h-5l-1,1H5v2h14V4zM6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM8,9h8v10H8V9z" />
|
||||||
|
</vector>
|
9
app/src/main/res/drawable/ic_menu_message_restore.xml
Normal file
9
app/src/main/res/drawable/ic_menu_message_restore.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4zM6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8,14L8,9h8v10L8,19v-5zM10,18h4v-4h2l-4,-4 -4,4h2z" />
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_notifications_off.xml
Normal file
5
app/src/main/res/drawable/ic_notifications_off.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="17dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="17dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M20,18.69L7.84,6.14 5.27,3.49 4,4.76l2.8,2.8v0.01c-0.52,0.99 -0.8,2.16 -0.8,3.42v5l-2,2v1h13.73l2,2L21,19.72l-1,-1.03zM12,22c1.11,0 2,-0.89 2,-2h-4c0,1.11 0.89,2 2,2zM18,14.68L18,11c0,-3.08 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68c-0.15,0.03 -0.29,0.08 -0.42,0.12 -0.1,0.03 -0.2,0.07 -0.3,0.11h-0.01c-0.01,0 -0.01,0 -0.02,0.01 -0.23,0.09 -0.46,0.2 -0.68,0.31 0,0 -0.01,0 -0.01,0.01L18,14.68z"/>
|
||||||
|
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user