mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-19 00:16:48 -06:00
Merge branch 'bugfix/2.5.3' into develop
This commit is contained in:
commit
7993366bfc
@ -27,8 +27,8 @@ android {
|
|||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 34
|
targetSdkVersion 34
|
||||||
versionCode 151
|
versionCode 152
|
||||||
versionName "2.5.2"
|
versionName "2.5.3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
@ -160,7 +160,7 @@ play {
|
|||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||||
userFraction = 0.50d
|
userFraction = 0.20d
|
||||||
updatePriority = 3
|
updatePriority = 3
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
@ -191,7 +191,7 @@ ext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'io.github.wulkanowy:sdk:2.5.2'
|
implementation 'io.github.wulkanowy:sdk:2.5.3'
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||||
|
|
||||||
|
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/63.json
Normal file
2547
app/schemas/io.github.wulkanowy.data.db.AppDatabase/63.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,15 @@
|
|||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||||
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.RemoteConfigHelper
|
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||||
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
import io.github.wulkanowy.utils.WebkitCookieManagerProxy
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -14,9 +18,12 @@ import javax.inject.Singleton
|
|||||||
class WulkanowySdkFactory @Inject constructor(
|
class WulkanowySdkFactory @Inject constructor(
|
||||||
private val chuckerInterceptor: ChuckerInterceptor,
|
private val chuckerInterceptor: ChuckerInterceptor,
|
||||||
private val remoteConfig: RemoteConfigHelper,
|
private val remoteConfig: RemoteConfigHelper,
|
||||||
private val webkitCookieManagerProxy: WebkitCookieManagerProxy
|
private val webkitCookieManagerProxy: WebkitCookieManagerProxy,
|
||||||
|
private val studentDb: StudentDao,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val eduOneMutex = Mutex()
|
||||||
|
|
||||||
private val sdk = Sdk().apply {
|
private val sdk = Sdk().apply {
|
||||||
androidVersion = android.os.Build.VERSION.RELEASE
|
androidVersion = android.os.Build.VERSION.RELEASE
|
||||||
buildTag = android.os.Build.MODEL
|
buildTag = android.os.Build.MODEL
|
||||||
@ -30,7 +37,12 @@ class WulkanowySdkFactory @Inject constructor(
|
|||||||
|
|
||||||
fun create() = sdk
|
fun create() = sdk
|
||||||
|
|
||||||
fun create(student: Student, semester: Semester? = null): Sdk {
|
suspend fun create(student: Student, semester: Semester? = null): Sdk {
|
||||||
|
val overrideIsEduOne = checkEduOneAndMigrateIfNecessary(student)
|
||||||
|
return buildSdk(student, semester, overrideIsEduOne)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildSdk(student: Student, semester: Semester?, isStudentEduOne: Boolean): Sdk {
|
||||||
return create().apply {
|
return create().apply {
|
||||||
email = student.email
|
email = student.email
|
||||||
password = student.password
|
password = student.password
|
||||||
@ -38,8 +50,8 @@ class WulkanowySdkFactory @Inject constructor(
|
|||||||
schoolSymbol = student.schoolSymbol
|
schoolSymbol = student.schoolSymbol
|
||||||
studentId = student.studentId
|
studentId = student.studentId
|
||||||
classId = student.classId
|
classId = student.classId
|
||||||
isEduOne = student.isEduOne
|
|
||||||
emptyCookieJarInterceptor = true
|
emptyCookieJarInterceptor = true
|
||||||
|
isEduOne = isStudentEduOne
|
||||||
|
|
||||||
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) {
|
||||||
mobileBaseUrl = student.mobileBaseUrl
|
mobileBaseUrl = student.mobileBaseUrl
|
||||||
@ -62,4 +74,40 @@ class WulkanowySdkFactory @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun checkEduOneAndMigrateIfNecessary(student: Student): Boolean {
|
||||||
|
if (student.isEduOne != null) return student.isEduOne
|
||||||
|
|
||||||
|
eduOneMutex.withLock {
|
||||||
|
val studentFromDatabase = studentDb.loadById(student.id)
|
||||||
|
if (studentFromDatabase?.isEduOne != null) {
|
||||||
|
Timber.d("Migration eduOne: already done")
|
||||||
|
return studentFromDatabase.isEduOne
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d("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.d("Migration eduOne: failed, so skipping")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
Timber.d("Migration eduOne: success. New isEduOne flag: ${newCurrentStudent.isEduOne}")
|
||||||
|
|
||||||
|
val studentIsEduOne = StudentIsEduOne(
|
||||||
|
id = student.id,
|
||||||
|
isEduOne = newCurrentStudent.isEduOne
|
||||||
|
)
|
||||||
|
studentDb.update(studentIsEduOne)
|
||||||
|
return newCurrentStudent.isEduOne
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,7 @@ import io.github.wulkanowy.data.db.migrations.Migration55
|
|||||||
import io.github.wulkanowy.data.db.migrations.Migration57
|
import io.github.wulkanowy.data.db.migrations.Migration57
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration58
|
import io.github.wulkanowy.data.db.migrations.Migration58
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||||
|
import io.github.wulkanowy.data.db.migrations.Migration63
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||||
@ -175,6 +176,7 @@ import javax.inject.Singleton
|
|||||||
AutoMigration(from = 59, to = 60),
|
AutoMigration(from = 59, to = 60),
|
||||||
AutoMigration(from = 60, to = 61),
|
AutoMigration(from = 60, to = 61),
|
||||||
AutoMigration(from = 61, to = 62),
|
AutoMigration(from = 61, to = 62),
|
||||||
|
AutoMigration(from = 62, to = 63, spec = Migration63::class),
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -183,7 +185,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 62
|
const val VERSION_SCHEMA = 63
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
|
@ -10,6 +10,7 @@ import androidx.room.Update
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
import io.github.wulkanowy.data.db.entities.StudentIsAuthorized
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||||
import io.github.wulkanowy.data.db.entities.StudentName
|
import io.github.wulkanowy.data.db.entities.StudentName
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -27,6 +28,9 @@ abstract class StudentDao {
|
|||||||
@Update(entity = Student::class)
|
@Update(entity = Student::class)
|
||||||
abstract suspend fun update(studentIsAuthorized: StudentIsAuthorized)
|
abstract suspend fun update(studentIsAuthorized: StudentIsAuthorized)
|
||||||
|
|
||||||
|
@Update(entity = Student::class)
|
||||||
|
abstract suspend fun update(studentIsEduOne: StudentIsEduOne)
|
||||||
|
|
||||||
@Update(entity = Student::class)
|
@Update(entity = Student::class)
|
||||||
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
abstract suspend fun update(studentNickAndAvatar: StudentNickAndAvatar)
|
||||||
|
|
||||||
|
@ -82,8 +82,8 @@ data class Student(
|
|||||||
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
||||||
val isAuthorized: Boolean,
|
val isAuthorized: Boolean,
|
||||||
|
|
||||||
@ColumnInfo(name = "is_edu_one", defaultValue = "0")
|
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||||
val isEduOne: Boolean,
|
val isEduOne: Boolean?,
|
||||||
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@ -95,3 +95,22 @@ data class Student(
|
|||||||
@ColumnInfo(name = "avatar_color")
|
@ColumnInfo(name = "avatar_color")
|
||||||
var avatarColor = 0L
|
var avatarColor = 0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class StudentIsAuthorized(
|
||||||
|
|
||||||
|
@PrimaryKey
|
||||||
|
var id: Long,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_authorized", defaultValue = "NULL")
|
||||||
|
val isAuthorized: Boolean?,
|
||||||
|
) : Serializable
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class StudentIsEduOne(
|
||||||
|
@PrimaryKey
|
||||||
|
var id: Long,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_edu_one", defaultValue = "NULL")
|
||||||
|
val isEduOne: Boolean?,
|
||||||
|
) : Serializable
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.db.entities
|
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
|
||||||
import androidx.room.Entity
|
|
||||||
import androidx.room.PrimaryKey
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
@Entity
|
|
||||||
data class StudentIsAuthorized(
|
|
||||||
@ColumnInfo(name = "is_authorized", defaultValue = "0")
|
|
||||||
val isAuthorized: Boolean,
|
|
||||||
) : Serializable {
|
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
var id: Long = 0
|
|
||||||
}
|
|
@ -0,0 +1,11 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.AutoMigrationSpec
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration63 : AutoMigrationSpec {
|
||||||
|
|
||||||
|
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||||
|
db.execSQL("UPDATE Students SET is_edu_one = NULL WHERE is_edu_one = 0")
|
||||||
|
}
|
||||||
|
}
|
@ -12,13 +12,14 @@ import io.github.wulkanowy.data.db.entities.StudentName
|
|||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.mappers.mapToPojo
|
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
|
||||||
import io.github.wulkanowy.utils.security.Scrambler
|
import io.github.wulkanowy.utils.security.Scrambler
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -101,23 +102,44 @@ class StudentRepository @Inject constructor(
|
|||||||
return student
|
return student
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkCurrentStudentAuthorizationStatus() {
|
suspend fun updateCurrentStudentAuthStatus() {
|
||||||
|
Timber.i("Check isAuthorized: started")
|
||||||
val student = getCurrentStudent()
|
val student = getCurrentStudent()
|
||||||
|
if (student.isAuthorized) {
|
||||||
if (!student.isAuthorized) {
|
Timber.i("Check isAuthorized: already authorized")
|
||||||
val currentSemester = semesterDb.loadAll(
|
return
|
||||||
studentId = student.studentId,
|
|
||||||
classId = student.classId,
|
|
||||||
).getCurrentOrLast()
|
|
||||||
val initializedSdk = wulkanowySdkFactory.create(student, currentSemester)
|
|
||||||
val isAuthorized = initializedSdk.getCurrentStudent()?.isAuthorized ?: false
|
|
||||||
|
|
||||||
if (isAuthorized) {
|
|
||||||
studentDb.update(StudentIsAuthorized(isAuthorized = true).apply {
|
|
||||||
id = student.id
|
|
||||||
})
|
|
||||||
} else throw NoAuthorizationException()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val initializedSdk = wulkanowySdkFactory.create(student)
|
||||||
|
val newCurrentStudent = runCatching { initializedSdk.getCurrentStudent() }
|
||||||
|
.onFailure { Timber.e(it, "Check isAuthorized: error occurred") }
|
||||||
|
.getOrNull()
|
||||||
|
|
||||||
|
if (newCurrentStudent == null) {
|
||||||
|
Timber.d("Check isAuthorized: current user is null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentStudentSemesters = semesterDb.loadAll(student.studentId, student.classId)
|
||||||
|
if (currentStudentSemesters.isEmpty()) {
|
||||||
|
Timber.d("Check isAuthorized: apply empty semesters workaround")
|
||||||
|
semesterDb.insertSemesters(
|
||||||
|
items = newCurrentStudent.semesters.mapToEntities(student.studentId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newCurrentStudent.isAuthorized) {
|
||||||
|
Timber.i("Check isAuthorized: authorization required")
|
||||||
|
throw NoAuthorizationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
val studentIsAuthorized = StudentIsAuthorized(
|
||||||
|
id = student.id,
|
||||||
|
isAuthorized = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Timber.i("Check isAuthorized: already authorized, update local status")
|
||||||
|
studentDb.update(studentIsAuthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||||
@ -172,15 +194,19 @@ class StudentRepository @Inject constructor(
|
|||||||
wulkanowySdkFactory.create(student, semester)
|
wulkanowySdkFactory.create(student, semester)
|
||||||
.authorizePermission(pesel)
|
.authorizePermission(pesel)
|
||||||
|
|
||||||
suspend fun refreshStudentName(student: Student, semester: Semester) {
|
suspend fun refreshStudentAfterAuthorize(student: Student, semester: Semester) {
|
||||||
val newCurrentApiStudent = wulkanowySdkFactory.create(student, semester)
|
val newCurrentApiStudent = wulkanowySdkFactory.create(student, semester)
|
||||||
.getCurrentStudent() ?: return
|
.getCurrentStudent() ?: return Timber.d("Can't find student with id ${student.studentId}")
|
||||||
|
|
||||||
val studentName = StudentName(
|
val studentName = StudentName(
|
||||||
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
studentName = "${newCurrentApiStudent.studentName} ${newCurrentApiStudent.studentSurname}"
|
||||||
).apply { id = student.id }
|
).apply { id = student.id }
|
||||||
|
|
||||||
studentDb.update(studentName)
|
studentDb.update(studentName)
|
||||||
|
semesterDb.removeOldAndSaveNew(
|
||||||
|
oldItems = semesterDb.loadAll(student.studentId, semester.classId),
|
||||||
|
newItems = newCurrentApiStudent.semesters.mapToEntities(newCurrentApiStudent.studentId)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
suspend fun deleteStudentsAssociatedWithAccount(student: Student) {
|
||||||
@ -196,4 +222,3 @@ class StudentRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NoAuthorizationException : Exception()
|
class NoAuthorizationException : Exception()
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import io.github.wulkanowy.data.repositories.SemesterRepository
|
|||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
|
||||||
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.exception.FeatureUnavailableException
|
||||||
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
import io.github.wulkanowy.services.sync.channels.DebugChannel
|
||||||
import io.github.wulkanowy.services.sync.works.Work
|
import io.github.wulkanowy.services.sync.works.Work
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
@ -48,6 +49,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||||
student to semester
|
student to semester
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
|
Timber.e(e)
|
||||||
return@withContext getResultFromErrors(listOf(e))
|
return@withContext getResultFromErrors(listOf(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
null
|
null
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException) {
|
if (e is FeatureDisabledException || e is FeatureNotAvailableException || e is FeatureUnavailableException) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
|
@ -5,6 +5,7 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
|||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AuthPresenter @Inject constructor(
|
class AuthPresenter @Inject constructor(
|
||||||
@ -57,8 +58,9 @@ class AuthPresenter @Inject constructor(
|
|||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
|
||||||
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
val isSuccess = studentRepository.authorizePermission(student, semester, pesel)
|
||||||
|
Timber.d("Auth succeed: $isSuccess")
|
||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
studentRepository.refreshStudentName(student, semester)
|
studentRepository.refreshStudentAfterAuthorize(student, semester)
|
||||||
}
|
}
|
||||||
isSuccess
|
isSuccess
|
||||||
}
|
}
|
||||||
@ -68,6 +70,7 @@ class AuthPresenter @Inject constructor(
|
|||||||
view?.showContent(true)
|
view?.showContent(true)
|
||||||
}
|
}
|
||||||
.onSuccess {
|
.onSuccess {
|
||||||
|
Timber.d("Auth fully succeed: $it")
|
||||||
if (it) {
|
if (it) {
|
||||||
view?.showSuccess(true)
|
view?.showSuccess(true)
|
||||||
view?.showContent(false)
|
view?.showContent(false)
|
||||||
|
@ -7,7 +7,6 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
@ -31,14 +30,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: GradePresenter
|
lateinit var presenter: GradePresenter
|
||||||
|
|
||||||
private val pagerAdapter by lazy {
|
|
||||||
BaseFragmentPagerAdapter(
|
|
||||||
fragmentManager = childFragmentManager,
|
|
||||||
pagesCount = 3,
|
|
||||||
lifecycle = lifecycle,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var semesterSwitchMenu: MenuItem? = null
|
private var semesterSwitchMenu: MenuItem? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -52,6 +43,8 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
|
|
||||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||||
|
|
||||||
|
private var pagerAdapter: BaseFragmentPagerAdapter? = null
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -71,13 +64,26 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
|
with(binding) {
|
||||||
|
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||||
|
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun initTabs(pageCount: Int) {
|
||||||
|
pagerAdapter = BaseFragmentPagerAdapter(
|
||||||
|
lifecycle = lifecycle,
|
||||||
|
pagesCount = pageCount,
|
||||||
|
fragmentManager = childFragmentManager
|
||||||
|
)
|
||||||
|
|
||||||
with(binding.gradeViewPager) {
|
with(binding.gradeViewPager) {
|
||||||
adapter = pagerAdapter
|
adapter = pagerAdapter
|
||||||
offscreenPageLimit = 3
|
offscreenPageLimit = 3
|
||||||
setOnSelectPageListener(presenter::onPageSelected)
|
setOnSelectPageListener(presenter::onPageSelected)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(pagerAdapter) {
|
with(pagerAdapter!!) {
|
||||||
containerId = binding.gradeViewPager.id
|
containerId = binding.gradeViewPager.id
|
||||||
titleFactory = {
|
titleFactory = {
|
||||||
when (it) {
|
when (it) {
|
||||||
@ -99,11 +105,6 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
binding.gradeTabLayout.elevation = requireContext().dpToPx(4f)
|
||||||
|
|
||||||
with(binding) {
|
|
||||||
gradeErrorRetry.setOnClickListener { presenter.onRetry() }
|
|
||||||
gradeErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@ -169,19 +170,20 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
override fun notifyChildLoadData(index: Int, semesterId: Int, forceRefresh: Boolean) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)
|
||||||
?.onParentLoadData(semesterId, forceRefresh)
|
?.onParentLoadData(semesterId, forceRefresh)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChildParentReselected(index: Int) {
|
override fun notifyChildParentReselected(index: Int) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentReselected()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun notifyChildSemesterChange(index: Int) {
|
override fun notifyChildSemesterChange(index: Int) {
|
||||||
(pagerAdapter.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
(pagerAdapter?.getFragmentInstance(index) as? GradeView.GradeChildView)?.onParentChangeSemester()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
pagerAdapter = null
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,8 @@ class GradePresenter @Inject constructor(
|
|||||||
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
) : BasePresenter<GradeView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
private var selectedIndex = 0
|
private var selectedIndex = 0
|
||||||
|
|
||||||
private var schoolYear = 0
|
private var schoolYear = 0
|
||||||
|
private var availableSemesters = emptyList<Semester>()
|
||||||
private var semesters = emptyList<Semester>()
|
|
||||||
|
|
||||||
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
private val loadedSemesterId = mutableMapOf<Int, Int>()
|
||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
@ -40,7 +37,7 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onCreateMenu() {
|
fun onCreateMenu() {
|
||||||
if (semesters.isEmpty()) view?.showSemesterSwitch(false)
|
if (availableSemesters.isEmpty()) view?.showSemesterSwitch(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onViewReselected() {
|
fun onViewReselected() {
|
||||||
@ -49,8 +46,8 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onSemesterSwitch(): Boolean {
|
fun onSemesterSwitch(): Boolean {
|
||||||
if (semesters.isNotEmpty()) {
|
if (availableSemesters.isNotEmpty()) {
|
||||||
view?.showSemesterDialog(selectedIndex - 1, semesters.take(2))
|
view?.showSemesterDialog(selectedIndex - 1, availableSemesters.take(2))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -83,7 +80,7 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onPageSelected(index: Int) {
|
fun onPageSelected(index: Int) {
|
||||||
if (semesters.isNotEmpty()) loadChild(index)
|
if (availableSemesters.isNotEmpty()) loadChild(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRetry() {
|
fun onRetry() {
|
||||||
@ -101,16 +98,24 @@ class GradePresenter @Inject constructor(
|
|||||||
private fun loadData() {
|
private fun loadData() {
|
||||||
resourceFlow {
|
resourceFlow {
|
||||||
val student = studentRepository.getCurrentStudent()
|
val student = studentRepository.getCurrentStudent()
|
||||||
semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
val semesters = semesterRepository.getSemesters(student, refreshOnNoCurrent = true)
|
||||||
|
|
||||||
|
student to semesters
|
||||||
}
|
}
|
||||||
.logResourceStatus("load grade data")
|
.logResourceStatus("load grade data")
|
||||||
.onResourceData {
|
.onResourceData { (student, semesters) ->
|
||||||
val current = it.getCurrentOrLast()
|
val currentSemester = semesters.getCurrentOrLast()
|
||||||
selectedIndex = if (selectedIndex == 0) current.semesterName else selectedIndex
|
selectedIndex =
|
||||||
schoolYear = current.schoolYear
|
if (selectedIndex == 0) currentSemester.semesterName else selectedIndex
|
||||||
semesters = it.filter { semester -> semester.diaryId == current.diaryId }
|
schoolYear = currentSemester.schoolYear
|
||||||
view?.setCurrentSemesterName(current.semesterName, schoolYear)
|
availableSemesters = semesters.filter { semester ->
|
||||||
|
semester.diaryId == currentSemester.diaryId
|
||||||
|
}
|
||||||
|
|
||||||
view?.run {
|
view?.run {
|
||||||
|
initTabs(if (student.isEduOne == true) 2 else 3)
|
||||||
|
setCurrentSemesterName(currentSemester.semesterName, schoolYear)
|
||||||
|
|
||||||
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
Timber.i("Loading grade data: Attempt load index $currentPageIndex")
|
||||||
loadChild(currentPageIndex)
|
loadChild(currentPageIndex)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
@ -131,10 +136,10 @@ class GradePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
private fun loadChild(index: Int, forceRefresh: Boolean = false) {
|
||||||
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${semesters.joinToString { it.semesterName.toString() }}")
|
Timber.d("Load grade tab child. Selected semester: $selectedIndex, semesters: ${availableSemesters.joinToString { it.semesterName.toString() }}")
|
||||||
|
|
||||||
val newSelectedSemesterId = try {
|
val newSelectedSemesterId = try {
|
||||||
semesters.first { it.semesterName == selectedIndex }.semesterId
|
availableSemesters.first { it.semesterName == selectedIndex }.semesterId
|
||||||
} catch (e: NoSuchElementException) {
|
} catch (e: NoSuchElementException) {
|
||||||
Timber.e(e, "Selected semester no exists")
|
Timber.e(e, "Selected semester no exists")
|
||||||
return
|
return
|
||||||
|
@ -9,6 +9,8 @@ interface GradeView : BaseView {
|
|||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
|
fun initTabs(pageCount: Int)
|
||||||
|
|
||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun showProgress(show: Boolean)
|
fun showProgress(show: Boolean)
|
||||||
|
@ -98,7 +98,9 @@ class HomeworkAddDialog : BaseDialogFragment<DialogHomeworkAddBinding>(), Homewo
|
|||||||
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
rangeEnd = LocalDate.now().lastSchoolDayInSchoolYear,
|
||||||
onDateSelected = {
|
onDateSelected = {
|
||||||
date = it
|
date = it
|
||||||
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
if (isAdded) {
|
||||||
|
binding.homeworkDialogDate.editText?.setText(date!!.toFormattedString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ class MainPresenter @Inject constructor(
|
|||||||
syncManager.startPeriodicSyncWorker()
|
syncManager.startPeriodicSyncWorker()
|
||||||
|
|
||||||
checkAppSupport()
|
checkAppSupport()
|
||||||
checkCurrentStudentAuthorizationStatus()
|
updateCurrentStudentAuthStatus()
|
||||||
|
|
||||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||||
Timber.i("Main view was initialized with $initDestination")
|
Timber.i("Main view was initialized with $initDestination")
|
||||||
@ -193,12 +193,10 @@ class MainPresenter @Inject constructor(
|
|||||||
view?.showStudentAvatar(currentStudent)
|
view?.showStudentAvatar(currentStudent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkCurrentStudentAuthorizationStatus() {
|
private fun updateCurrentStudentAuthStatus() {
|
||||||
presenterScope.launch {
|
presenterScope.launch {
|
||||||
runCatching { studentRepository.checkCurrentStudentAuthorizationStatus() }
|
runCatching { studentRepository.updateCurrentStudentAuthStatus() }
|
||||||
.onFailure { errorHandler.dispatch(it) }
|
.onFailure { errorHandler.dispatch(it) }
|
||||||
|
|
||||||
Timber.i("Current student authorization status checked")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
return when (TimetableItemType.values()[viewType]) {
|
return when (TimetableItemType.entries[viewType]) {
|
||||||
TimetableItemType.SMALL -> SmallViewHolder(
|
TimetableItemType.SMALL -> SmallViewHolder(
|
||||||
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
@ -79,6 +79,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableSmallItemNumber.text = lesson.number.toString()
|
timetableSmallItemNumber.text = lesson.number.toString()
|
||||||
|
timetableSmallItemNumber.isVisible = item.isLessonNumberVisible
|
||||||
timetableSmallItemSubject.text = lesson.subject
|
timetableSmallItemSubject.text = lesson.subject
|
||||||
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
timetableSmallItemTimeStart.text = lesson.start.toFormattedString("HH:mm")
|
||||||
timetableSmallItemRoom.text = lesson.room
|
timetableSmallItemRoom.text = lesson.room
|
||||||
@ -97,6 +98,7 @@ class TimetableAdapter @Inject constructor() :
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableItemNumber.text = lesson.number.toString()
|
timetableItemNumber.text = lesson.number.toString()
|
||||||
|
timetableItemNumber.isVisible = item.isLessonNumberVisible
|
||||||
timetableItemSubject.text = lesson.subject
|
timetableItemSubject.text = lesson.subject
|
||||||
timetableItemGroup.text = lesson.group
|
timetableItemGroup.text = lesson.group
|
||||||
timetableItemRoom.text = lesson.room
|
timetableItemRoom.text = lesson.room
|
||||||
|
@ -7,12 +7,14 @@ sealed class TimetableItem(val type: TimetableItemType) {
|
|||||||
|
|
||||||
data class Small(
|
data class Small(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
val onClick: (Timetable) -> Unit,
|
val onClick: (Timetable) -> Unit,
|
||||||
) : TimetableItem(TimetableItemType.SMALL)
|
) : TimetableItem(TimetableItemType.SMALL)
|
||||||
|
|
||||||
data class Normal(
|
data class Normal(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
val showGroupsInPlan: Boolean,
|
val showGroupsInPlan: Boolean,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
val timeLeft: TimeLeft?,
|
val timeLeft: TimeLeft?,
|
||||||
val onClick: (Timetable) -> Unit,
|
val onClick: (Timetable) -> Unit,
|
||||||
) : TimetableItem(TimetableItemType.NORMAL)
|
) : TimetableItem(TimetableItemType.NORMAL)
|
||||||
|
@ -57,6 +57,7 @@ class TimetablePresenter @Inject constructor(
|
|||||||
|
|
||||||
private var initialDate: LocalDate? = null
|
private var initialDate: LocalDate? = null
|
||||||
private var isWeekendHasLessons: Boolean = false
|
private var isWeekendHasLessons: Boolean = false
|
||||||
|
private var isEduOne: Boolean = false
|
||||||
|
|
||||||
var currentDate: LocalDate? = null
|
var currentDate: LocalDate? = null
|
||||||
private set
|
private set
|
||||||
@ -149,6 +150,7 @@ class TimetablePresenter @Inject constructor(
|
|||||||
val student = studentRepository.getCurrentStudent()
|
val student = studentRepository.getCurrentStudent()
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
|
||||||
|
isEduOne = student.isEduOne == true
|
||||||
checkInitialAndCurrentDate(semester)
|
checkInitialAndCurrentDate(semester)
|
||||||
timetableRepository.getTimetable(
|
timetableRepository.getTimetable(
|
||||||
student = student,
|
student = student,
|
||||||
@ -234,9 +236,8 @@ class TimetablePresenter @Inject constructor(
|
|||||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||||
it.isStudentPlan
|
it.isStudentPlan
|
||||||
} else true
|
} else true
|
||||||
}.sortedWith(
|
}
|
||||||
compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
|
.sortedWith(compareBy({ item -> item.start }, { item -> !item.isStudentPlan }))
|
||||||
)
|
|
||||||
|
|
||||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||||
@ -257,13 +258,15 @@ class TimetablePresenter @Inject constructor(
|
|||||||
lesson = it,
|
lesson = it,
|
||||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||||
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||||
onClick = ::onTimetableItemSelected
|
onClick = ::onTimetableItemSelected,
|
||||||
|
isLessonNumberVisible = !isEduOne
|
||||||
)
|
)
|
||||||
add(normalLesson)
|
add(normalLesson)
|
||||||
} else {
|
} else {
|
||||||
val smallLesson = TimetableItem.Small(
|
val smallLesson = TimetableItem.Small(
|
||||||
lesson = it,
|
lesson = it,
|
||||||
onClick = ::onTimetableItemSelected
|
onClick = ::onTimetableItemSelected,
|
||||||
|
isLessonNumberVisible = !isEduOne
|
||||||
)
|
)
|
||||||
add(smallLesson)
|
add(smallLesson)
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,8 @@ class TimetableWidgetFactory(
|
|||||||
) : RemoteViewsService.RemoteViewsFactory {
|
) : RemoteViewsService.RemoteViewsFactory {
|
||||||
|
|
||||||
private var items = emptyList<TimetableWidgetItem>()
|
private var items = emptyList<TimetableWidgetItem>()
|
||||||
|
|
||||||
private var timetableCanceledColor: Int? = null
|
private var timetableCanceledColor: Int? = null
|
||||||
|
|
||||||
private var textColor: Int? = null
|
private var textColor: Int? = null
|
||||||
|
|
||||||
private var timetableChangeColor: Int? = null
|
private var timetableChangeColor: Int? = null
|
||||||
|
|
||||||
override fun getLoadingView() = null
|
override fun getLoadingView() = null
|
||||||
@ -81,7 +78,7 @@ class TimetableWidgetFactory(
|
|||||||
val lessons = getLessons(student, semester, date)
|
val lessons = getLessons(student, semester, date)
|
||||||
val lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
|
val lastSync = timetableRepository.getLastRefreshTimestamp(semester, date, date)
|
||||||
|
|
||||||
createItems(lessons, lastSync)
|
createItems(lessons, lastSync, !(student.isEduOne ?: false))
|
||||||
}
|
}
|
||||||
.onFailure {
|
.onFailure {
|
||||||
items = listOf(TimetableWidgetItem.Error(it))
|
items = listOf(TimetableWidgetItem.Error(it))
|
||||||
@ -113,12 +110,13 @@ class TimetableWidgetFactory(
|
|||||||
isFromAppWidget = true
|
isFromAppWidget = true
|
||||||
)
|
)
|
||||||
val lessons = timetable.toFirstResult().dataOrThrow.lessons
|
val lessons = timetable.toFirstResult().dataOrThrow.lessons
|
||||||
return lessons.sortedBy { it.number }
|
return lessons.sortedBy { it.start }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createItems(
|
private fun createItems(
|
||||||
lessons: List<Timetable>,
|
lessons: List<Timetable>,
|
||||||
lastSync: Instant?,
|
lastSync: Instant?,
|
||||||
|
isEduOne: Boolean
|
||||||
): List<TimetableWidgetItem> {
|
): List<TimetableWidgetItem> {
|
||||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||||
@ -134,7 +132,7 @@ class TimetableWidgetFactory(
|
|||||||
)
|
)
|
||||||
add(emptyItem)
|
add(emptyItem)
|
||||||
}
|
}
|
||||||
add(TimetableWidgetItem.Normal(it))
|
add(TimetableWidgetItem.Normal(it, isEduOne))
|
||||||
prevNum = it.number
|
prevNum = it.number
|
||||||
}
|
}
|
||||||
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
|
add(TimetableWidgetItem.Synchronized(lastSync ?: Instant.MIN))
|
||||||
@ -162,9 +160,11 @@ class TimetableWidgetFactory(
|
|||||||
|
|
||||||
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
|
val lessonStartTime = lesson.start.toFormattedString(TIME_FORMAT_STYLE)
|
||||||
val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE)
|
val lessonEndTime = lesson.end.toFormattedString(TIME_FORMAT_STYLE)
|
||||||
|
val lessonNumberVisibility = if (item.isLessonNumberVisible) VISIBLE else GONE
|
||||||
|
|
||||||
val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
val remoteViews = RemoteViews(context.packageName, R.layout.item_widget_timetable).apply {
|
||||||
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
|
setTextViewText(R.id.timetableWidgetItemNumber, lesson.number.toString())
|
||||||
|
setViewVisibility(R.id.timetableWidgetItemNumber, lessonNumberVisibility)
|
||||||
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
|
setTextViewText(R.id.timetableWidgetItemTimeStart, lessonStartTime)
|
||||||
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
|
setTextViewText(R.id.timetableWidgetItemTimeFinish, lessonEndTime)
|
||||||
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
|
setTextViewText(R.id.timetableWidgetItemSubject, lesson.subject)
|
||||||
|
@ -7,6 +7,7 @@ sealed class TimetableWidgetItem(val type: TimetableWidgetItemType) {
|
|||||||
|
|
||||||
data class Normal(
|
data class Normal(
|
||||||
val lesson: Timetable,
|
val lesson: Timetable,
|
||||||
|
val isLessonNumberVisible: Boolean,
|
||||||
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
|
) : TimetableWidgetItem(TimetableWidgetItemType.NORMAL)
|
||||||
|
|
||||||
data class Empty(
|
data class Empty(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
Wersja 2.5.2
|
Wersja 2.5.3
|
||||||
|
|
||||||
— naprawiliśmy omyłkowe wyświetlanie ekranu z wymaganą autoryzacją numerem PESEL
|
— naprawiliśmy wyświetlanie błędu "Brak uprawnień" po starcie aplikacji u użytkowników eduOne
|
||||||
— naprawiliśmy kilka problemów ze stabilnością
|
— naprawiliśmy obsługę autoryzacji u użytkowników eduOne
|
||||||
— poprawiliśmy wyświetlanie kolorów we frekwencji
|
— ukryliśmy numery lekcji i oceny klasy u użytkowników eduOne, bo VULCAN te funkcje usunął
|
||||||
|
— naprawiliśmy inne rzeczy u użytkowników eduOne (jak brak opisu oceny czy ładowanie danych na kilku ekranach)
|
||||||
|
|
||||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||||
|
@ -93,8 +93,12 @@ class AdsHelper @Inject constructor(
|
|||||||
private fun initializeMobileAds() {
|
private fun initializeMobileAds() {
|
||||||
if (isMobileAdsInitializeCalled.getAndSet(true)) return
|
if (isMobileAdsInitializeCalled.getAndSet(true)) return
|
||||||
|
|
||||||
MobileAds.initialize(context) {
|
try {
|
||||||
isMobileAdsSdkInitialized.value = true
|
MobileAds.initialize(context) {
|
||||||
|
isMobileAdsSdkInitialized.value = true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,12 @@ package io.github.wulkanowy
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.WulkanowySdkFactory
|
import io.github.wulkanowy.data.WulkanowySdkFactory
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
|
||||||
fun createWulkanowySdkFactoryMock(sdk: Sdk) = mockk<WulkanowySdkFactory>()
|
fun createWulkanowySdkFactoryMock(sdk: Sdk) = mockk<WulkanowySdkFactory>()
|
||||||
.apply {
|
.apply {
|
||||||
every { create() } returns sdk
|
every { create() } returns sdk
|
||||||
every { create(any(), any()) } answers { callOriginal() }
|
coEvery { create(any(), any()) } returns sdk
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import dagger.hilt.android.testing.HiltTestApplication
|
||||||
|
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentIsEduOne
|
||||||
|
import io.github.wulkanowy.getStudentEntity
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.sdk.pojo.RegisterStudent
|
||||||
|
import io.mockk.Runs
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.spyk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||||
|
class WulkanowySdkFactoryTest {
|
||||||
|
|
||||||
|
private lateinit var wulkanowySdkFactory: WulkanowySdkFactory
|
||||||
|
private lateinit var studentDao: StudentDao
|
||||||
|
private lateinit var sdk: Sdk
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
sdk = mockk(relaxed = true)
|
||||||
|
studentDao = mockk()
|
||||||
|
wulkanowySdkFactory = spyk(
|
||||||
|
WulkanowySdkFactory(
|
||||||
|
chuckerInterceptor = mockk(),
|
||||||
|
remoteConfig = mockk(relaxed = true),
|
||||||
|
webkitCookieManagerProxy = mockk(),
|
||||||
|
studentDb = studentDao
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
every { wulkanowySdkFactory.create() } returns sdk
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check sdk flag isEduOne when local student is eduone`() = runTest {
|
||||||
|
val student = getStudentEntity().copy(isEduOne = true)
|
||||||
|
|
||||||
|
wulkanowySdkFactory.create(student)
|
||||||
|
|
||||||
|
verify { sdk.isEduOne = true }
|
||||||
|
coVerify(exactly = 0) { sdk.getCurrentStudent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check sdk flag isEduOne when local student is not eduone`() = runTest {
|
||||||
|
val student = getStudentEntity().copy(isEduOne = false)
|
||||||
|
|
||||||
|
wulkanowySdkFactory.create(student)
|
||||||
|
|
||||||
|
verify { sdk.isEduOne = false }
|
||||||
|
coVerify(exactly = 0) { sdk.getCurrentStudent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check sdk flag isEduOne when local student is eduone null and remote student is eduone true`() =
|
||||||
|
runTest {
|
||||||
|
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||||
|
val registerStudent = studentToProcess.toRegisterStudent(isEduOne = true)
|
||||||
|
|
||||||
|
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||||
|
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||||
|
coEvery { sdk.getCurrentStudent() } returns registerStudent
|
||||||
|
|
||||||
|
wulkanowySdkFactory.create(studentToProcess)
|
||||||
|
|
||||||
|
verify { sdk.isEduOne = true }
|
||||||
|
coVerify { sdk.getCurrentStudent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check sdk flag isEduOne when local student is eduone null and remote student is eduone false`() =
|
||||||
|
runTest {
|
||||||
|
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||||
|
val registerStudent = studentToProcess.toRegisterStudent(isEduOne = false)
|
||||||
|
|
||||||
|
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||||
|
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||||
|
coEvery { sdk.getCurrentStudent() } returns registerStudent
|
||||||
|
|
||||||
|
wulkanowySdkFactory.create(studentToProcess)
|
||||||
|
|
||||||
|
verify { sdk.isEduOne = false }
|
||||||
|
coVerify { sdk.getCurrentStudent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check sdk flag isEduOne when sdk getCurrentStudent throws error`() =
|
||||||
|
runTest {
|
||||||
|
val studentToProcess = getStudentEntity().copy(isEduOne = null)
|
||||||
|
|
||||||
|
coEvery { studentDao.loadById(any()) } returns studentToProcess
|
||||||
|
coEvery { studentDao.update(any(StudentIsEduOne::class)) } just Runs
|
||||||
|
coEvery { sdk.getCurrentStudent() } throws Exception()
|
||||||
|
|
||||||
|
wulkanowySdkFactory.create(studentToProcess)
|
||||||
|
|
||||||
|
verify { sdk.isEduOne = false }
|
||||||
|
coVerify { sdk.getCurrentStudent() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Student.toRegisterStudent(isEduOne: Boolean) = RegisterStudent(
|
||||||
|
studentId = studentId,
|
||||||
|
studentName = studentName,
|
||||||
|
studentSecondName = studentName,
|
||||||
|
studentSurname = studentName,
|
||||||
|
className = className,
|
||||||
|
classId = classId,
|
||||||
|
isParent = isParent,
|
||||||
|
isAuthorized = isAuthorized,
|
||||||
|
semesters = emptyList(),
|
||||||
|
isEduOne = isEduOne,
|
||||||
|
)
|
||||||
|
}
|
@ -21,10 +21,10 @@ abstract class AbstractMigrationTest {
|
|||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val helper: MigrationTestHelper = MigrationTestHelper(
|
val helper: MigrationTestHelper = MigrationTestHelper(
|
||||||
InstrumentationRegistry.getInstrumentation(),
|
instrumentation = InstrumentationRegistry.getInstrumentation(),
|
||||||
AppDatabase::class.java,
|
databaseClass = AppDatabase::class.java,
|
||||||
listOf(Migration55()),
|
specs = listOf(Migration63()),
|
||||||
FrameworkSQLiteOpenHelperFactory()
|
openFactory = FrameworkSQLiteOpenHelperFactory()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun runMigrationsAndValidate(migration: Migration) {
|
fun runMigrationsAndValidate(migration: Migration) {
|
||||||
|
@ -7,8 +7,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
|
|||||||
import dagger.hilt.android.testing.HiltAndroidTest
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
import dagger.hilt.android.testing.HiltTestApplication
|
import dagger.hilt.android.testing.HiltTestApplication
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.*
|
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.ADFSLight
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.ADFSLightScoped
|
||||||
|
import io.github.wulkanowy.sdk.Sdk.ScrapperLoginType.STANDARD
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@ -19,7 +20,6 @@ import kotlin.test.assertEquals
|
|||||||
|
|
||||||
@HiltAndroidTest
|
@HiltAndroidTest
|
||||||
@RunWith(RobolectricTestRunner::class)
|
@RunWith(RobolectricTestRunner::class)
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||||
class Migration54Test : AbstractMigrationTest() {
|
class Migration54Test : AbstractMigrationTest() {
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.database.sqlite.SQLiteDatabase
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
import dagger.hilt.android.testing.HiltAndroidTest
|
||||||
|
import dagger.hilt.android.testing.HiltTestApplication
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
@HiltAndroidTest
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [Build.VERSION_CODES.O_MR1], application = HiltTestApplication::class)
|
||||||
|
class Migration63Test : AbstractMigrationTest() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `update is_edu_one to null if 0`() = runTest {
|
||||||
|
with(helper.createDatabase(dbName, 62)) {
|
||||||
|
createStudent(1, 0)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.runMigrationsAndValidate(dbName, 63, true)
|
||||||
|
|
||||||
|
val database = getMigratedRoomDatabase()
|
||||||
|
val studentDb = database.studentDao
|
||||||
|
val student = studentDb.loadById(1)
|
||||||
|
|
||||||
|
assertNull(student!!.isEduOne)
|
||||||
|
|
||||||
|
database.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check is_edu_one is stay same`() = runTest {
|
||||||
|
with(helper.createDatabase(dbName, 62)) {
|
||||||
|
createStudent(1, 1)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.runMigrationsAndValidate(dbName, 63, true)
|
||||||
|
|
||||||
|
val database = getMigratedRoomDatabase()
|
||||||
|
val studentDb = database.studentDao
|
||||||
|
val student = studentDb.loadById(1)
|
||||||
|
|
||||||
|
val isEduOne = assertNotNull(student!!.isEduOne)
|
||||||
|
assertTrue(isEduOne)
|
||||||
|
database.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SupportSQLiteDatabase.createStudent(id: Long, isEduOneValue: Int) {
|
||||||
|
insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
|
||||||
|
put("scrapper_base_url", "https://fakelog.cf")
|
||||||
|
put("mobile_base_url", "")
|
||||||
|
put("login_type", "SCRAPPER")
|
||||||
|
put("login_mode", "SCRAPPER")
|
||||||
|
put("certificate_key", "")
|
||||||
|
put("private_key", "")
|
||||||
|
put("is_parent", false)
|
||||||
|
put("email", "jan@fakelog.cf")
|
||||||
|
put("password", "******")
|
||||||
|
put("symbol", "symbol")
|
||||||
|
put("student_id", Random.nextInt())
|
||||||
|
put("user_login_id", 123)
|
||||||
|
put("user_name", "studentName")
|
||||||
|
put("student_name", "studentName")
|
||||||
|
put("school_id", "123")
|
||||||
|
put("school_short", "")
|
||||||
|
put("school_name", "")
|
||||||
|
put("class_name", "")
|
||||||
|
put("class_id", Random.nextInt())
|
||||||
|
put("is_current", false)
|
||||||
|
put("registration_date", "0")
|
||||||
|
put("id", id)
|
||||||
|
put("nick", "")
|
||||||
|
put("avatar_color", "")
|
||||||
|
put("is_edu_one", isEduOneValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -48,8 +48,11 @@ class LuckyNumberRemoteTest {
|
|||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockKAnnotations.init(this)
|
MockKAnnotations.init(this)
|
||||||
|
|
||||||
luckyNumberRepository =
|
luckyNumberRepository = LuckyNumberRepository(
|
||||||
LuckyNumberRepository(luckyNumberDb, wulkanowySdkFactory, appWidgetUpdater)
|
luckyNumberDb = luckyNumberDb,
|
||||||
|
wulkanowySdkFactory = wulkanowySdkFactory,
|
||||||
|
appWidgetUpdater = appWidgetUpdater,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user