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