Compare commits
2 Commits
master
...
feature/up
Author | SHA1 | Date | |
---|---|---|---|
7a85d13812 | |||
b978abfcbe |
2
.gitignore
vendored
2
.gitignore
vendored
@ -71,7 +71,6 @@ captures/
|
||||
.idea/deploymentTargetDropDown.xml
|
||||
.idea/deploymentTargetSelector.xml
|
||||
.idea/kotlinc.xml
|
||||
.idea/studiobot.xml
|
||||
|
||||
# Keystore files
|
||||
*.jks
|
||||
@ -128,4 +127,3 @@ google-services.json
|
||||
!app/google-services.json
|
||||
|
||||
|
||||
.idea/appInsightsSettings.xml
|
||||
|
@ -27,8 +27,8 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 177
|
||||
versionName "2.7.0"
|
||||
versionCode 150
|
||||
versionName "2.5.1"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
@ -160,8 +160,8 @@ play {
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.99d
|
||||
updatePriority = 2
|
||||
userFraction = 0.50d
|
||||
updatePriority = 1
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
@ -186,30 +186,28 @@ ext {
|
||||
android_hilt = "1.2.0"
|
||||
room = "2.6.1"
|
||||
chucker = "4.0.0"
|
||||
mockk = "1.13.11"
|
||||
coroutines = "1.8.1"
|
||||
mockk = "1.13.10"
|
||||
coroutines = "1.8.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'io.github.wulkanowy:sdk:2.7.0'
|
||||
implementation 'io.github.wulkanowy:sdk:2.5.1'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation "androidx.activity:activity-ktx:1.9.0"
|
||||
implementation "androidx.activity:activity-ktx:1.8.2"
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.7.1"
|
||||
implementation "androidx.annotation:annotation:1.8.0"
|
||||
implementation "androidx.javascriptengine:javascriptengine:1.0.0-beta01"
|
||||
implementation "androidx.fragment:fragment-ktx:1.6.2"
|
||||
implementation "androidx.annotation:annotation:1.7.1"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.2.1"
|
||||
implementation "androidx.recyclerview:recyclerview:1.3.2"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
@ -221,7 +219,7 @@ dependencies {
|
||||
implementation "androidx.work:work-runtime:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.0"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.7.0"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
@ -235,7 +233,7 @@ dependencies {
|
||||
implementation 'com.github.ncapdevi:FragNav:3.3.0'
|
||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
||||
@ -248,15 +246,15 @@ dependencies {
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||
implementation 'org.apache.commons:commons-text:1.12.0'
|
||||
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:33.0.0')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:32.7.4')
|
||||
playImplementation 'com.google.firebase:firebase-analytics'
|
||||
playImplementation 'com.google.firebase:firebase-messaging'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||
playImplementation 'com.google.firebase:firebase-config'
|
||||
|
||||
playImplementation 'com.google.android.gms:play-services-ads:22.6.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:23.0.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:review-ktx:2.0.1'
|
||||
@ -276,7 +274,7 @@ dependencies {
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.12.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.11.1'
|
||||
testImplementation "androidx.test:runner:1.5.2"
|
||||
testImplementation "androidx.test.ext:junit:1.1.5"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
|
@ -36,37 +36,6 @@
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1",
|
||||
"android_client_info": {
|
||||
"package_name": "io.github.wulkanowy"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": ""
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -3,8 +3,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-sdk tools:overrideLibrary="androidx.javascriptengine" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
@ -44,16 +42,16 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsRtl="false"
|
||||
android:theme="@style/WulkanowyTheme"
|
||||
android:resizeableActivity="true"
|
||||
tools:ignore="DataExtractionRules,UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.splash.SplashActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/WulkanowyTheme.SplashScreen"
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity">
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@ -13,8 +13,8 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import io.github.wulkanowy.data.api.services.SchoolsService
|
||||
import io.github.wulkanowy.data.api.services.WulkanowyService
|
||||
import io.github.wulkanowy.data.api.AdminMessageService
|
||||
import io.github.wulkanowy.data.api.SchoolsService
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
@ -71,7 +71,7 @@ internal class DataModule {
|
||||
okHttpClient: OkHttpClient,
|
||||
json: Json,
|
||||
appInfo: AppInfo
|
||||
): WulkanowyService = Retrofit.Builder()
|
||||
): AdminMessageService = Retrofit.Builder()
|
||||
.baseUrl(appInfo.messagesBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
@ -268,8 +267,7 @@ inline fun <DatabaseType, ApiType, OutputType> networkBoundResource(
|
||||
emit(Resource.Loading())
|
||||
|
||||
val data = query().first()
|
||||
val updatedShouldFetch = if (isEndDateReached) false else shouldFetch(data)
|
||||
if (updatedShouldFetch) {
|
||||
if (shouldFetch(data)) {
|
||||
emit(Resource.Intermediate(data))
|
||||
|
||||
try {
|
||||
|
@ -1,49 +1,25 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.javascriptengine.JavaScriptSandbox
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.scrapper.EvaluateHandler
|
||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||
import kotlinx.coroutines.guava.await
|
||||
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(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val chuckerInterceptor: ChuckerInterceptor,
|
||||
private val remoteConfig: RemoteConfigHelper,
|
||||
private val webkitCookieManagerProxy: WebkitCookieManagerProxy,
|
||||
private val studentDb: StudentDao,
|
||||
private val wulkanowyRepository: WulkanowyRepository,
|
||||
private val webkitCookieManagerProxy: WebkitCookieManagerProxy
|
||||
) {
|
||||
|
||||
private val eduOneMutex = Mutex()
|
||||
private val migrationFailedStudentIds = mutableSetOf<Long>()
|
||||
private val sandbox: ListenableFuture<JavaScriptSandbox>? =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && JavaScriptSandbox.isSupported())
|
||||
runCatching { JavaScriptSandbox.createConnectedInstanceAsync(context) }
|
||||
.onFailure { Timber.e(it) }
|
||||
.getOrNull()
|
||||
else null
|
||||
|
||||
private val sdk = Sdk().apply {
|
||||
androidVersion = Build.VERSION.RELEASE
|
||||
buildTag = Build.MODEL
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
setAdditionalCookieManager(webkitCookieManagerProxy)
|
||||
@ -52,47 +28,9 @@ class WulkanowySdkFactory @Inject constructor(
|
||||
addInterceptor(chuckerInterceptor, network = true)
|
||||
}
|
||||
|
||||
fun createBase() = sdk
|
||||
fun create() = sdk
|
||||
|
||||
suspend fun create(): Sdk {
|
||||
val mapping = wulkanowyRepository.getMapping()
|
||||
|
||||
return createBase().apply {
|
||||
if (mapping != null) {
|
||||
endpointsMapping = mapping.endpoints
|
||||
vTokenMapping = mapping.vTokens
|
||||
vHeaders = mapping.vHeaders
|
||||
responseMapping = mapping.responseMap
|
||||
vParamsEvaluation = createIsolate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createIsolate(): suspend () -> EvaluateHandler {
|
||||
return {
|
||||
val isolate = sandbox?.await()?.createIsolate()
|
||||
object : EvaluateHandler {
|
||||
override suspend fun evaluate(code: String): String? {
|
||||
return isolate?.evaluateJavaScriptAsync(code)?.await()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
isolate?.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun create(student: Student, semester: Semester? = null): Sdk {
|
||||
val overrideIsEduOne = checkEduOneAndMigrateIfNecessary(student)
|
||||
return buildSdk(student, semester, overrideIsEduOne)
|
||||
}
|
||||
|
||||
private suspend fun buildSdk(
|
||||
student: Student,
|
||||
semester: Semester?,
|
||||
isStudentEduOne: Boolean
|
||||
): Sdk {
|
||||
fun create(student: Student, semester: Semester? = null): Sdk {
|
||||
return create().apply {
|
||||
email = student.email
|
||||
password = student.password
|
||||
@ -101,7 +39,6 @@ class WulkanowySdkFactory @Inject constructor(
|
||||
studentId = student.studentId
|
||||
classId = student.classId
|
||||
emptyCookieJarInterceptor = true
|
||||
isEduOne = isStudentEduOne
|
||||
|
||||
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
||||
mobileBaseUrl = student.mobileBaseUrl
|
||||
@ -124,51 +61,4 @@ class WulkanowySdkFactory @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,12 @@
|
||||
package io.github.wulkanowy.data.api.services
|
||||
package io.github.wulkanowy.data.api
|
||||
|
||||
import io.github.wulkanowy.data.api.models.Mapping
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import retrofit2.http.GET
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
interface WulkanowyService {
|
||||
interface AdminMessageService {
|
||||
|
||||
@GET("/v1.json")
|
||||
suspend fun getAdminMessages(): List<AdminMessage>
|
||||
|
||||
@GET("/mapping4.json")
|
||||
suspend fun getMapping(): Mapping
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.data.api.services
|
||||
package io.github.wulkanowy.data.api
|
||||
|
||||
import io.github.wulkanowy.data.pojos.IntegrityRequest
|
||||
import io.github.wulkanowy.data.pojos.LoginEvent
|
@ -1,23 +0,0 @@
|
||||
package io.github.wulkanowy.data.api.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Mapping(
|
||||
|
||||
@SerialName("endpoints")
|
||||
val endpoints: Map<String, Map<String, Map<String, String>>>,
|
||||
|
||||
@SerialName("vTokens")
|
||||
val vTokens: Map<String, Map<String, Map<String, String>>>,
|
||||
|
||||
@SerialName("vTokenScheme")
|
||||
val vTokenScheme: Map<String, Map<String, String>> = emptyMap(),
|
||||
|
||||
@SerialName("vHeaders")
|
||||
val vHeaders: Map<String, Map<String, Map<String, String>>> = emptyMap(),
|
||||
|
||||
@SerialName("responseMap")
|
||||
val responseMap: Map<String, Map<String, Map<String, Map<String, String>>>> = emptyMap(),
|
||||
)
|
@ -120,7 +120,6 @@ import io.github.wulkanowy.data.db.migrations.Migration55
|
||||
import io.github.wulkanowy.data.db.migrations.Migration57
|
||||
import io.github.wulkanowy.data.db.migrations.Migration58
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
import io.github.wulkanowy.data.db.migrations.Migration63
|
||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||
@ -175,9 +174,6 @@ import javax.inject.Singleton
|
||||
AutoMigration(from = 58, to = 59),
|
||||
AutoMigration(from = 59, to = 60),
|
||||
AutoMigration(from = 60, to = 61),
|
||||
AutoMigration(from = 61, to = 62),
|
||||
AutoMigration(from = 62, to = 63, spec = Migration63::class),
|
||||
AutoMigration(from = 63, to = 64),
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
@ -186,7 +182,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 64
|
||||
const val VERSION_SCHEMA = 61
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -313,6 +309,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract val adminMessagesDao: AdminMessageDao
|
||||
|
||||
abstract val mutedMessageSendersDao: MutedMessageSendersDao
|
||||
|
||||
|
||||
abstract val gradeDescriptiveDao: GradeDescriptiveDao
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
@Dao
|
||||
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
||||
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<MobileDevice>>
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||
}
|
||||
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>>
|
||||
}
|
||||
|
@ -14,6 +14,6 @@ interface SemesterDao : BaseDao<Semester> {
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
suspend fun insertSemesters(items: List<Semester>): List<Long>
|
||||
|
||||
@Query("SELECT * FROM Semesters WHERE (student_id = :studentId AND class_id = :classId) OR (student_id = :studentId AND class_id = 0)")
|
||||
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
|
||||
suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
|
||||
}
|
||||
|
@ -9,8 +9,6 @@ import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||
import io.github.wulkanowy.data.db.entities.StudentName
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import javax.inject.Singleton
|
||||
@ -25,12 +23,6 @@ abstract class StudentDao {
|
||||
@Delete
|
||||
abstract suspend fun delete(student: Student)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentIsAuthorized: StudentIsAuthorized)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentIsEduOne: StudentIsEduOne)
|
||||
|
||||
@Update(entity = Student::class)
|
||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||
|
||||
@ -47,11 +39,11 @@ abstract class StudentDao {
|
||||
abstract suspend fun loadAll(): List<Student>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0)")
|
||||
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id")
|
||||
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Students JOIN Semesters ON (Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id) OR (Students.student_id = Semesters.student_id AND Semesters.class_id = 0) WHERE Students.id = :id")
|
||||
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id WHERE Students.id = :id")
|
||||
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
|
||||
|
||||
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||
|
@ -4,8 +4,6 @@ import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import io.github.wulkanowy.data.serializers.SafeMessageTypeEnumListSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@ -36,8 +34,6 @@ data class AdminMessage(
|
||||
|
||||
val priority: String,
|
||||
|
||||
@SerialName("messageTypes")
|
||||
@Serializable(with = SafeMessageTypeEnumListSerializer::class)
|
||||
@ColumnInfo(name = "types", defaultValue = "[]")
|
||||
val types: List<MessageType> = emptyList(),
|
||||
|
||||
|
@ -33,13 +33,7 @@ data class GradeSummary(
|
||||
@ColumnInfo(name = "points_sum")
|
||||
val pointsSum: String,
|
||||
|
||||
@ColumnInfo(name = "points_sum_all_year")
|
||||
val pointsSumAllYear: String?,
|
||||
|
||||
val average: Double,
|
||||
|
||||
@ColumnInfo(name = "average_all_year")
|
||||
val averageAllYear: Double? = null,
|
||||
val average: Double
|
||||
) {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
@ -9,8 +9,8 @@ import java.time.Instant
|
||||
@Entity(tableName = "MobileDevices")
|
||||
data class MobileDevice(
|
||||
|
||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
||||
val studentId: Int,
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
@ColumnInfo(name = "device_id")
|
||||
val deviceId: Int,
|
||||
|
@ -9,8 +9,8 @@ import java.time.LocalDate
|
||||
@Entity(tableName = "SchoolAnnouncements")
|
||||
data class SchoolAnnouncement(
|
||||
|
||||
@ColumnInfo(name = "user_login_id") // todo: change column name
|
||||
val studentId: Int,
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
@ -49,7 +49,6 @@ data class Student(
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@Deprecated("not available in VULCAN anymore")
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
@ -79,13 +78,6 @@ data class Student(
|
||||
|
||||
@ColumnInfo(name = "registration_date")
|
||||
val registrationDate: Instant,
|
||||
|
||||
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
||||
val isAuthorized: Boolean,
|
||||
|
||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||
val isEduOne: Boolean?,
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ -96,22 +88,3 @@ data class Student(
|
||||
@ColumnInfo(name = "avatar_color")
|
||||
var avatarColor = 0L
|
||||
}
|
||||
|
||||
@Entity
|
||||
data class StudentIsAuthorized(
|
||||
|
||||
@PrimaryKey
|
||||
var id: Long,
|
||||
|
||||
@ColumnInfo(name = "is_authorized", defaultValue = "NULL")
|
||||
val isAuthorized: Boolean?,
|
||||
) : Serializable
|
||||
|
||||
@Entity
|
||||
data class StudentIsEduOne(
|
||||
@PrimaryKey
|
||||
var id: Long,
|
||||
|
||||
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||
val isEduOne: Boolean?,
|
||||
) : Serializable
|
||||
|
@ -1,11 +0,0 @@
|
||||
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")
|
||||
}
|
||||
}
|
@ -4,8 +4,6 @@ enum class MessageType {
|
||||
GENERAL_MESSAGE,
|
||||
DASHBOARD_MESSAGE,
|
||||
LOGIN_MESSAGE,
|
||||
LOGIN_STUDENT_SELECT_MESSAGE,
|
||||
LOGIN_SYMBOL_MESSAGE,
|
||||
PASS_RESET_MESSAGE,
|
||||
ERROR_OVERRIDE,
|
||||
}
|
||||
|
@ -1,11 +0,0 @@
|
||||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class ShowAdditionalLessonsMode(val value: String) {
|
||||
NONE("none"),
|
||||
INLINE("inline"),
|
||||
BELOW("below");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = entries.find { it.value == value } ?: INLINE
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.LastAnnouncement as SdkLastAnnouncement
|
||||
@JvmName("mapDirectorInformationToEntities")
|
||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
studentId = student.studentId,
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
@ -19,7 +19,7 @@ fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
@JvmName("mapLastAnnouncementsToEntities")
|
||||
fun List<SdkLastAnnouncement>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
studentId = student.studentId,
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
|
@ -37,11 +37,9 @@ fun List<SdkGradeSummary>.mapToEntities(semester: Semester) = map {
|
||||
predictedGrade = it.predicted,
|
||||
finalGrade = it.final,
|
||||
pointsSum = it.pointsSum,
|
||||
pointsSumAllYear = it.pointsSumAllYear,
|
||||
proposedPoints = it.proposedPoints,
|
||||
finalPoints = it.finalPoints,
|
||||
average = it.average,
|
||||
averageAllYear = it.averageAllYear,
|
||||
average = it.average
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
||||
|
||||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||
MobileDevice(
|
||||
studentId = student.studentId,
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.createDate.toInstant(),
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
|
@ -34,19 +34,17 @@ fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
||||
error = it.error,
|
||||
students = it.subjects
|
||||
.filterIsInstance<SdkRegisterStudent>()
|
||||
.map { registerStudent ->
|
||||
.map { registerSubject ->
|
||||
RegisterStudent(
|
||||
studentId = registerStudent.studentId,
|
||||
studentName = registerStudent.studentName,
|
||||
studentSecondName = registerStudent.studentSecondName,
|
||||
studentSurname = registerStudent.studentSurname,
|
||||
className = registerStudent.className,
|
||||
classId = registerStudent.classId,
|
||||
isParent = registerStudent.isParent,
|
||||
isAuthorized = registerStudent.isAuthorized,
|
||||
isEduOne = registerStudent.isEduOne,
|
||||
semesters = registerStudent.semesters
|
||||
.mapToEntities(registerStudent.studentId),
|
||||
studentId = registerSubject.studentId,
|
||||
studentName = registerSubject.studentName,
|
||||
studentSecondName = registerSubject.studentSecondName,
|
||||
studentSurname = registerSubject.studentSurname,
|
||||
className = registerSubject.className,
|
||||
classId = registerSubject.classId,
|
||||
isParent = registerSubject.isParent,
|
||||
semesters = registerSubject.semesters
|
||||
.mapToEntities(registerSubject.studentId),
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -86,8 +84,6 @@ fun RegisterStudent.mapToStudentWithSemesters(
|
||||
password = user.password.orEmpty(),
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
isAuthorized = this.isAuthorized,
|
||||
isEduOne = this.isEduOne,
|
||||
).apply {
|
||||
avatarColor = colors.random()
|
||||
},
|
||||
|
@ -45,6 +45,4 @@ data class RegisterStudent(
|
||||
val classId: Int,
|
||||
val isParent: Boolean,
|
||||
val semesters: List<Semester>,
|
||||
val isAuthorized: Boolean,
|
||||
val isEduOne: Boolean
|
||||
) : java.io.Serializable
|
||||
|
@ -0,0 +1,34 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.api.AdminMessageService
|
||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AdminMessageRepository @Inject constructor(
|
||||
private val adminMessageService: AdminMessageService,
|
||||
private val adminMessageDao: AdminMessageDao,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getAdminMessages(): Flow<Resource<List<AdminMessage>>> =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { false },
|
||||
query = { adminMessageDao.loadAll() },
|
||||
fetch = { adminMessageService.getAdminMessages() },
|
||||
shouldFetch = { true },
|
||||
saveFetchResult = { oldItems, newItems ->
|
||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||
},
|
||||
)
|
||||
.filterNot { it is Resource.Intermediate }
|
||||
}
|
@ -6,8 +6,6 @@ import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
|
||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -20,7 +18,6 @@ import javax.inject.Singleton
|
||||
class LuckyNumberRepository @Inject constructor(
|
||||
private val luckyNumberDb: LuckyNumberDao,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val appWidgetUpdater: AppWidgetUpdater,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -29,7 +26,6 @@ class LuckyNumberRepository @Inject constructor(
|
||||
student: Student,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
isFromAppWidget: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
@ -48,9 +44,6 @@ class LuckyNumberRepository @Inject constructor(
|
||||
oldItems = listOfNotNull(oldLuckyNumber),
|
||||
newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
|
||||
)
|
||||
if (!isFromAppWidget) {
|
||||
appWidgetUpdater.updateAllAppWidgetsByProvider(LuckyNumberWidgetProvider::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -38,7 +38,7 @@ class MobileDeviceRepository @Inject constructor(
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { mobileDb.loadAll(student.studentId) },
|
||||
query = { mobileDb.loadAll(student.userLoginId) },
|
||||
fetch = {
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.getRegisteredDevices()
|
||||
|
@ -9,13 +9,11 @@ import com.fredporciuncula.flow.preferences.Preference
|
||||
import com.fredporciuncula.flow.preferences.Serializer
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.api.models.Mapping
|
||||
import io.github.wulkanowy.data.enums.AppTheme
|
||||
import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode
|
||||
import io.github.wulkanowy.data.enums.GradeColorTheme
|
||||
import io.github.wulkanowy.data.enums.GradeExpandMode
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode
|
||||
import io.github.wulkanowy.data.enums.TimetableGapsMode
|
||||
import io.github.wulkanowy.data.enums.TimetableMode
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
@ -215,12 +213,6 @@ class PreferencesRepository @Inject constructor(
|
||||
)
|
||||
)
|
||||
|
||||
val showAdditionalLessonsInPlan: ShowAdditionalLessonsMode
|
||||
get() = getString(
|
||||
R.string.pref_key_timetable_show_additional_lessons,
|
||||
R.string.pref_default_timetable_show_additional_lessons
|
||||
).let { ShowAdditionalLessonsMode.getByValue(it) }
|
||||
|
||||
val gradeSortingMode: GradeSortingMode
|
||||
get() = GradeSortingMode.getByValue(
|
||||
getString(
|
||||
@ -376,15 +368,6 @@ class PreferencesRepository @Inject constructor(
|
||||
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
||||
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
||||
|
||||
var mapping: Mapping?
|
||||
get() {
|
||||
val value = sharedPref.getString("mapping", null)
|
||||
return value?.let { json.decodeFromString(it) }
|
||||
}
|
||||
set(value) = sharedPref.edit(commit = true) {
|
||||
putString("mapping", value?.let { json.encodeToString(it) })
|
||||
}
|
||||
|
||||
init {
|
||||
if (installationId.isEmpty()) {
|
||||
installationId = UUID.randomUUID().toString()
|
||||
|
@ -37,7 +37,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
schoolAnnouncementDb.loadAll(student.studentId)
|
||||
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
},
|
||||
fetch = {
|
||||
val sdk = wulkanowySdkFactory.create(student)
|
||||
@ -57,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
)
|
||||
|
||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
||||
return schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
}
|
||||
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.data.api.services.SchoolsService
|
||||
import io.github.wulkanowy.data.api.SchoolsService
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
|
@ -64,10 +64,7 @@ class SemesterRepository @Inject constructor(
|
||||
.getSemesters()
|
||||
.mapToEntities(student.studentId)
|
||||
|
||||
if (new.isEmpty()) {
|
||||
Timber.i("Empty semester list from SDK!")
|
||||
return
|
||||
}
|
||||
if (new.isEmpty()) return Timber.i("Empty semester list!")
|
||||
|
||||
val old = semesterDb.loadAll(student.studentId, student.classId)
|
||||
semesterDb.removeOldAndSaveNew(
|
||||
|
@ -7,19 +7,16 @@ import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
||||
import io.github.wulkanowy.data.db.entities.StudentName
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.security.Scrambler
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -42,7 +39,6 @@ class StudentRepository @Inject constructor(
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getStudentsFromHebe(token, pin, symbol, "")
|
||||
.mapToPojo(null)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getUserSubjectsFromScrapper(
|
||||
email: String,
|
||||
@ -53,7 +49,6 @@ class StudentRepository @Inject constructor(
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol)
|
||||
.mapToPojo(password)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getStudentsHybrid(
|
||||
email: String,
|
||||
@ -63,7 +58,6 @@ class StudentRepository @Inject constructor(
|
||||
): RegisterUser = wulkanowySdkFactory.create()
|
||||
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||
.mapToPojo(password)
|
||||
.also { it.logErrors() }
|
||||
|
||||
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
|
||||
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
|
||||
@ -105,46 +99,6 @@ class StudentRepository @Inject constructor(
|
||||
return student
|
||||
}
|
||||
|
||||
suspend fun updateCurrentStudentAuthStatus() {
|
||||
Timber.i("Check isAuthorized: started")
|
||||
val student = getCurrentStudent()
|
||||
if (student.isAuthorized) {
|
||||
Timber.i("Check isAuthorized: already authorized")
|
||||
return
|
||||
}
|
||||
|
||||
val initializedSdk = wulkanowySdkFactory.create(student)
|
||||
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
||||
.onFailure { Timber.e(it, "Check isAuthorized: error occurred") }
|
||||
.getOrNull()
|
||||
|
||||
if (newCurrentStudent == null) {
|
||||
Timber.d("Check isAuthorized: current user is null")
|
||||
return
|
||||
}
|
||||
|
||||
val currentStudentSemesters = semesterDb.loadAll(student.studentId, student.classId)
|
||||
if (currentStudentSemesters.isEmpty()) {
|
||||
Timber.d("Check isAuthorized: apply empty semesters workaround")
|
||||
semesterDb.insertSemesters(
|
||||
items = newCurrentStudent.semesters.mapToEntities(student.studentId),
|
||||
)
|
||||
}
|
||||
|
||||
if (!newCurrentStudent.isAuthorized) {
|
||||
Timber.i("Check isAuthorized: authorization required")
|
||||
throw NoAuthorizationException()
|
||||
}
|
||||
|
||||
val studentIsAuthorized = StudentIsAuthorized(
|
||||
id = student.id,
|
||||
isAuthorized = true
|
||||
)
|
||||
|
||||
Timber.i("Check isAuthorized: already authorized, update local status")
|
||||
studentDb.update(studentIsAuthorized)
|
||||
}
|
||||
|
||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||
|
||||
@ -197,21 +151,15 @@ class StudentRepository @Inject constructor(
|
||||
wulkanowySdkFactory.create(student, semester)
|
||||
.authorizePermission(pesel)
|
||||
|
||||
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
|
||||
val wulkanowySdk = wulkanowySdkFactory.create(student, semester)
|
||||
val newCurrentApiStudent = runCatching { wulkanowySdk.getCurrentStudent() }
|
||||
.onFailure { Timber.e(it, "Can't find student with id ${student.studentId}") }
|
||||
.getOrNull() ?: return
|
||||
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
||||
val newCurrentApiStudent = wulkanowySdkFactory.create(student, semester)
|
||||
.getCurrentStudent() ?: return
|
||||
|
||||
val studentName = StudentName(
|
||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||
).apply { id = student.id }
|
||||
|
||||
studentDb.update(studentName)
|
||||
semesterDb.removeOldAndSaveNew(
|
||||
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
|
||||
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
||||
@ -224,18 +172,4 @@ class StudentRepository @Inject constructor(
|
||||
appDatabase.clearAllTables()
|
||||
}
|
||||
}
|
||||
|
||||
private fun RegisterUser.logErrors() {
|
||||
val symbolsErrors = symbols.filter { it.error != null }
|
||||
.map { it.error }
|
||||
val unitsErrors = symbols.flatMap { it.schools }
|
||||
.filter { it.error != null }
|
||||
.map { it.error }
|
||||
|
||||
(symbolsErrors + unitsErrors).forEach { error ->
|
||||
Timber.e(error, "Error occurred while fetching students")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoAuthorizationException : Exception()
|
||||
|
@ -13,8 +13,6 @@ import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
|
||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.monday
|
||||
@ -28,7 +26,6 @@ import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
@Singleton
|
||||
class TimetableRepository @Inject constructor(
|
||||
private val timetableDb: TimetableDao,
|
||||
@ -37,7 +34,6 @@ class TimetableRepository @Inject constructor(
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val appWidgetUpdater: AppWidgetUpdater,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -56,8 +52,7 @@ class TimetableRepository @Inject constructor(
|
||||
forceRefresh: Boolean,
|
||||
refreshAdditional: Boolean = false,
|
||||
notify: Boolean = false,
|
||||
timetableType: TimetableType = TimetableType.NORMAL,
|
||||
isFromAppWidget: Boolean = false
|
||||
timetableType: TimetableType = TimetableType.NORMAL
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = {
|
||||
@ -88,9 +83,6 @@ class TimetableRepository @Inject constructor(
|
||||
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
if (!isFromAppWidget) {
|
||||
appWidgetUpdater.updateAllAppWidgetsByProvider(TimetableWidgetProvider::class)
|
||||
}
|
||||
},
|
||||
filterResult = { (timetable, additional, headers) ->
|
||||
TimetableFull(
|
||||
|
@ -1,69 +0,0 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.api.models.Mapping
|
||||
import io.github.wulkanowy.data.api.services.WulkanowyService
|
||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import timber.log.Timber
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private val endDate = LocalDate.of(2024, 6, 25)
|
||||
val isEndDateReached = LocalDate.now() >= endDate
|
||||
|
||||
@Singleton
|
||||
class WulkanowyRepository @Inject constructor(
|
||||
private val wulkanowyService: WulkanowyService,
|
||||
private val adminMessageDao: AdminMessageDao,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
private val cacheKey = "mapping_refresh_key"
|
||||
|
||||
fun getAdminMessages(): Flow<Resource<List<AdminMessage>>> =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { false },
|
||||
query = { adminMessageDao.loadAll() },
|
||||
fetch = { wulkanowyService.getAdminMessages() },
|
||||
shouldFetch = { true },
|
||||
saveFetchResult = { oldItems, newItems ->
|
||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||
},
|
||||
)
|
||||
.filterNot { it is Resource.Intermediate }
|
||||
|
||||
suspend fun getMapping(): Mapping? {
|
||||
var savedMapping = preferencesRepository.mapping
|
||||
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey)
|
||||
)
|
||||
|
||||
if (savedMapping == null || isExpired) {
|
||||
fetchMapping()
|
||||
savedMapping = preferencesRepository.mapping
|
||||
}
|
||||
|
||||
return savedMapping
|
||||
}
|
||||
|
||||
suspend fun fetchMapping() {
|
||||
runCatching { wulkanowyService.getMapping() }
|
||||
.onFailure { Timber.e(it) }
|
||||
.onSuccess {
|
||||
preferencesRepository.mapping = it
|
||||
refreshHelper.updateLastRefreshTimestamp(cacheKey)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package io.github.wulkanowy.data.serializers
|
||||
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
object SafeMessageTypeEnumListSerializer : KSerializer<List<MessageType>> {
|
||||
|
||||
private val serializer = ListSerializer(String.serializer())
|
||||
|
||||
override val descriptor = serializer.descriptor
|
||||
|
||||
override fun serialize(encoder: Encoder, value: List<MessageType>) {
|
||||
encoder.encodeNotNullMark()
|
||||
serializer.serialize(encoder, value.map { it.name })
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): List<MessageType> =
|
||||
serializer.deserialize(decoder).mapNotNull { enumName ->
|
||||
MessageType.entries.find { it.name == enumName }
|
||||
}
|
||||
}
|
@ -5,14 +5,14 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import io.github.wulkanowy.data.mapResourceData
|
||||
import io.github.wulkanowy.data.repositories.AdminMessageRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetAppropriateAdminMessageUseCase @Inject constructor(
|
||||
private val wulkanowyRepository: WulkanowyRepository,
|
||||
private val adminMessageRepository: AdminMessageRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val appInfo: AppInfo
|
||||
) {
|
||||
@ -22,7 +22,7 @@ class GetAppropriateAdminMessageUseCase @Inject constructor(
|
||||
}
|
||||
|
||||
operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow<Resource<AdminMessage?>> {
|
||||
return wulkanowyRepository.getAdminMessages().mapResourceData { adminMessages ->
|
||||
return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages ->
|
||||
adminMessages
|
||||
.asSequence()
|
||||
.filter { it.isNotDismissed() }
|
||||
|
@ -59,7 +59,7 @@ class GetMailboxByStudentUseCase @Inject constructor(
|
||||
private fun String.getUnauthorizedVersion(): String {
|
||||
return normalizeStudentName().split(" ")
|
||||
.joinToString(" ") {
|
||||
it.firstOrNull()?.toString().orEmpty() + "*".repeat((it.length - 1).coerceAtLeast(0))
|
||||
it.first() + "*".repeat(it.length - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,27 +4,19 @@ import android.os.Build.VERSION.SDK_INT
|
||||
import android.os.Build.VERSION_CODES.O
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.work.*
|
||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||
import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.NetworkType.CONNECTED
|
||||
import androidx.work.NetworkType.UNMETERED
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import io.github.wulkanowy.services.sync.channels.Channel
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import timber.log.Timber
|
||||
import java.time.LocalDate.now
|
||||
import java.util.concurrent.TimeUnit.MINUTES
|
||||
@ -42,9 +34,7 @@ class SyncManager @Inject constructor(
|
||||
) {
|
||||
|
||||
init {
|
||||
if (now().isHolidays || isEndDateReached) {
|
||||
stopSyncWorker()
|
||||
}
|
||||
if (now().isHolidays) stopSyncWorker()
|
||||
|
||||
if (SDK_INT >= O) {
|
||||
channels.forEach { it.create() }
|
||||
@ -60,7 +50,7 @@ class SyncManager @Inject constructor(
|
||||
}
|
||||
|
||||
fun startPeriodicSyncWorker(restart: Boolean = false) {
|
||||
if (preferencesRepository.isServiceEnabled && !now().isHolidays && isEndDateReached) {
|
||||
if (preferencesRepository.isServiceEnabled && !now().isHolidays) {
|
||||
val serviceInterval = preferencesRepository.servicesInterval
|
||||
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
@ -80,10 +70,6 @@ class SyncManager @Inject constructor(
|
||||
|
||||
// if quiet, no notifications will be sent
|
||||
fun startOneTimeSyncWorker(quiet: Boolean = false): Flow<WorkInfo?> {
|
||||
if (isEndDateReached) {
|
||||
return flowOf(null)
|
||||
}
|
||||
|
||||
val work = OneTimeWorkRequestBuilder<SyncWorker>()
|
||||
.setInputData(
|
||||
Data.Builder()
|
||||
|
@ -15,10 +15,8 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureUnavailableException
|
||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||
import io.github.wulkanowy.services.sync.works.Work
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
@ -43,16 +41,13 @@ class SyncWorker @AssistedInject constructor(
|
||||
override suspend fun doWork(): Result = withContext(dispatchersProvider.io) {
|
||||
Timber.i("SyncWorker is starting")
|
||||
|
||||
if (!studentRepository.isCurrentStudentSet() || isEndDateReached) {
|
||||
return@withContext Result.failure()
|
||||
}
|
||||
if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure()
|
||||
|
||||
val (student, semester) = try {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||
student to semester
|
||||
} catch (e: Throwable) {
|
||||
Timber.e(e)
|
||||
return@withContext getResultFromErrors(listOf(e))
|
||||
}
|
||||
|
||||
@ -64,7 +59,7 @@ class SyncWorker @AssistedInject constructor(
|
||||
null
|
||||
} catch (e: Throwable) {
|
||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException || e is FeatureUnavailableException) {
|
||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException) {
|
||||
null
|
||||
} else {
|
||||
Timber.e(e)
|
||||
@ -94,7 +89,6 @@ class SyncWorker @AssistedInject constructor(
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
errors.isNotEmpty() -> Result.retry()
|
||||
else -> {
|
||||
preferencesRepository.lasSyncDate = Instant.now()
|
||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.ui.base
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||
import io.github.wulkanowy.data.repositories.NoAuthorizationException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.AuthorizationRequiredException
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.CloudflareVerificationException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.BadCredentialsException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
|
||||
@ -40,7 +40,7 @@ open class ErrorHandler @Inject constructor(@ApplicationContext protected val co
|
||||
is ScramblerException -> onDecryptionFailed()
|
||||
is BadCredentialsException -> onExpiredCredentials()
|
||||
is NoCurrentStudentException -> onNoCurrentStudent()
|
||||
is NoAuthorizationException -> onAuthorizationRequired()
|
||||
is AuthorizationRequiredException -> onAuthorizationRequired()
|
||||
is CloudflareVerificationException -> onCaptchaVerificationRequired(error.originalUrl)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.calculator
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -12,9 +9,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.AttendanceData
|
||||
import io.github.wulkanowy.databinding.FragmentAttendanceCalculatorBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.settings.appearance.AppearanceFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
@ -38,12 +33,6 @@ class AttendanceCalculatorFragment :
|
||||
|
||||
override val isViewEmpty get() = attendanceCalculatorAdapter.items.isEmpty()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentAttendanceCalculatorBinding.bind(view)
|
||||
@ -51,19 +40,6 @@ class AttendanceCalculatorFragment :
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_attendance_calculator, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.attendance_calculator_menu_settings) presenter.onSettingsSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
override fun openSettingsView() {
|
||||
(activity as? MainActivity)?.pushView(AppearanceFragment.withFocusedPreference(getString(R.string.pref_key_attendance_target)))
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(binding.attendanceCalculatorRecycler) {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
@ -74,11 +50,7 @@ class AttendanceCalculatorFragment :
|
||||
with(binding) {
|
||||
attendanceCalculatorSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
attendanceCalculatorSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
attendanceCalculatorSwipe.setProgressBackgroundColorSchemeColor(
|
||||
requireContext().getThemeAttrColor(
|
||||
R.attr.colorSwipeRefresh
|
||||
)
|
||||
)
|
||||
attendanceCalculatorSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
attendanceCalculatorErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
attendanceCalculatorErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
|
@ -1,11 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.calculator
|
||||
|
||||
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.onResourceIntermediate
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.domain.attendance.GetAttendanceCalculatorDataUseCase
|
||||
@ -86,9 +81,4 @@ class AttendanceCalculatorPresenter @Inject constructor(
|
||||
} else showError(message, error)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSettingsSelected(): Boolean {
|
||||
view?.openSettingsView()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,4 @@ interface AttendanceCalculatorView : BaseView {
|
||||
fun updateData(data: List<AttendanceData>)
|
||||
|
||||
fun clearView()
|
||||
|
||||
fun openSettingsView()
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AuthPresenter @Inject constructor(
|
||||
@ -27,12 +26,8 @@ class AuthPresenter @Inject constructor(
|
||||
|
||||
private fun loadName() {
|
||||
presenterScope.launch {
|
||||
runCatching {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.studentName
|
||||
.replace(" ", "\u00A0")
|
||||
}
|
||||
.onSuccess { view?.showDescriptionWithName(it) }
|
||||
runCatching { studentRepository.getCurrentStudent(false) }
|
||||
.onSuccess { view?.showDescriptionWithName(it.studentName) }
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
}
|
||||
}
|
||||
@ -62,9 +57,8 @@ class AuthPresenter @Inject constructor(
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
||||
Timber.d("Auth succeed: $isSuccess")
|
||||
if (isSuccess) {
|
||||
studentRepository.refreshStudentAfterAuthorize(student, semester)
|
||||
studentRepository.refreshStudentName(student, semester)
|
||||
}
|
||||
isSuccess
|
||||
}
|
||||
@ -74,7 +68,6 @@ class AuthPresenter @Inject constructor(
|
||||
view?.showContent(true)
|
||||
}
|
||||
.onSuccess {
|
||||
Timber.d("Auth fully succeed: $it")
|
||||
if (it) {
|
||||
view?.showSuccess(true)
|
||||
view?.showContent(false)
|
||||
|
@ -59,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment<DialogCaptchaBinding>() {
|
||||
webView = this
|
||||
with(settings) {
|
||||
javaScriptEnabled = true
|
||||
userAgentString = wulkanowySdkFactory.createBase().userAgent
|
||||
userAgentString = wulkanowySdkFactory.create().userAgent
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
|
@ -30,7 +30,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.notificationscenter.NotificationsCenterFragment
|
||||
import io.github.wulkanowy.ui.modules.panicmode.PanicModeFragment
|
||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
@ -126,7 +125,6 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
mainActivity.pushView(ConferenceFragment.newInstance())
|
||||
}
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected
|
||||
onPanicButtonClickListener = presenter::onPanicButtonClicked
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed
|
||||
|
||||
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
@ -210,11 +208,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
binding = binding.dashboardErrorAdminMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
onPanicButtonClickListener = presenter::onPanicButtonClicked,
|
||||
).bind(
|
||||
item = adminMessageItem.adminMessage,
|
||||
showPanicButton = true,
|
||||
)
|
||||
).bind(adminMessageItem.adminMessage)
|
||||
}
|
||||
}
|
||||
|
||||
@ -242,10 +236,6 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
requireContext().openInternetBrowser(url)
|
||||
}
|
||||
|
||||
override fun openPanicWebView(url: String) {
|
||||
(requireActivity() as MainActivity).pushView(PanicModeFragment.newInstance(url))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
dashboardAdapter.clearTimers()
|
||||
presenter.onDetachView()
|
||||
|
@ -11,7 +11,6 @@ import io.github.wulkanowy.data.errorOrNull
|
||||
import io.github.wulkanowy.data.flatResourceFlow
|
||||
import io.github.wulkanowy.data.mapResourceData
|
||||
import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
|
||||
import io.github.wulkanowy.data.repositories.ConferenceRepository
|
||||
import io.github.wulkanowy.data.repositories.ExamRepository
|
||||
@ -24,7 +23,6 @@ import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||
import io.github.wulkanowy.domain.timetable.IsStudentHasLessonsOnWeekendUseCase
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -46,7 +44,6 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import timber.log.Timber
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
@ -285,22 +282,6 @@ class DashboardPresenter @Inject constructor(
|
||||
url?.let { view?.openInternetBrowser(it) }
|
||||
}
|
||||
|
||||
fun onPanicButtonClicked() {
|
||||
resourceFlow { studentRepository.getCurrentStudent() }
|
||||
.onResourceError { errorHandler.dispatch(it) }
|
||||
.onResourceSuccess {
|
||||
val baseUrl = it.scrapperBaseUrl.toHttpUrl()
|
||||
val urlToOpen = baseUrl.newBuilder()
|
||||
.host("uonetplus${it.scrapperDomainSuffix}.${baseUrl.host}")
|
||||
.addPathSegment(it.symbol)
|
||||
.build()
|
||||
.toString()
|
||||
|
||||
view?.openPanicWebView(urlToOpen)
|
||||
}
|
||||
.launch("panic_button")
|
||||
}
|
||||
|
||||
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
|
||||
flow {
|
||||
val selectedTiles = selectedDashboardTiles
|
||||
|
@ -31,6 +31,4 @@ interface DashboardView : BaseView {
|
||||
fun openNotificationsCenterView()
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
|
||||
fun openPanicWebView(url: String)
|
||||
}
|
||||
|
@ -59,8 +59,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
|
||||
var onAdminMessageClickListener: (String?) -> Unit = {}
|
||||
|
||||
var onPanicButtonClickListener: () -> Unit = {}
|
||||
|
||||
var onAdminMessageDismissClickListener: (AdminMessage) -> Unit = {}
|
||||
|
||||
val items = mutableListOf<DashboardItem>()
|
||||
@ -88,46 +86,35 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
DashboardItem.Type.ACCOUNT.ordinal -> AccountViewHolder(
|
||||
ItemDashboardAccountBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.HORIZONTAL_GROUP.ordinal -> HorizontalGroupViewHolder(
|
||||
ItemDashboardHorizontalGroupBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.GRADES.ordinal -> GradesViewHolder(
|
||||
ItemDashboardGradesBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.LESSONS.ordinal -> LessonsViewHolder(
|
||||
ItemDashboardLessonsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.HOMEWORK.ordinal -> HomeworkViewHolder(
|
||||
ItemDashboardHomeworkBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.ANNOUNCEMENTS.ordinal -> AnnouncementsViewHolder(
|
||||
ItemDashboardAnnouncementsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.EXAMS.ordinal -> ExamsViewHolder(
|
||||
ItemDashboardExamsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.CONFERENCES.ordinal -> ConferencesViewHolder(
|
||||
ItemDashboardConferencesBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder(
|
||||
ItemDashboardAdminMessageBinding.inflate(inflater, parent, false),
|
||||
onAdminMessageDismissClickListener = onAdminMessageDismissClickListener,
|
||||
onAdminMessageClickListener = onAdminMessageClickListener,
|
||||
onPanicButtonClickListener = onPanicButtonClickListener,
|
||||
)
|
||||
|
||||
DashboardItem.Type.ADS.ordinal -> AdsViewHolder(
|
||||
ItemDashboardAdsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
@ -142,11 +129,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
is AnnouncementsViewHolder -> bindAnnouncementsViewHolder(holder, position)
|
||||
is ExamsViewHolder -> bindExamsViewHolder(holder, position)
|
||||
is ConferencesViewHolder -> bindConferencesViewHolder(holder, position)
|
||||
is AdminMessageViewHolder -> holder.bind(
|
||||
(items[position] as DashboardItem.AdminMessages).adminMessage,
|
||||
showPanicButton = true
|
||||
)
|
||||
|
||||
is AdminMessageViewHolder -> holder.bind((items[position] as DashboardItem.AdminMessages).adminMessage)
|
||||
is AdsViewHolder -> bindAdsViewHolder(holder, position)
|
||||
}
|
||||
}
|
||||
@ -257,15 +240,12 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
attendancePercentage == null || attendancePercentage == .0 -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
}
|
||||
|
||||
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
}
|
||||
|
||||
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorTimetableChange)
|
||||
}
|
||||
|
||||
else -> root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
}
|
||||
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
|
||||
@ -356,28 +336,24 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
binding.dashboardLessonsItemTitleTomorrow.isVisible = false
|
||||
binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false
|
||||
}
|
||||
|
||||
tomorrowTimetable.isNotEmpty() -> {
|
||||
dateToNavigate = tomorrowDate
|
||||
updateLessonView(item, tomorrowTimetable, binding)
|
||||
binding.dashboardLessonsItemTitleTomorrow.isVisible = true
|
||||
binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false
|
||||
}
|
||||
|
||||
currentDayHeader != null && currentDayHeader.content.isNotBlank() -> {
|
||||
dateToNavigate = currentDate
|
||||
updateLessonView(item, emptyList(), binding, currentDayHeader)
|
||||
binding.dashboardLessonsItemTitleTomorrow.isVisible = false
|
||||
binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false
|
||||
}
|
||||
|
||||
tomorrowDayHeader != null && tomorrowDayHeader.content.isNotBlank() -> {
|
||||
dateToNavigate = tomorrowDate
|
||||
updateLessonView(item, emptyList(), binding, tomorrowDayHeader)
|
||||
binding.dashboardLessonsItemTitleTomorrow.isVisible = true
|
||||
binding.dashboardLessonsItemTitleTodayAndTomorrow.isVisible = false
|
||||
}
|
||||
|
||||
else -> {
|
||||
dateToNavigate = currentDate
|
||||
updateLessonView(item, emptyList(), binding)
|
||||
@ -485,7 +461,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
firstTitleText =
|
||||
context.getString(R.string.dashboard_timetable_first_lesson_title_moment)
|
||||
}
|
||||
|
||||
minutesToStartLesson < 240 -> {
|
||||
firstTitleAndValueTextColor =
|
||||
context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
@ -493,7 +468,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
firstTitleText =
|
||||
context.getString(R.string.dashboard_timetable_first_lesson_title_soon)
|
||||
}
|
||||
|
||||
else -> {
|
||||
firstTitleAndValueTextColor =
|
||||
context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
|
@ -13,10 +13,9 @@ class AdminMessageViewHolder(
|
||||
private val binding: ItemDashboardAdminMessageBinding,
|
||||
private val onAdminMessageDismissClickListener: (AdminMessage) -> Unit,
|
||||
private val onAdminMessageClickListener: (String?) -> Unit,
|
||||
private val onPanicButtonClickListener: () -> Unit,
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(item: AdminMessage?, showPanicButton: Boolean = false) {
|
||||
fun bind(item: AdminMessage?) {
|
||||
item ?: return
|
||||
|
||||
val context = binding.root.context
|
||||
@ -49,14 +48,10 @@ class AdminMessageViewHolder(
|
||||
dashboardAdminMessageItemClose.setOnClickListener {
|
||||
onAdminMessageDismissClickListener(item)
|
||||
}
|
||||
dashboardPanicSection.root.isVisible = showPanicButton
|
||||
dashboardPanicSection.dashboardPanicButton.setOnClickListener {
|
||||
onPanicButtonClickListener()
|
||||
}
|
||||
|
||||
dashboardAdminMessage.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) })
|
||||
root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) })
|
||||
item.destinationUrl?.let { url ->
|
||||
dashboardAdminMessage.setOnClickListener { onAdminMessageClickListener(url) }
|
||||
root.setOnClickListener { onAdminMessageClickListener(url) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,5 @@ private fun generateSummary(subject: String, predicted: String, final: String) =
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
average = .0,
|
||||
pointsSumAllYear = null,
|
||||
averageAllYear = null,
|
||||
average = .0
|
||||
)
|
||||
|
@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
|
||||
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
|
||||
subject = subject,
|
||||
content = content,
|
||||
studentId = 0,
|
||||
userLoginId = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
|
@ -1,31 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.end
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.View
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.text.HtmlCompat
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.FragmentEndBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class EndFragment : BaseFragment<FragmentEndBinding>(R.layout.fragment_end), EndView {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentEndBinding.bind(view)
|
||||
|
||||
requireActivity().onBackPressedDispatcher.addCallback {
|
||||
requireActivity().finishAffinity()
|
||||
}
|
||||
|
||||
binding.endClose.setOnClickListener { requireActivity().finishAffinity() }
|
||||
|
||||
val message = getString(R.string.end_message)
|
||||
binding.endDescription.movementMethod = LinkMovementMethod.getInstance()
|
||||
binding.endDescription.text =
|
||||
HtmlCompat.fromHtml(message, HtmlCompat.FROM_HTML_MODE_COMPACT)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.end
|
||||
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface EndView : BaseView
|
@ -266,9 +266,7 @@ class GradeAverageProvider @Inject constructor(
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
pointsSumAllYear = null,
|
||||
average = .0,
|
||||
averageAllYear = null,
|
||||
average = .0
|
||||
)
|
||||
}
|
||||
|
||||
@ -296,15 +294,13 @@ class GradeAverageProvider @Inject constructor(
|
||||
proposedPoints = "",
|
||||
finalPoints = "",
|
||||
pointsSum = "",
|
||||
pointsSumAllYear = null,
|
||||
average = when {
|
||||
calcAverage -> details
|
||||
.updateModifiers(student, params)
|
||||
.calcAverage(isOptionalArithmeticAverage = params.isOptionalArithmeticAverage)
|
||||
|
||||
else -> .0
|
||||
},
|
||||
averageAllYear = null,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.INVISIBLE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -30,6 +31,14 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
@Inject
|
||||
lateinit var presenter: GradePresenter
|
||||
|
||||
private val pagerAdapter by lazy {
|
||||
BaseFragmentPagerAdapter(
|
||||
fragmentManager = childFragmentManager,
|
||||
pagesCount = 3,
|
||||
lifecycle = lifecycle,
|
||||
)
|
||||
}
|
||||
|
||||
private var semesterSwitchMenu: MenuItem? = null
|
||||
|
||||
companion object {
|
||||
@ -43,8 +52,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
|
||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||
|
||||
private var pagerAdapter: BaseFragmentPagerAdapter? = null
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -64,26 +71,13 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
with(binding) {
|
||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun initTabs(pageCount: Int) {
|
||||
pagerAdapter = BaseFragmentPagerAdapter(
|
||||
lifecycle = lifecycle,
|
||||
pagesCount = pageCount,
|
||||
fragmentManager = childFragmentManager
|
||||
)
|
||||
|
||||
with(binding.gradeViewPager) {
|
||||
adapter = pagerAdapter
|
||||
offscreenPageLimit = 3
|
||||
setOnSelectPageListener(presenter::onPageSelected)
|
||||
}
|
||||
|
||||
with(pagerAdapter!!) {
|
||||
with(pagerAdapter) {
|
||||
containerId = binding.gradeViewPager.id
|
||||
titleFactory = {
|
||||
when (it) {
|
||||
@ -105,6 +99,11 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
||||
|
||||
with(binding) {
|
||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@ -170,20 +169,19 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
||||
}
|
||||
|
||||
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||
?.onParentLoadData(semesterId, forceRefresh)
|
||||
}
|
||||
|
||||
override fun notifyChildParentReselected(index: Int) {
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||
}
|
||||
|
||||
override fun notifyChildSemesterChange(index: Int) {
|
||||
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerAdapter = null
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
@ -22,8 +22,11 @@ class GradePresenter @Inject constructor(
|
||||
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
||||
|
||||
private var selectedIndex = 0
|
||||
|
||||
private var schoolYear = 0
|
||||
private var availableSemesters = emptyList<Semester>()
|
||||
|
||||
private var semesters = emptyList<Semester>()
|
||||
|
||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||
|
||||
private lateinit var lastError: Throwable
|
||||
@ -37,7 +40,7 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onCreateMenu() {
|
||||
if (availableSemesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||
if (semesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||
}
|
||||
|
||||
fun onViewReselected() {
|
||||
@ -46,8 +49,8 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onSemesterSwitch(): Boolean {
|
||||
if (availableSemesters.isNotEmpty()) {
|
||||
view?.showSemesterDialog(selectedIndex - 1, availableSemesters.take(2))
|
||||
if (semesters.isNotEmpty()) {
|
||||
view?.showSemesterDialog(selectedIndex - 1, semesters.take(2))
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -80,7 +83,7 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPageSelected(index: Int) {
|
||||
if (availableSemesters.isNotEmpty()) loadChild(index)
|
||||
if (semesters.isNotEmpty()) loadChild(index)
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
@ -98,24 +101,16 @@ class GradePresenter @Inject constructor(
|
||||
private fun loadData() {
|
||||
resourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semesters = semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||
|
||||
student to semesters
|
||||
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||
}
|
||||
.logResourceStatus("load grade data")
|
||||
.onResourceData { (student, semesters) ->
|
||||
val currentSemester = semesters.getCurrentOrLast()
|
||||
selectedIndex =
|
||||
if (selectedIndex == 0) currentSemester.semesterName else selectedIndex
|
||||
schoolYear = currentSemester.schoolYear
|
||||
availableSemesters = semesters.filter { semester ->
|
||||
semester.diaryId == currentSemester.diaryId
|
||||
}
|
||||
|
||||
.onResourceData {
|
||||
val current = it.getCurrentOrLast()
|
||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
||||
schoolYear = current.schoolYear
|
||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
||||
view?.setCurrentSemesterName(current.semesterName, schoolYear)
|
||||
view?.run {
|
||||
initTabs(if (student.isEduOne == true) 2 else 3)
|
||||
setCurrentSemesterName(currentSemester.semesterName, schoolYear)
|
||||
|
||||
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
||||
loadChild(currentPageIndex)
|
||||
showErrorView(false)
|
||||
@ -136,10 +131,10 @@ class GradePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${availableSemesters.joinToString { it.semesterName.toString() }}")
|
||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}")
|
||||
|
||||
val newSelectedSemesterId = try {
|
||||
availableSemesters.first { it.semesterName == selectedIndex }.semesterId
|
||||
semesters.first { it.semesterName == selectedIndex }.semesterId
|
||||
} catch (e: NoSuchElementException) {
|
||||
Timber.e(e, "Selected semester no exists")
|
||||
return
|
||||
|
@ -9,8 +9,6 @@ interface GradeView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun initTabs(pageCount: Int)
|
||||
|
||||
fun showContent(show: Boolean)
|
||||
|
||||
fun showProgress(show: Boolean)
|
||||
|
@ -96,11 +96,9 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
ViewType.HEADER.id -> HeaderViewHolder(
|
||||
HeaderGradeDetailsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
ViewType.ITEM.id -> ItemViewHolder(
|
||||
ItemGradeDetailsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
@ -112,7 +110,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
header = items[position].value as GradeDetailsHeader,
|
||||
position = position
|
||||
)
|
||||
|
||||
is ItemViewHolder -> bindItemViewHolder(
|
||||
holder = holder,
|
||||
grade = items[position].value as Grade
|
||||
@ -136,10 +133,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
maxLines = if (expandedPositions[headerPosition]) 2 else 1
|
||||
}
|
||||
gradeHeaderAverage.text = formatAverage(header.average, root.context.resources)
|
||||
with(gradeHeaderAverageAllYear) {
|
||||
isVisible = header.averageAllYear != null && header.averageAllYear != .0
|
||||
text = formatAverageAllYear(header.averageAllYear, root.context.resources)
|
||||
}
|
||||
gradeHeaderPointsSum.text =
|
||||
context.getString(R.string.grade_points_sum, header.pointsSum)
|
||||
gradeHeaderPointsSum.isVisible = !header.pointsSum.isNullOrEmpty()
|
||||
@ -240,13 +233,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
||||
resources.getString(R.string.grade_average, average)
|
||||
}
|
||||
|
||||
private fun formatAverageAllYear(average: Double?, resources: Resources) =
|
||||
if (average == null || average == .0) {
|
||||
resources.getString(R.string.grade_no_average)
|
||||
} else {
|
||||
resources.getString(R.string.grade_average_year, average)
|
||||
}
|
||||
|
||||
private class HeaderViewHolder(val binding: HeaderGradeDetailsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
|
@ -13,7 +13,6 @@ data class GradeDetailsItem(
|
||||
data class GradeDetailsHeader(
|
||||
val subject: String,
|
||||
val average: Double?,
|
||||
val averageAllYear: Double?,
|
||||
val pointsSum: String?,
|
||||
val grades: List<GradeDetailsItem>
|
||||
) {
|
||||
|
@ -226,9 +226,8 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
GradeDetailsHeader(
|
||||
subject = gradeSubject.subject,
|
||||
average = gradeSubject.average,
|
||||
averageAllYear = gradeSubject.summary.averageAllYear,
|
||||
pointsSum = gradeSubject.points,
|
||||
grades = subItems,
|
||||
grades = subItems
|
||||
).apply {
|
||||
newGrades = gradeSubject.grades.filter { grade -> !grade.isRead }.size
|
||||
}, ViewType.HEADER
|
||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.grade.summary
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
@ -66,55 +65,37 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
val gradeSummaries = items
|
||||
.filter { it.gradeDescriptive == null }
|
||||
.map { it.gradeSummary }
|
||||
val isSecondSemester = items.any { item ->
|
||||
item.gradeSummary.let { it.averageAllYear != null && it.averageAllYear != .0 }
|
||||
}
|
||||
|
||||
val context = binding.root.context
|
||||
val finalItemsCount = gradeSummaries.count { isGradeValid(it.finalGrade) }
|
||||
val calculatedSemesterItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
|
||||
val calculatedAnnualItemsCount =
|
||||
gradeSummaries.count { value -> value.averageAllYear != 0.0 }
|
||||
val calculatedItemsCount = gradeSummaries.count { value -> value.average != 0.0 }
|
||||
val allItemsCount = gradeSummaries.count { !it.subject.equals("zachowanie", true) }
|
||||
val finalAverage = gradeSummaries.calcFinalAverage(
|
||||
plusModifier = preferencesRepository.gradePlusModifier,
|
||||
minusModifier = preferencesRepository.gradeMinusModifier,
|
||||
preferencesRepository.gradePlusModifier,
|
||||
preferencesRepository.gradeMinusModifier
|
||||
)
|
||||
val calculatedSemesterAverage = gradeSummaries.filter { value -> value.average != 0.0 }
|
||||
val calculatedAverage = gradeSummaries.filter { value -> value.average != 0.0 }
|
||||
.map { values -> values.average }
|
||||
.reversed() // fix average precision
|
||||
.average()
|
||||
.let { if (it.isNaN()) 0.0 else it }
|
||||
val calculatedAnnualAverage = gradeSummaries.filter { value -> value.averageAllYear != 0.0 }
|
||||
.mapNotNull { values -> values.averageAllYear }
|
||||
.reversed() // fix average precision
|
||||
.average()
|
||||
.let { if (it.isNaN()) 0.0 else it }
|
||||
|
||||
with(binding) {
|
||||
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedSemesterAverage)
|
||||
gradeSummaryScrollableHeaderCalculatedAnnual.text =
|
||||
formatAverage(calculatedAnnualAverage)
|
||||
gradeSummaryScrollableHeaderFinal.text = formatAverage(finalAverage)
|
||||
gradeSummaryScrollableHeaderFinalSubjectCount.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
finalItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculated.text = formatAverage(calculatedAverage)
|
||||
gradeSummaryScrollableHeaderFinalSubjectCount.text =
|
||||
context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
finalItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedSubjectCount.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
calculatedSemesterItemsCount,
|
||||
calculatedItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedSubjectCountAnnual.text = context.getString(
|
||||
R.string.grade_summary_from_subjects,
|
||||
calculatedAnnualItemsCount,
|
||||
allItemsCount
|
||||
)
|
||||
gradeSummaryScrollableHeaderCalculatedAnnualContainer.isVisible = isSecondSemester
|
||||
|
||||
gradeSummaryCalculatedAverageHelp.setOnClickListener { onCalculatedHelpClickListener() }
|
||||
gradeSummaryCalculatedAverageHelpAnnual.setOnClickListener { onCalculatedHelpClickListener() }
|
||||
gradeSummaryFinalAverageHelp.setOnClickListener { onFinalHelpClickListener() }
|
||||
}
|
||||
}
|
||||
@ -126,12 +107,7 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
with(binding) {
|
||||
gradeSummaryItemTitle.text = gradeSummary.subject
|
||||
gradeSummaryItemPoints.text = gradeSummary.pointsSum
|
||||
|
||||
gradeSummaryItemAverage.text = formatAverage(gradeSummary.average, "")
|
||||
gradeSummaryItemAverageAllYear.text = gradeSummary.averageAllYear?.let {
|
||||
formatAverage(it, "")
|
||||
}
|
||||
|
||||
gradeSummaryItemPredicted.text =
|
||||
"${gradeSummary.predictedGrade} ${gradeSummary.proposedPoints}".trim()
|
||||
gradeSummaryItemFinal.text =
|
||||
@ -140,12 +116,6 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
root.context.getString(R.string.all_no_data)
|
||||
}
|
||||
|
||||
gradeSummaryItemAverageContainer.isVisible = gradeSummary.average != .0
|
||||
gradeSummaryItemAverageDivider.isVisible = gradeSummaryItemAverageContainer.isVisible
|
||||
gradeSummaryItemAverageAllYearContainer.isGone =
|
||||
gradeSummary.averageAllYear == null || gradeSummary.averageAllYear == .0
|
||||
gradeSummaryItemAverageAllYearDivider.isGone =
|
||||
gradeSummaryItemAverageAllYearContainer.isGone
|
||||
gradeSummaryItemFinalDivider.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemPredictedDivider.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemPointsDivider.isVisible = gradeDescriptive == null
|
||||
@ -153,7 +123,6 @@ class GradeSummaryAdapter @Inject constructor(
|
||||
gradeSummaryItemFinalContainer.isVisible = gradeDescriptive == null
|
||||
gradeSummaryItemDescriptiveContainer.isVisible = gradeDescriptive != null
|
||||
gradeSummaryItemPointsContainer.isVisible = gradeSummary.pointsSum.isNotBlank()
|
||||
gradeSummaryItemPointsDivider.isVisible = gradeSummaryItemPointsContainer.isVisible
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,9 +98,7 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
|
||||
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
||||
onDateSelected = {
|
||||
date = it
|
||||
if (isAdded) {
|
||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||
}
|
||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.databinding.ActivityLoginBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.end.EndFragment
|
||||
import io.github.wulkanowy.ui.modules.login.advanced.LoginAdvancedFragment
|
||||
import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
|
||||
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
|
||||
@ -116,14 +115,8 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
}
|
||||
}
|
||||
|
||||
override fun navigateToEnd() {
|
||||
openFragment(EndFragment(), clearBackStack = true)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
inAppUpdateHelper.onResume()
|
||||
presenter.updateSdkMappings()
|
||||
presenter.checkIfEnd()
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,14 @@
|
||||
package io.github.wulkanowy.ui.modules.login
|
||||
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
class LoginPresenter @Inject constructor(
|
||||
private val wulkanowyRepository: WulkanowyRepository,
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val syncManager: SyncManager
|
||||
studentRepository: StudentRepository
|
||||
) : BasePresenter<LoginView>(errorHandler, studentRepository) {
|
||||
|
||||
override fun onAttachView(view: LoginView) {
|
||||
@ -22,18 +16,4 @@ class LoginPresenter @Inject constructor(
|
||||
view.initView()
|
||||
Timber.i("Login view was initialized")
|
||||
}
|
||||
|
||||
fun updateSdkMappings() {
|
||||
presenterScope.launch {
|
||||
runCatching { wulkanowyRepository.fetchMapping() }
|
||||
.onFailure { Timber.e(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIfEnd() {
|
||||
if (isEndDateReached) {
|
||||
syncManager.stopSyncWorker()
|
||||
view?.navigateToEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,4 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
interface LoginView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun navigateToEnd()
|
||||
}
|
||||
|
@ -238,7 +238,6 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
||||
binding = binding.loginFormMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
onPanicButtonClickListener = {},
|
||||
).bind(message)
|
||||
binding.loginFormMessage.root.isVisible = message != null
|
||||
}
|
||||
|
@ -19,23 +19,19 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (LoginStudentSelectItemType.entries[viewType]) {
|
||||
return when (LoginStudentSelectItemType.values()[viewType]) {
|
||||
LoginStudentSelectItemType.EMPTY_SYMBOLS_HEADER -> EmptySymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectEmptySymbolHeaderBinding.inflate(inflater, parent, false),
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.SYMBOL_HEADER -> SymbolsHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSymbolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.SCHOOL_HEADER -> SchoolHeaderViewHolder(
|
||||
ItemLoginStudentSelectHeaderSchoolBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.STUDENT -> StudentViewHolder(
|
||||
ItemLoginStudentSelectStudentBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
LoginStudentSelectItemType.HELP -> HelpViewHolder(
|
||||
ItemLoginStudentSelectHelpBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
@ -102,11 +98,9 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
with(binding) {
|
||||
loginStudentSelectHeaderSchoolName.text = buildString {
|
||||
append(item.unit.schoolName.trim())
|
||||
if (item.unit.schoolShortName.isNotBlank()) {
|
||||
append(" (")
|
||||
append(item.unit.schoolShortName)
|
||||
append(")")
|
||||
}
|
||||
append(" (")
|
||||
append(item.unit.schoolShortName)
|
||||
append(")")
|
||||
}
|
||||
loginStudentSelectHeaderSchoolDetails.isVisible = item.unit.students.isEmpty()
|
||||
loginStudentSelectHeaderSchoolError.text = item.unit.error?.message
|
||||
@ -176,11 +170,9 @@ class LoginStudentSelectAdapter @Inject constructor() :
|
||||
oldItem is LoginStudentSelectItem.SymbolHeader && newItem is LoginStudentSelectItem.SymbolHeader -> {
|
||||
oldItem.symbol == newItem.symbol
|
||||
}
|
||||
|
||||
oldItem is LoginStudentSelectItem.Student && newItem is LoginStudentSelectItem.Student -> {
|
||||
oldItem.student == newItem.student
|
||||
}
|
||||
|
||||
else -> oldItem == newItem
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,10 @@ import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
|
||||
@ -113,20 +111,6 @@ class LoginStudentSelectFragment :
|
||||
LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog")
|
||||
}
|
||||
|
||||
override fun showAdminMessage(adminMessage: AdminMessage?) {
|
||||
AdminMessageViewHolder(
|
||||
binding = binding.loginStudentSelectAdminMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
onPanicButtonClickListener = {},
|
||||
).bind(adminMessage)
|
||||
binding.loginStudentSelectAdminMessage.root.isVisible = adminMessage != null
|
||||
}
|
||||
|
||||
override fun openInternetBrowser(url: String) {
|
||||
requireContext().openInternetBrowser(url)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
|
@ -2,24 +2,16 @@ package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import io.github.wulkanowy.data.flatResourceFlow
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.mappers.mapToStudentWithSemesters
|
||||
import io.github.wulkanowy.data.onResourceData
|
||||
import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.pojos.RegisterStudent
|
||||
import io.github.wulkanowy.data.pojos.RegisterSymbol
|
||||
import io.github.wulkanowy.data.pojos.RegisterUnit
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SchoolsRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -40,8 +32,6 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
private val syncManager: SyncManager,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val appInfo: AppInfo,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase
|
||||
) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
private var lastError: Throwable? = null
|
||||
@ -74,7 +64,6 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
this.loginData = loginData
|
||||
this.registerUser = registerUser
|
||||
loadData()
|
||||
loadAdminMessage()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
@ -98,20 +87,7 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
refreshItems()
|
||||
}
|
||||
}
|
||||
}.launch("load_data")
|
||||
}
|
||||
|
||||
private fun loadAdminMessage() {
|
||||
flatResourceFlow {
|
||||
getAppropriateAdminMessageUseCase(
|
||||
scrapperBaseUrl = registerUser.scrapperBaseUrl.orEmpty(),
|
||||
type = MessageType.LOGIN_STUDENT_SELECT_MESSAGE,
|
||||
)
|
||||
}
|
||||
.logResourceStatus("load login admin message")
|
||||
.onResourceData { view?.showAdminMessage(it) }
|
||||
.onResourceError { view?.showAdminMessage(null) }
|
||||
.launch("load_admin_message")
|
||||
}.launch()
|
||||
}
|
||||
|
||||
private fun getStudentsWithCurrentlyActiveSemesters(): List<LoginStudentSelectItem.Student> {
|
||||
@ -132,8 +108,8 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun createItems(): List<LoginStudentSelectItem> = buildList {
|
||||
val notEmptySymbols = registerUser.symbols.filter { it.shouldShowOnTop() }
|
||||
val emptySymbols = registerUser.symbols.filter { !it.shouldShowOnTop() }
|
||||
val notEmptySymbols = registerUser.symbols.filter { it.schools.isNotEmpty() }
|
||||
val emptySymbols = registerUser.symbols.filter { it.schools.isEmpty() }
|
||||
|
||||
if (emptySymbols.isNotEmpty() && notEmptySymbols.isNotEmpty() && emptySymbols.any { it.symbol == loginData.userEnteredSymbol }) {
|
||||
add(createEmptySymbolItem(emptySymbols.first { it.symbol == loginData.userEnteredSymbol }))
|
||||
@ -151,10 +127,6 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
add(helpItem)
|
||||
}
|
||||
|
||||
private fun RegisterSymbol.shouldShowOnTop(): Boolean {
|
||||
return schools.isNotEmpty() || error is StudentGraduateException
|
||||
}
|
||||
|
||||
private fun createNotEmptySymbolItems(
|
||||
notEmptySymbols: List<RegisterSymbol>,
|
||||
students: List<StudentWithSemesters>,
|
||||
@ -364,14 +336,4 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAdminMessageSelected(url: String?) {
|
||||
url?.let { view?.openInternetBrowser(it) }
|
||||
}
|
||||
|
||||
fun onAdminMessageDismissed(adminMessage: AdminMessage) {
|
||||
preferencesRepository.dismissedAdminMessageIds += adminMessage.id
|
||||
|
||||
view?.showAdminMessage(null)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
|
||||
@ -26,8 +25,4 @@ interface LoginStudentSelectView : BaseView {
|
||||
fun openDiscordInvite()
|
||||
|
||||
fun openEmail(supportInfo: LoginSupportInfo)
|
||||
|
||||
fun showAdminMessage(adminMessage: AdminMessage?)
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
}
|
||||
|
@ -9,16 +9,13 @@ import android.view.inputmethod.EditorInfo.IME_NULL
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
|
||||
@ -182,18 +179,4 @@ class LoginSymbolFragment :
|
||||
override fun openSupportDialog(supportInfo: LoginSupportInfo) {
|
||||
LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog")
|
||||
}
|
||||
|
||||
override fun showAdminMessage(adminMessage: AdminMessage?) {
|
||||
AdminMessageViewHolder(
|
||||
binding = binding.loginSymbolAdminMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
onPanicButtonClickListener = {},
|
||||
).bind(adminMessage)
|
||||
binding.loginSymbolAdminMessage.root.isVisible = adminMessage != null
|
||||
}
|
||||
|
||||
override fun openInternetBrowser(url: String) {
|
||||
requireContext().openInternetBrowser(url)
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,10 @@ package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
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.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -29,9 +21,7 @@ import javax.inject.Inject
|
||||
class LoginSymbolPresenter @Inject constructor(
|
||||
studentRepository: StudentRepository,
|
||||
private val loginErrorHandler: LoginErrorHandler,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
private var lastError: Throwable? = null
|
||||
@ -53,21 +43,6 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
clearAndFocusSymbol()
|
||||
showSoftKeyboard()
|
||||
}
|
||||
|
||||
loadAdminMessage()
|
||||
}
|
||||
|
||||
private fun loadAdminMessage() {
|
||||
flatResourceFlow {
|
||||
getAppropriateAdminMessageUseCase(
|
||||
scrapperBaseUrl = loginData.baseUrl,
|
||||
type = MessageType.LOGIN_SYMBOL_MESSAGE,
|
||||
)
|
||||
}
|
||||
.logResourceStatus("load login admin message")
|
||||
.onResourceData { view?.showAdminMessage(it) }
|
||||
.onResourceError { view?.showAdminMessage(null) }
|
||||
.launch("load_admin_message")
|
||||
}
|
||||
|
||||
fun onSymbolTextChanged() {
|
||||
@ -191,14 +166,4 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onAdminMessageSelected(url: String?) {
|
||||
url?.let { view?.openInternetBrowser(it) }
|
||||
}
|
||||
|
||||
fun onAdminMessageDismissed(adminMessage: AdminMessage) {
|
||||
preferencesRepository.dismissedAdminMessageIds += adminMessage.id
|
||||
|
||||
view?.showAdminMessage(null)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
@ -45,8 +44,4 @@ interface LoginSymbolView : BaseView {
|
||||
fun openFaqPage()
|
||||
|
||||
fun openSupportDialog(supportInfo: LoginSupportInfo)
|
||||
|
||||
fun showAdminMessage(adminMessage: AdminMessage?)
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
}
|
||||
|
@ -33,4 +33,4 @@ class LuckyNumberHistoryAdapter @Inject constructor() :
|
||||
}
|
||||
|
||||
class ItemViewHolder(val binding: ItemLuckyNumberHistoryBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
@ -14,9 +14,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.dataOrThrow
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.LuckyNumberRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import io.github.wulkanowy.data.toFirstResult
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||
@ -37,9 +35,6 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
@Inject
|
||||
lateinit var sharedPref: SharedPrefProvider
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
private const val LUCKY_NUMBER_WIDGET_MAX_SIZE = 196
|
||||
|
||||
@ -135,10 +130,6 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
private fun getLuckyNumber(studentId: Long, appWidgetId: Int) = runBlocking {
|
||||
if (isEndDateReached) {
|
||||
return@runBlocking null
|
||||
}
|
||||
|
||||
try {
|
||||
val students = studentRepository.getSavedStudents()
|
||||
val student = students.singleOrNull { it.student.id == studentId }?.student
|
||||
@ -154,11 +145,7 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
if (currentStudent != null) {
|
||||
luckyNumberRepository.getLuckyNumber(
|
||||
student = currentStudent,
|
||||
forceRefresh = false,
|
||||
isFromAppWidget = true
|
||||
)
|
||||
luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false)
|
||||
.toFirstResult()
|
||||
.dataOrThrow
|
||||
} else null
|
||||
|
@ -33,7 +33,6 @@ import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
|
||||
import io.github.wulkanowy.ui.modules.auth.AuthDialog
|
||||
import io.github.wulkanowy.ui.modules.captcha.CaptchaDialog
|
||||
import io.github.wulkanowy.ui.modules.end.EndFragment
|
||||
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
@ -139,8 +138,6 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
inAppUpdateHelper.onResume()
|
||||
presenter.updateSdkMappings()
|
||||
presenter.checkIfEnd()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
@ -364,10 +361,4 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun navigateToEnd() {
|
||||
binding.mainToolbar.isVisible = false
|
||||
pushView(EndFragment())
|
||||
onBackCallback?.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.WulkanowyRepository
|
||||
import io.github.wulkanowy.data.repositories.isEndDateReached
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -16,7 +14,6 @@ import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.account.AccountView
|
||||
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsView
|
||||
import io.github.wulkanowy.ui.modules.end.EndView
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.AdsHelper
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
@ -32,7 +29,6 @@ class MainPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val wulkanowyRepository: WulkanowyRepository,
|
||||
private val syncManager: SyncManager,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val json: Json,
|
||||
@ -77,7 +73,6 @@ class MainPresenter @Inject constructor(
|
||||
syncManager.startPeriodicSyncWorker()
|
||||
|
||||
checkAppSupport()
|
||||
updateCurrentStudentAuthStatus()
|
||||
|
||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||
Timber.i("Main view was initialized with $initDestination")
|
||||
@ -112,7 +107,6 @@ class MainPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun shouldShowBottomNavigation(destination: BaseView) = when (destination) {
|
||||
is EndView,
|
||||
is AccountView,
|
||||
is StudentInfoView,
|
||||
is AccountDetailsView -> false
|
||||
@ -197,25 +191,4 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
view?.showStudentAvatar(currentStudent)
|
||||
}
|
||||
|
||||
private fun updateCurrentStudentAuthStatus() {
|
||||
presenterScope.launch {
|
||||
runCatching { studentRepository.updateCurrentStudentAuthStatus() }
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSdkMappings() {
|
||||
presenterScope.launch {
|
||||
runCatching { wulkanowyRepository.fetchMapping() }
|
||||
.onFailure { Timber.e(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun checkIfEnd() {
|
||||
if (isEndDateReached) {
|
||||
syncManager.stopSyncWorker()
|
||||
view?.navigateToEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ interface MainView : BaseView {
|
||||
|
||||
fun openMoreDestination(destination: Destination)
|
||||
|
||||
fun navigateToEnd()
|
||||
|
||||
interface MainChildView {
|
||||
|
||||
fun onFragmentReselected()
|
||||
|
@ -27,7 +27,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||
import io.github.wulkanowy.ui.modules.message.mailboxchooser.MailboxChooserDialog
|
||||
import io.github.wulkanowy.ui.modules.message.preview.MessagePreviewFragment
|
||||
import io.github.wulkanowy.ui.modules.panicmode.PanicModeFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
@ -133,7 +132,6 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
)
|
||||
messageTabErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
messageTabErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
messageTabPanicSection.dashboardPanicButton.setOnClickListener { presenter.onPanicButtonClicked() }
|
||||
}
|
||||
|
||||
setFragmentResultListener(requireArguments().getString(MESSAGE_TAB_FOLDER_ID)!!) { _, bundle ->
|
||||
@ -285,10 +283,6 @@ class MessageTabFragment : BaseFragment<FragmentMessageTabBinding>(R.layout.frag
|
||||
)
|
||||
}
|
||||
|
||||
override fun openPanicWebView(url: String) {
|
||||
(requireActivity() as MainActivity).pushView(PanicModeFragment.newInstance(url))
|
||||
}
|
||||
|
||||
override fun hideKeyboard() {
|
||||
activity?.hideSoftInput()
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.pow
|
||||
@ -430,20 +429,4 @@ class MessageTabPresenter @Inject constructor(
|
||||
+ dateRatio.toDouble().pow(2) * 2
|
||||
).toInt()
|
||||
}
|
||||
|
||||
fun onPanicButtonClicked() {
|
||||
resourceFlow { studentRepository.getCurrentStudent() }
|
||||
.onResourceError { errorHandler.dispatch(it) }
|
||||
.onResourceSuccess {
|
||||
val baseUrl = it.scrapperBaseUrl.toHttpUrl()
|
||||
val urlToOpen = baseUrl.newBuilder()
|
||||
.host("uonetplus${it.scrapperDomainSuffix}-wiadomosciplus.${baseUrl.host}")
|
||||
.addPathSegment(it.symbol)
|
||||
.build()
|
||||
.toString()
|
||||
|
||||
view?.openPanicWebView(urlToOpen)
|
||||
}
|
||||
.launch("panic_button")
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,4 @@ interface MessageTabView : BaseView {
|
||||
fun showRecyclerBottomPadding(show: Boolean)
|
||||
|
||||
fun showMailboxChooser(mailboxes: List<Mailbox>)
|
||||
|
||||
fun openPanicWebView(url: String)
|
||||
}
|
||||
|
@ -1,99 +0,0 @@
|
||||
package io.github.wulkanowy.ui.modules.panicmode
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.os.bundleOf
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||
import io.github.wulkanowy.databinding.FragmentPanicModeBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||
import io.github.wulkanowy.utils.openInternetBrowser
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PanicModeFragment : BaseFragment<FragmentPanicModeBinding>(R.layout.fragment_panic_mode),
|
||||
MainView.TitledView {
|
||||
|
||||
@Inject
|
||||
lateinit var wulkanowySdkFactory: WulkanowySdkFactory
|
||||
|
||||
@Inject
|
||||
lateinit var webkitCookieManagerProxy: WebkitCookieManagerProxy
|
||||
|
||||
private var webView: WebView? = null
|
||||
|
||||
override val titleStringId: Int get() = R.string.panic_mode_title
|
||||
|
||||
companion object {
|
||||
|
||||
private const val PANIC_URL = "panic_mode_url"
|
||||
fun newInstance(url: String?): PanicModeFragment {
|
||||
return PanicModeFragment().apply {
|
||||
arguments = bundleOf(PANIC_URL to url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentPanicModeBinding.bind(view)
|
||||
|
||||
binding.panicModeRefresh.setOnClickListener {
|
||||
binding.panicModeWebview.loadUrl(
|
||||
binding.panicModeWebview.url ?: arguments?.getString(PANIC_URL).orEmpty()
|
||||
)
|
||||
}
|
||||
binding.panicModeBack.setOnClickListener { binding.panicModeWebview.goBack() }
|
||||
binding.panicModeHome.setOnClickListener {
|
||||
binding.panicModeWebview.loadUrl(
|
||||
arguments?.getString(PANIC_URL).orEmpty()
|
||||
)
|
||||
}
|
||||
binding.panicModeForward.setOnClickListener { binding.panicModeWebview.goForward() }
|
||||
binding.panicModeShare.setOnClickListener {
|
||||
requireContext().openInternetBrowser(
|
||||
binding.panicModeWebview.url.toString(),
|
||||
)
|
||||
}
|
||||
|
||||
val onBackPressedCallback = requireActivity().onBackPressedDispatcher
|
||||
.addCallback(viewLifecycleOwner) {
|
||||
binding.panicModeWebview.goBack()
|
||||
}
|
||||
|
||||
with(binding.panicModeWebview) {
|
||||
webView = this
|
||||
with(settings) {
|
||||
javaScriptEnabled = true
|
||||
userAgentString = wulkanowySdkFactory.createBase().userAgent
|
||||
}
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun doUpdateVisitedHistory(
|
||||
view: WebView?,
|
||||
url: String?,
|
||||
isReload: Boolean
|
||||
) {
|
||||
binding.panicModeBack.isEnabled = binding.panicModeWebview.canGoBack()
|
||||
binding.panicModeForward.isEnabled = binding.panicModeWebview.canGoForward()
|
||||
onBackPressedCallback.isEnabled = binding.panicModeWebview.canGoBack()
|
||||
}
|
||||
}
|
||||
loadUrl(arguments?.getString(PANIC_URL).orEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
webkitCookieManagerProxy.webkitCookieManager?.flush()
|
||||
webView?.destroy()
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.settings.appearance
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SeekBarPreference
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
@ -31,18 +30,9 @@ class AppearanceFragment : PreferenceFragmentCompat(),
|
||||
|
||||
override val titleStringId get() = R.string.pref_settings_appearance_title
|
||||
|
||||
companion object {
|
||||
fun withFocusedPreference(key: String) = AppearanceFragment().apply {
|
||||
arguments = bundleOf(FOCUSED_KEY to key)
|
||||
}
|
||||
|
||||
private const val FOCUSED_KEY = "focusedKey"
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
arguments?.getString(FOCUSED_KEY)?.let { scrollToPreference(it) }
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
@ -7,28 +7,27 @@ import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.databinding.ItemTimetableBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableEmptyBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableMainAdditionalBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
|
||||
import io.github.wulkanowy.utils.SyncListAdapter
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableAdapter @Inject constructor() :
|
||||
SyncListAdapter<TimetableItem, RecyclerView.ViewHolder>(Differ) {
|
||||
ListAdapter<TimetableItem, RecyclerView.ViewHolder>(differ) {
|
||||
|
||||
override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
|
||||
return when (TimetableItemType.entries[viewType]) {
|
||||
return when (TimetableItemType.values()[viewType]) {
|
||||
TimetableItemType.SMALL -> SmallViewHolder(
|
||||
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
@ -40,10 +39,6 @@ class TimetableAdapter @Inject constructor() :
|
||||
TimetableItemType.EMPTY -> EmptyViewHolder(
|
||||
ItemTimetableEmptyBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
TimetableItemType.ADDITIONAL -> AdditionalViewHolder(
|
||||
ItemTimetableMainAdditionalBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,30 +61,16 @@ class TimetableAdapter @Inject constructor() :
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Small,
|
||||
)
|
||||
|
||||
is NormalViewHolder -> bindNormalView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Normal,
|
||||
)
|
||||
|
||||
is EmptyViewHolder -> bindEmptyView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Empty,
|
||||
)
|
||||
|
||||
is AdditionalViewHolder -> bindAdditionalView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Additional,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAdditionalView(
|
||||
binding: ItemTimetableMainAdditionalBinding,
|
||||
item: TimetableItem.Additional
|
||||
) {
|
||||
with(binding) {
|
||||
timetableItemSubject.text = item.additional.subject
|
||||
timetableItemTimeStart.text = item.additional.start.toFormattedString("HH:mm")
|
||||
timetableItemTimeFinish.text = item.additional.end.toFormattedString("HH:mm")
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,7 +79,6 @@ class TimetableAdapter @Inject constructor() :
|
||||
|
||||
with(binding) {
|
||||
timetableSmallItemNumber.text = lesson.number.toString()
|
||||
timetableSmallItemNumber.isVisible = item.isLessonNumberVisible
|
||||
timetableSmallItemSubject.text = lesson.subject
|
||||
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
||||
timetableSmallItemRoom.text = lesson.room
|
||||
@ -117,7 +97,6 @@ class TimetableAdapter @Inject constructor() :
|
||||
|
||||
with(binding) {
|
||||
timetableItemNumber.text = lesson.number.toString()
|
||||
timetableItemNumber.isVisible = item.isLessonNumberVisible
|
||||
timetableItemSubject.text = lesson.subject
|
||||
timetableItemGroup.text = lesson.group
|
||||
timetableItemRoom.text = lesson.room
|
||||
@ -326,32 +305,31 @@ class TimetableAdapter @Inject constructor() :
|
||||
private class EmptyViewHolder(val binding: ItemTimetableEmptyBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class AdditionalViewHolder(val binding: ItemTimetableMainAdditionalBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
companion object {
|
||||
private val differ = object : DiffUtil.ItemCallback<TimetableItem>() {
|
||||
override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
|
||||
when {
|
||||
oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
|
||||
oldItem.lesson.start == newItem.lesson.start
|
||||
}
|
||||
|
||||
private object Differ : DiffUtil.ItemCallback<TimetableItem>() {
|
||||
override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
|
||||
when {
|
||||
oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
|
||||
oldItem.lesson.start == newItem.lesson.start
|
||||
oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> {
|
||||
oldItem.lesson.start == newItem.lesson.start
|
||||
}
|
||||
|
||||
else -> oldItem == newItem
|
||||
}
|
||||
|
||||
oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> {
|
||||
oldItem.lesson.start == newItem.lesson.start
|
||||
}
|
||||
override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) =
|
||||
oldItem == newItem
|
||||
|
||||
else -> oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) =
|
||||
oldItem == newItem
|
||||
|
||||
override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? {
|
||||
return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) {
|
||||
if (oldItem.lesson == newItem.lesson && oldItem.showGroupsInPlan == newItem.showGroupsInPlan && oldItem.timeLeft != newItem.timeLeft) {
|
||||
"time_left"
|
||||
override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? {
|
||||
return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) {
|
||||
if (oldItem.lesson == newItem.lesson && oldItem.showGroupsInPlan == newItem.showGroupsInPlan && oldItem.timeLeft != newItem.timeLeft) {
|
||||
"time_left"
|
||||
} else super.getChangePayload(oldItem, newItem)
|
||||
} else super.getChangePayload(oldItem, newItem)
|
||||
} else super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.openMaterialDatePicker
|
||||
import io.github.wulkanowy.utils.*
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -108,11 +104,8 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TimetableItem>, isDayChanged: Boolean) {
|
||||
when {
|
||||
isDayChanged -> timetableAdapter.recreate(data)
|
||||
else -> timetableAdapter.submitList(data)
|
||||
}
|
||||
override fun updateData(data: List<TimetableItem>) {
|
||||
timetableAdapter.submitList(data)
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
|
@ -1,21 +1,18 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import java.time.Duration
|
||||
|
||||
sealed class TimetableItem(val type: TimetableItemType) {
|
||||
|
||||
data class Small(
|
||||
val lesson: Timetable,
|
||||
val isLessonNumberVisible: Boolean,
|
||||
val onClick: (Timetable) -> Unit,
|
||||
) : TimetableItem(TimetableItemType.SMALL)
|
||||
|
||||
data class Normal(
|
||||
val lesson: Timetable,
|
||||
val showGroupsInPlan: Boolean,
|
||||
val isLessonNumberVisible: Boolean,
|
||||
val timeLeft: TimeLeft?,
|
||||
val onClick: (Timetable) -> Unit,
|
||||
) : TimetableItem(TimetableItemType.NORMAL)
|
||||
@ -24,10 +21,6 @@ sealed class TimetableItem(val type: TimetableItemType) {
|
||||
val numFrom: Int,
|
||||
val numTo: Int
|
||||
) : TimetableItem(TimetableItemType.EMPTY)
|
||||
|
||||
data class Additional(
|
||||
val additional: TimetableAdditional,
|
||||
) : TimetableItem(TimetableItemType.ADDITIONAL)
|
||||
}
|
||||
|
||||
data class TimeLeft(
|
||||
@ -39,6 +32,5 @@ data class TimeLeft(
|
||||
enum class TimetableItemType {
|
||||
SMALL,
|
||||
NORMAL,
|
||||
EMPTY,
|
||||
ADDITIONAL,
|
||||
EMPTY
|
||||
}
|
||||
|
@ -4,9 +4,6 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode.BELOW
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode.NONE
|
||||
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.TimetableMode
|
||||
@ -17,7 +14,6 @@ import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceIntermediate
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
@ -61,7 +57,6 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
private var initialDate: LocalDate? = null
|
||||
private var isWeekendHasLessons: Boolean = false
|
||||
private var isEduOne: Boolean = false
|
||||
|
||||
var currentDate: LocalDate? = null
|
||||
private set
|
||||
@ -85,7 +80,7 @@ class TimetablePresenter @Inject constructor(
|
||||
} else currentDate?.previousSchoolDay
|
||||
|
||||
reloadView(date ?: return)
|
||||
loadData(isDayChanged = true)
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
@ -94,7 +89,7 @@ class TimetablePresenter @Inject constructor(
|
||||
} else currentDate?.nextSchoolDay
|
||||
|
||||
reloadView(date ?: return)
|
||||
loadData(isDayChanged = true)
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun onPickDate() {
|
||||
@ -108,7 +103,7 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
fun onSwipeRefresh() {
|
||||
Timber.i("Force refreshing the timetable")
|
||||
loadData(forceRefresh = true)
|
||||
loadData(true)
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
@ -116,7 +111,7 @@ class TimetablePresenter @Inject constructor(
|
||||
showErrorView(false)
|
||||
showProgress(true)
|
||||
}
|
||||
loadData(forceRefresh = true)
|
||||
loadData(true)
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
@ -149,12 +144,11 @@ class TimetablePresenter @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadData(forceRefresh: Boolean = false, isDayChanged: Boolean = false) {
|
||||
private fun loadData(forceRefresh: Boolean = false) {
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
isEduOne = student.isEduOne == true
|
||||
checkInitialAndCurrentDate(semester)
|
||||
timetableRepository.getTimetable(
|
||||
student = student,
|
||||
@ -173,9 +167,9 @@ class TimetablePresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
showProgress(false)
|
||||
showErrorView(false)
|
||||
updateData(it, isDayChanged)
|
||||
showContent(it.lessons.isNotEmpty() || it.additional.isNotEmpty())
|
||||
showEmpty(it.lessons.isEmpty() && it.additional.isEmpty())
|
||||
showContent(it.lessons.isNotEmpty())
|
||||
showEmpty(it.lessons.isEmpty())
|
||||
updateData(it.lessons)
|
||||
setDayHeaderMessage(it.headers.find { header -> header.date == currentDate }?.content)
|
||||
reloadNavigation()
|
||||
}
|
||||
@ -220,97 +214,66 @@ class TimetablePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateData(lessons: TimetableFull, isDayChanged: Boolean) {
|
||||
private fun updateData(lessons: List<Timetable>) {
|
||||
tickTimer?.cancel()
|
||||
|
||||
view?.updateData(createItems(lessons), isDayChanged)
|
||||
if (currentDate == now()) {
|
||||
tickTimer = timer(period = 2_000, initialDelay = 2_000) {
|
||||
if (currentDate != now()) {
|
||||
view?.updateData(createItems(lessons))
|
||||
} else {
|
||||
tickTimer = timer(period = 2_000) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
view?.updateData(createItems(lessons), isDayChanged)
|
||||
view?.updateData(createItems(lessons))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class Item(
|
||||
val isStudentPlan: Boolean,
|
||||
val start: Instant,
|
||||
val number: Int?,
|
||||
) {
|
||||
class Lesson(val lesson: Timetable) :
|
||||
Item(lesson.isStudentPlan, lesson.start, lesson.number)
|
||||
|
||||
class Additional(val additional: TimetableAdditional) : Item(true, additional.start, null)
|
||||
}
|
||||
|
||||
private fun createItems(fullTimetable: TimetableFull): List<TimetableItem> {
|
||||
val showAdditionalLessonsInPlan = prefRepository.showAdditionalLessonsInPlan
|
||||
val allItems =
|
||||
fullTimetable.lessons.map(Item::Lesson) + fullTimetable.additional.map(Item::Additional)
|
||||
.takeIf { showAdditionalLessonsInPlan != NONE }.orEmpty()
|
||||
|
||||
val filteredItems = allItems.filter {
|
||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||
it.isStudentPlan
|
||||
} else true
|
||||
}.sortedWith(
|
||||
(compareBy<Item> { it is Item.Additional }
|
||||
.takeIf { showAdditionalLessonsInPlan == BELOW } ?: EmptyComparator())
|
||||
.thenBy { it.start }
|
||||
.thenBy { !it.isStudentPlan }
|
||||
)
|
||||
private fun createItems(items: List<Timetable>): List<TimetableItem> {
|
||||
val filteredItems = items
|
||||
.filter {
|
||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||
it.isStudentPlan
|
||||
} else true
|
||||
}.sortedWith(
|
||||
compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
|
||||
)
|
||||
|
||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||
else -> null
|
||||
}
|
||||
var prevIsAdditional = false
|
||||
return buildList {
|
||||
filteredItems.forEachIndexed { i, it ->
|
||||
if (prefRepository.showTimetableGaps != NO_GAPS) {
|
||||
if (prevNum != null && it.number != null && it.number > prevNum!! + 1) {
|
||||
if (!prevIsAdditional) {
|
||||
// Additional lessons do count as a lesson so don't add empty lessons
|
||||
// when there is an additional lesson present
|
||||
val emptyLesson = TimetableItem.Empty(
|
||||
numFrom = prevNum!! + 1, numTo = it.number - 1
|
||||
)
|
||||
add(emptyLesson)
|
||||
}
|
||||
}
|
||||
prevNum = it.number
|
||||
prevIsAdditional = it is Item.Additional
|
||||
if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
|
||||
val emptyLesson = TimetableItem.Empty(
|
||||
numFrom = prevNum!! + 1,
|
||||
numTo = it.number - 1
|
||||
)
|
||||
add(emptyLesson)
|
||||
}
|
||||
|
||||
if (it is Item.Lesson) {
|
||||
if (it.isStudentPlan) {
|
||||
val normalLesson = TimetableItem.Normal(
|
||||
lesson = it.lesson,
|
||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||
timeLeft = filteredItems.getTimeLeftForLesson(it.lesson, i),
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(normalLesson)
|
||||
} else {
|
||||
val smallLesson = TimetableItem.Small(
|
||||
lesson = it.lesson,
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(smallLesson)
|
||||
}
|
||||
} else if (it is Item.Additional) {
|
||||
// If the user disabled showing additional lessons, they would've been filtered
|
||||
// out already, so there's no need to check it again.
|
||||
add(TimetableItem.Additional(it.additional))
|
||||
if (it.isStudentPlan) {
|
||||
val normalLesson = TimetableItem.Normal(
|
||||
lesson = it,
|
||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||
onClick = ::onTimetableItemSelected
|
||||
)
|
||||
add(normalLesson)
|
||||
} else {
|
||||
val smallLesson = TimetableItem.Small(
|
||||
lesson = it,
|
||||
onClick = ::onTimetableItemSelected
|
||||
)
|
||||
add(smallLesson)
|
||||
}
|
||||
|
||||
prevNum = it.number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Item>.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
|
||||
private fun List<Timetable>.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
|
||||
val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index))
|
||||
return TimeLeft(
|
||||
until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil },
|
||||
@ -319,20 +282,11 @@ class TimetablePresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Item>.getPreviousLesson(position: Int): Instant? {
|
||||
val lessonAdditionalOffset = filterIndexed { i, item ->
|
||||
i < position && item is Item.Additional
|
||||
}.size
|
||||
val lessonStudentPlanOffset = filterIndexed { i, item ->
|
||||
i < position && !item.isStudentPlan
|
||||
}.size
|
||||
val lessonIndex = position - 1 - lessonAdditionalOffset - lessonStudentPlanOffset
|
||||
|
||||
return filterIsInstance<Item.Lesson>()
|
||||
.filter { it.isStudentPlan }
|
||||
.getOrNull(lessonIndex)
|
||||
private fun List<Timetable>.getPreviousLesson(position: Int): Instant? {
|
||||
return filter { it.isStudentPlan }
|
||||
.getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
|
||||
?.let {
|
||||
if (!it.lesson.canceled && it.isStudentPlan) it.lesson.end
|
||||
if (!it.canceled && it.isStudentPlan) it.end
|
||||
else null
|
||||
}
|
||||
}
|
||||
@ -385,7 +339,3 @@ class TimetablePresenter @Inject constructor(
|
||||
super.onDetachView()
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyComparator<T> : Comparator<T> {
|
||||
override fun compare(o1: T, o2: T) = 0
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ interface TimetableView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<TimetableItem>, isDayChanged: Boolean)
|
||||
fun updateData(data: List<TimetableItem>)
|
||||
|
||||
fun updateNavigationDay(date: String)
|
||||
|
||||
|
@ -13,11 +13,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.openMaterialDatePicker
|
||||
import io.github.wulkanowy.utils.*
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -136,12 +132,8 @@ class AdditionalLessonsFragment :
|
||||
binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun showAddAdditionalLessonDialog(currentDate: LocalDate) {
|
||||
(activity as? MainActivity)?.showDialogFragment(
|
||||
AdditionalLessonAddDialog.newInstance(
|
||||
currentDate
|
||||
)
|
||||
)
|
||||
override fun showAddAdditionalLessonDialog() {
|
||||
(activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance())
|
||||
}
|
||||
|
||||
override fun showDatePickerDialog(selectedDate: LocalDate) {
|
||||
|
@ -1,27 +1,14 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.additional
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
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.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||
import io.github.wulkanowy.domain.timetable.IsStudentHasLessonsOnWeekendUseCase
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.nextSchoolDay
|
||||
import io.github.wulkanowy.utils.previousSchoolDay
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.utils.*
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -35,14 +22,11 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val timetableRepository: TimetableRepository,
|
||||
private val isStudentHasLessonsOnWeekendUseCase: IsStudentHasLessonsOnWeekendUseCase,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<AdditionalLessonsView>(errorHandler, studentRepository) {
|
||||
|
||||
private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay
|
||||
|
||||
private var isWeekendHasLessons: Boolean = false
|
||||
|
||||
lateinit var currentDate: LocalDate
|
||||
private set
|
||||
|
||||
@ -59,18 +43,12 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPreviousDay() {
|
||||
val date = if (isWeekendHasLessons) {
|
||||
currentDate.minusDays(1)
|
||||
} else currentDate.previousSchoolDay
|
||||
loadData(date)
|
||||
loadData(currentDate.previousSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
val date = if (isWeekendHasLessons) {
|
||||
currentDate.plusDays(1)
|
||||
} else currentDate.nextSchoolDay
|
||||
loadData(date)
|
||||
loadData(currentDate.nextSchoolDay)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
@ -79,7 +57,7 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onAdditionalLessonAddButtonClicked() {
|
||||
view?.showAddAdditionalLessonDialog(currentDate)
|
||||
view?.showAddAdditionalLessonDialog()
|
||||
}
|
||||
|
||||
fun onDateSet(year: Int, month: Int, day: Int) {
|
||||
@ -153,8 +131,6 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(semester, currentDate)
|
||||
timetableRepository.getTimetable(
|
||||
student = student,
|
||||
semester = semester,
|
||||
|
@ -36,7 +36,7 @@ interface AdditionalLessonsView : BaseView {
|
||||
|
||||
fun showDatePickerDialog(selectedDate: LocalDate)
|
||||
|
||||
fun showAddAdditionalLessonDialog(currentDate: LocalDate)
|
||||
fun showAddAdditionalLessonDialog()
|
||||
|
||||
fun showSuccessMessage()
|
||||
|
||||
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.ui.modules.timetable.additional.add
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
@ -27,12 +26,10 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
lateinit var presenter: AdditionalLessonAddPresenter
|
||||
|
||||
companion object {
|
||||
const val ARGUMENT_KEY = "additional_lesson_default_date"
|
||||
fun newInstance(defaultDate: LocalDate) = AdditionalLessonAddDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to defaultDate.toEpochDay())
|
||||
}
|
||||
fun newInstance() = AdditionalLessonAddDialog()
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
.setView(
|
||||
@ -43,13 +40,10 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
arguments?.getLong(ARGUMENT_KEY)?.let(LocalDate::ofEpochDay)?.let {
|
||||
presenter.onDateSelected(it)
|
||||
}
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView(selectedDate: LocalDate) {
|
||||
override fun initView() {
|
||||
with(binding) {
|
||||
additionalLessonDialogStartEdit.doOnTextChanged { _, _, _, _ ->
|
||||
additionalLessonDialogStart.isErrorEnabled = false
|
||||
@ -59,7 +53,6 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
additionalLessonDialogEnd.isErrorEnabled = false
|
||||
additionalLessonDialogEnd.error = null
|
||||
}
|
||||
additionalLessonDialogDateEdit.setText(selectedDate.toFormattedString())
|
||||
additionalLessonDialogDateEdit.doOnTextChanged { _, _, _, _ ->
|
||||
additionalLessonDialogDate.isErrorEnabled = false
|
||||
additionalLessonDialogDate.error = null
|
||||
@ -68,6 +61,7 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
additionalLessonDialogContent.isErrorEnabled = false
|
||||
additionalLessonDialogContent.error = null
|
||||
}
|
||||
|
||||
additionalLessonDialogAdd.setOnClickListener {
|
||||
presenter.onAddAdditionalClicked(
|
||||
start = additionalLessonDialogStartEdit.text?.toString(),
|
||||
@ -161,9 +155,7 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
.build()
|
||||
|
||||
timePicker.addOnPositiveButtonClickListener {
|
||||
if (isAdded) {
|
||||
onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute))
|
||||
}
|
||||
onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute))
|
||||
}
|
||||
|
||||
if (!parentFragmentManager.isStateSaved) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user