diff --git a/app/build.gradle b/app/build.gradle index a741f8d2..aa4effbe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -191,7 +191,7 @@ ext { } dependencies { - implementation 'io.github.wulkanowy:sdk:2.6.3' + implementation 'io.github.wulkanowy:sdk:2.6.4-SNAPSHOT' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' diff --git a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt index a492c08d..a21d56cc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt @@ -13,8 +13,8 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import io.github.wulkanowy.data.api.AdminMessageService -import io.github.wulkanowy.data.api.SchoolsService +import io.github.wulkanowy.data.api.services.SchoolsService +import io.github.wulkanowy.data.api.services.WulkanowyService import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.PreferencesRepository @@ -71,7 +71,7 @@ internal class DataModule { okHttpClient: OkHttpClient, json: Json, appInfo: AppInfo - ): AdminMessageService = Retrofit.Builder() + ): WulkanowyService = Retrofit.Builder() .baseUrl(appInfo.messagesBaseUrl) .client(okHttpClient) .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) diff --git a/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt b/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt index 83268a0e..7692d52c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt +++ b/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt @@ -5,6 +5,7 @@ import io.github.wulkanowy.data.db.dao.StudentDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentIsEduOne +import io.github.wulkanowy.data.repositories.WulkanowyRepository import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.RemoteConfigHelper import io.github.wulkanowy.utils.WebkitCookieManagerProxy @@ -20,6 +21,7 @@ class WulkanowySdkFactory @Inject constructor( private val remoteConfig: RemoteConfigHelper, private val webkitCookieManagerProxy: WebkitCookieManagerProxy, private val studentDb: StudentDao, + private val wulkanowyRepository: WulkanowyRepository, ) { private val eduOneMutex = Mutex() @@ -36,14 +38,29 @@ class WulkanowySdkFactory @Inject constructor( addInterceptor(chuckerInterceptor, network = true) } - fun create() = sdk + fun createBase() = sdk + + suspend fun create(): Sdk { + val mapping = wulkanowyRepository.getMapping() + + return createBase().apply { + if (mapping != null) { + endpointsMapping = mapping.endpoints + vTokenMapping = mapping.vTokens + } + } + } 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 { + private suspend fun buildSdk( + student: Student, + semester: Semester?, + isStudentEduOne: Boolean + ): Sdk { return create().apply { email = student.email password = student.password diff --git a/app/src/main/java/io/github/wulkanowy/data/api/models/Mapping.kt b/app/src/main/java/io/github/wulkanowy/data/api/models/Mapping.kt new file mode 100644 index 00000000..9a72542f --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/data/api/models/Mapping.kt @@ -0,0 +1,14 @@ +package io.github.wulkanowy.data.api.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class Mapping( + + @SerialName("endpoints") + val endpoints: Map>>, + + @SerialName("vTokens") + val vTokens: Map>>, +) diff --git a/app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt b/app/src/main/java/io/github/wulkanowy/data/api/services/SchoolsService.kt similarity index 87% rename from app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt rename to app/src/main/java/io/github/wulkanowy/data/api/services/SchoolsService.kt index a7da9b63..07fbeb89 100644 --- a/app/src/main/java/io/github/wulkanowy/data/api/SchoolsService.kt +++ b/app/src/main/java/io/github/wulkanowy/data/api/services/SchoolsService.kt @@ -1,4 +1,4 @@ -package io.github.wulkanowy.data.api +package io.github.wulkanowy.data.api.services import io.github.wulkanowy.data.pojos.IntegrityRequest import io.github.wulkanowy.data.pojos.LoginEvent diff --git a/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt b/app/src/main/java/io/github/wulkanowy/data/api/services/WulkanowyService.kt similarity index 51% rename from app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt rename to app/src/main/java/io/github/wulkanowy/data/api/services/WulkanowyService.kt index 23f5af24..bf1349f2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/api/AdminMessageService.kt +++ b/app/src/main/java/io/github/wulkanowy/data/api/services/WulkanowyService.kt @@ -1,12 +1,16 @@ -package io.github.wulkanowy.data.api +package io.github.wulkanowy.data.api.services +import io.github.wulkanowy.data.api.models.Mapping import io.github.wulkanowy.data.db.entities.AdminMessage import retrofit2.http.GET import javax.inject.Singleton @Singleton -interface AdminMessageService { +interface WulkanowyService { @GET("/v1.json") suspend fun getAdminMessages(): List -} \ No newline at end of file + + @GET("/mapping1.json") + suspend fun getMapping(): Mapping +} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt index 8082068c..29e8bccd 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/PreferencesRepository.kt @@ -9,6 +9,7 @@ import com.fredporciuncula.flow.preferences.Preference import com.fredporciuncula.flow.preferences.Serializer import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R +import io.github.wulkanowy.data.api.models.Mapping import io.github.wulkanowy.data.enums.AppTheme import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode import io.github.wulkanowy.data.enums.GradeColorTheme @@ -375,6 +376,15 @@ class PreferencesRepository @Inject constructor( get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty() private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) } + var mapping: Mapping? + get() { + val value = sharedPref.getString("mapping", null) + return value?.let { json.decodeFromString(it) } + } + set(value) = sharedPref.edit(commit = true) { + putString("mapping", value?.let { json.encodeToString(it) }) + } + init { if (installationId.isEmpty()) { installationId = UUID.randomUUID().toString() diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt index 4a16d6f1..25da241a 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolsRepository.kt @@ -1,7 +1,7 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.WulkanowySdkFactory -import io.github.wulkanowy.data.api.SchoolsService +import io.github.wulkanowy.data.api.services.SchoolsService import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt similarity index 55% rename from app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt rename to app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt index aa0022b0..5b563b35 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/WulkanowyRepository.kt @@ -1,20 +1,23 @@ package io.github.wulkanowy.data.repositories import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.api.AdminMessageService +import io.github.wulkanowy.data.api.models.Mapping +import io.github.wulkanowy.data.api.services.WulkanowyService import io.github.wulkanowy.data.db.dao.AdminMessageDao import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.networkBoundResource import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.sync.Mutex +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @Singleton -class AdminMessageRepository @Inject constructor( - private val adminMessageService: AdminMessageService, +class WulkanowyRepository @Inject constructor( + private val wulkanowyService: WulkanowyService, private val adminMessageDao: AdminMessageDao, + private val preferencesRepository: PreferencesRepository, ) { private val saveFetchResultMutex = Mutex() @@ -24,11 +27,28 @@ class AdminMessageRepository @Inject constructor( mutex = saveFetchResultMutex, isResultEmpty = { false }, query = { adminMessageDao.loadAll() }, - fetch = { adminMessageService.getAdminMessages() }, + fetch = { wulkanowyService.getAdminMessages() }, shouldFetch = { true }, saveFetchResult = { oldItems, newItems -> adminMessageDao.removeOldAndSaveNew(oldItems, newItems) }, ) .filterNot { it is Resource.Intermediate } + + suspend fun getMapping(): Mapping? { + var savedMapping = preferencesRepository.mapping + + if (savedMapping == null) { + fetchMapping() + savedMapping = preferencesRepository.mapping + } + + return savedMapping + } + + suspend fun fetchMapping() { + runCatching { wulkanowyService.getMapping() } + .onFailure { Timber.e(it) } + .onSuccess { preferencesRepository.mapping = it } + } } diff --git a/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt index b55bf899..8b0d67b5 100644 --- a/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt +++ b/app/src/main/java/io/github/wulkanowy/domain/adminmessage/GetAppropriateAdminMessageUseCase.kt @@ -5,14 +5,14 @@ import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.enums.MessageType import io.github.wulkanowy.data.mapResourceData -import io.github.wulkanowy.data.repositories.AdminMessageRepository import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.data.repositories.WulkanowyRepository import io.github.wulkanowy.utils.AppInfo import kotlinx.coroutines.flow.Flow import javax.inject.Inject class GetAppropriateAdminMessageUseCase @Inject constructor( - private val adminMessageRepository: AdminMessageRepository, + private val wulkanowyRepository: WulkanowyRepository, private val preferencesRepository: PreferencesRepository, private val appInfo: AppInfo ) { @@ -22,7 +22,7 @@ class GetAppropriateAdminMessageUseCase @Inject constructor( } operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow> { - return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages -> + return wulkanowyRepository.getAdminMessages().mapResourceData { adminMessages -> adminMessages .asSequence() .filter { it.isNotDismissed() } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt index ce2173d2..922b652b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/captcha/CaptchaDialog.kt @@ -59,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment() { webView = this with(settings) { javaScriptEnabled = true - userAgentString = wulkanowySdkFactory.create().userAgent + userAgentString = wulkanowySdkFactory.createBase().userAgent } webViewClient = object : WebViewClient() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt index 88f29578..e528c514 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginActivity.kt @@ -118,5 +118,6 @@ class LoginActivity : BaseActivity(), Logi override fun onResume() { super.onResume() inAppUpdateHelper.onResume() + presenter.updateSdkMappings() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt index 9031cb8a..aff0515f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/login/LoginPresenter.kt @@ -1,12 +1,15 @@ package io.github.wulkanowy.ui.modules.login import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.WulkanowyRepository import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject class LoginPresenter @Inject constructor( + private val wulkanowyRepository: WulkanowyRepository, errorHandler: ErrorHandler, studentRepository: StudentRepository ) : BasePresenter(errorHandler, studentRepository) { @@ -16,4 +19,11 @@ class LoginPresenter @Inject constructor( view.initView() Timber.i("Login view was initialized") } + + fun updateSdkMappings() { + presenterScope.launch { + runCatching { wulkanowyRepository.fetchMapping() } + .onFailure { Timber.e(it) } + } + } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt index 62c16257..e64aa9b0 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainActivity.kt @@ -138,6 +138,7 @@ class MainActivity : BaseActivity(), MainVie override fun onResume() { super.onResume() inAppUpdateHelper.onResume() + presenter.updateSdkMappings() } override fun onCreateOptionsMenu(menu: Menu): Boolean { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt index a544381c..6a072718 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/main/MainPresenter.kt @@ -6,6 +6,7 @@ import io.github.wulkanowy.data.onResourceError import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.WulkanowyRepository import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.BasePresenter @@ -29,6 +30,7 @@ class MainPresenter @Inject constructor( errorHandler: ErrorHandler, studentRepository: StudentRepository, private val preferencesRepository: PreferencesRepository, + private val wulkanowyRepository: WulkanowyRepository, private val syncManager: SyncManager, private val analytics: AnalyticsHelper, private val json: Json, @@ -199,4 +201,11 @@ class MainPresenter @Inject constructor( .onFailure { errorHandler.dispatch(it) } } } + + fun updateSdkMappings() { + presenterScope.launch { + runCatching { wulkanowyRepository.fetchMapping() } + .onFailure { Timber.e(it) } + } + } } diff --git a/app/src/test/java/io/github/wulkanowy/WulkanowySdkFactoryCreator.kt b/app/src/test/java/io/github/wulkanowy/WulkanowySdkFactoryCreator.kt index 9623c9f9..4a8ff656 100644 --- a/app/src/test/java/io/github/wulkanowy/WulkanowySdkFactoryCreator.kt +++ b/app/src/test/java/io/github/wulkanowy/WulkanowySdkFactoryCreator.kt @@ -8,6 +8,7 @@ import io.mockk.mockk fun createWulkanowySdkFactoryMock(sdk: Sdk) = mockk() .apply { - every { create() } returns sdk + every { createBase() } returns sdk + coEvery { create() } returns sdk coEvery { create(any(), any()) } returns sdk } diff --git a/app/src/test/java/io/github/wulkanowy/data/WulkanowySdkFactoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/WulkanowySdkFactoryTest.kt index bedeffe7..e52d771b 100644 --- a/app/src/test/java/io/github/wulkanowy/data/WulkanowySdkFactoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/WulkanowySdkFactoryTest.kt @@ -11,7 +11,6 @@ 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 @@ -40,11 +39,12 @@ class WulkanowySdkFactoryTest { chuckerInterceptor = mockk(), remoteConfig = mockk(relaxed = true), webkitCookieManagerProxy = mockk(), - studentDb = studentDao + studentDb = studentDao, + wulkanowyRepository = mockk(relaxed = true), ) ) - every { wulkanowySdkFactory.create() } returns sdk + coEvery { wulkanowySdkFactory.create() } returns sdk } @Test diff --git a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt index 3e95774d..afb60305 100644 --- a/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt +++ b/app/src/test/java/io/github/wulkanowy/ui/modules/main/MainPresenterTest.kt @@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.main import io.github.wulkanowy.MainCoroutineRule import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository +import io.github.wulkanowy.data.repositories.WulkanowyRepository import io.github.wulkanowy.services.sync.SyncManager import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.utils.AdsHelper @@ -31,6 +32,9 @@ class MainPresenterTest { @MockK lateinit var studentRepository: StudentRepository + @MockK(relaxed = true) + lateinit var wulkanowyRepository: WulkanowyRepository + @MockK(relaxed = true) lateinit var prefRepository: PreferencesRepository @@ -65,7 +69,8 @@ class MainPresenterTest { analytics = analytics, json = Json, appInfo = appInfo, - adsHelper = adsHelper + adsHelper = adsHelper, + wulkanowyRepository = wulkanowyRepository ) presenter.onAttachView(mainView, null) }