mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 12:58:21 +01:00
Hide grade statistics when is eduOne, add isEduOne flag migration (#2496)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
This commit is contained in:
parent
3a55c3c760
commit
76d038eefa
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
|
||||||
@ -39,7 +51,7 @@ class WulkanowySdkFactory @Inject constructor(
|
|||||||
studentId = student.studentId
|
studentId = student.studentId
|
||||||
classId = student.classId
|
classId = student.classId
|
||||||
emptyCookieJarInterceptor = true
|
emptyCookieJarInterceptor = true
|
||||||
isEduOne = student.isEduOne
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -16,9 +16,9 @@ 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 +101,29 @@ 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 = initializedSdk.getCurrentStudent() ?: return
|
||||||
|
|
||||||
|
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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +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
|
isEduOne = student.isEduOne == true
|
||||||
checkInitialAndCurrentDate(semester)
|
checkInitialAndCurrentDate(semester)
|
||||||
timetableRepository.getTimetable(
|
timetableRepository.getTimetable(
|
||||||
student = student,
|
student = student,
|
||||||
|
@ -78,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, !student.isEduOne)
|
createItems(lessons, lastSync, !(student.isEduOne ?: false))
|
||||||
}
|
}
|
||||||
.onFailure {
|
.onFailure {
|
||||||
items = listOf(TimetableWidgetItem.Error(it))
|
items = listOf(TimetableWidgetItem.Error(it))
|
||||||
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user