diff --git a/.circleci/config.yml b/.circleci/config.yml index 2cb2e147..ce2922ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -162,7 +162,7 @@ jobs: openssl aes-256-cbc -d -in ./app/upload-key-encrypted.jks -k $ENCRYPT_KEY >> ./app/upload-key.jks - run: name: Publish release - command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex + command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PenableCrashlytics -PdisablePreDex workflows: version: 2 diff --git a/.github/workflows/deploy-store.yml b/.github/workflows/deploy-store.yml index 0195f3e5..e8237a38 100644 --- a/.github/workflows/deploy-store.yml +++ b/.github/workflows/deploy-store.yml @@ -40,7 +40,7 @@ jobs: SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }} DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }} SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }} - run: ./gradlew publishPlayReleaseApps --stacktrace; + run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace; deploy-app-gallery: name: AppGallery diff --git a/.github/workflows/deploy-test.yml b/.github/workflows/deploy-test.yml index 42c1f8e7..c4f55e6a 100644 --- a/.github/workflows/deploy-test.yml +++ b/.github/workflows/deploy-test.yml @@ -36,7 +36,8 @@ jobs: - name: Prepare build configuration run: | sed -i -e "s#applicationIdSuffix \".dev\"#applicationIdSuffix \".${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/build.gradle - sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/google-services.json + sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/google-services.json + sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/agconnect-services.json sed -i -e '/versionNameSuffix/d' app/build.gradle - name: Add signing config run: | @@ -130,7 +131,7 @@ jobs: BITRISE_KEYSTORE_PASSWORD: ${{ secrets.BITRISE_KEYSTORE_PASSWORD }} BITRISE_KEY_ALIAS: ${{ secrets.BITRISE_KEY_ALIAS }} BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }} - run: ./gradlew assemblePlayDebug --stacktrace + run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace - name: Upload apk to github artifacts uses: actions/upload-artifact@v3 with: diff --git a/.gitignore b/.gitignore index ad83ced8..980085e3 100644 --- a/.gitignore +++ b/.gitignore @@ -67,10 +67,6 @@ captures/ .idea/discord.xml .idea/migrations.xml .idea/androidTestResultsUserPreferences.xml -.idea/copilot -.idea/deploymentTargetDropDown.xml -.idea/deploymentTargetSelector.xml -.idea/kotlinc.xml # Keystore files *.jks @@ -117,13 +113,12 @@ Thumbs.db *.ear ### AndroidStudio Patch ### + !/gradle/wrapper/gradle-wrapper.jar .idea/jarRepositories.xml -### Services config files -agconnect-services.json -agconnect-credentials.json -google-services.json -!app/google-services.json - +app/src/release/agconnect-services.json +app/src/release/agconnect-credentials.json +.idea/deploymentTargetDropDown.xml +.idea/kotlinc.xml diff --git a/.travis.yml b/.travis.yml index e0b0be97..04db3a61 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,7 +61,7 @@ script: gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; - ./gradlew publishPlayRelease --stacktrace; + ./gradlew publishPlayRelease -PenableFirebase --stacktrace; fi after_success: diff --git a/app/build.gradle b/app/build.gradle index d1c67ab0..6f63715b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,12 +27,15 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 21 targetSdkVersion 34 - versionCode 150 - versionName "2.5.1" + versionCode 149 + versionName "2.5.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resValue "string", "app_name", "Wulkanowy" - manifestPlaceholders = [admob_project_id: ""] + manifestPlaceholders = [ + firebase_enabled: project.hasProperty("enableFirebase"), + admob_project_id: "" + ] buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null" buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null" @@ -73,6 +76,7 @@ android { resValue "string", "app_name", "Wulkanowy DEV" applicationIdSuffix ".dev" versionNameSuffix "-dev" + ext.enableCrashlytics = project.hasProperty("enableFirebase") buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\"" buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"' } @@ -160,7 +164,7 @@ play { defaultToAppBundles = false track = 'production' releaseStatus = ReleaseStatus.IN_PROGRESS - userFraction = 0.50d + userFraction = 0.20d updatePriority = 1 enabled.set(false) } @@ -191,7 +195,7 @@ ext { } dependencies { - implementation 'io.github.wulkanowy:sdk:2.5.1' + implementation 'io.github.wulkanowy:sdk:2.5.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' @@ -248,13 +252,13 @@ dependencies { implementation 'com.fredporciuncula:flow-preferences:1.9.1' implementation 'org.apache.commons:commons-text:1.11.0' - playImplementation platform('com.google.firebase:firebase-bom:32.7.4') + playImplementation platform('com.google.firebase:firebase-bom:32.7.3') playImplementation 'com.google.firebase:firebase-analytics' playImplementation 'com.google.firebase:firebase-messaging' playImplementation 'com.google.firebase:firebase-crashlytics:' playImplementation 'com.google.firebase:firebase-config' - playImplementation 'com.google.android.gms:play-services-ads:23.0.0' + playImplementation 'com.google.android.gms:play-services-ads:22.6.0' playImplementation "com.google.android.play:integrity:1.3.0" playImplementation 'com.google.android.play:app-update-ktx:2.1.0' playImplementation 'com.google.android.play:review-ktx:2.0.1' diff --git a/app/play-publish-lint.sh b/app/play-publish-lint.sh index 5f0391de..d3354b1a 100755 --- a/app/play-publish-lint.sh +++ b/app/play-publish-lint.sh @@ -1,8 +1,7 @@ #!/bin/bash - content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit -content2=echo "$content" | dos2unix -if [[ "${#content2}" -gt 500 ]]; then +if [[ "${#content}" -gt 500 ]]; then echo >&2 "Release notes content has reached the limit of 500 characters" exit 1 fi diff --git a/app/src/debug/agconnect-services.json b/app/src/debug/agconnect-services.json new file mode 100644 index 00000000..52426f54 --- /dev/null +++ b/app/src/debug/agconnect-services.json @@ -0,0 +1,92 @@ +{ + "agcgw": { + "backurl": "connect-dre.hispace.hicloud.com", + "url": "connect-dre.dbankcloud.cn", + "websocketbackurl": "connect-ws-dre.hispace.dbankcloud.com", + "websocketurl": "connect-ws-dre.hispace.dbankcloud.cn" + }, + "agcgw_all": { + "CN": "connect-drcn.dbankcloud.cn", + "CN_back": "connect-drcn.hispace.hicloud.com", + "DE": "connect-dre.dbankcloud.cn", + "DE_back": "connect-dre.hispace.hicloud.com", + "RU": "connect-drru.hispace.dbankcloud.ru", + "RU_back": "connect-drru.hispace.dbankcloud.cn", + "SG": "connect-dra.dbankcloud.cn", + "SG_back": "connect-dra.hispace.hicloud.com" + }, + "websocketgw_all": { + "CN": "connect-ws-drcn.hispace.dbankcloud.cn", + "CN_back": "connect-ws-drcn.hispace.dbankcloud.com", + "DE": "connect-ws-dre.hispace.dbankcloud.cn", + "DE_back": "connect-ws-dre.hispace.dbankcloud.com", + "RU": "connect-ws-drru.hispace.dbankcloud.ru", + "RU_back": "connect-ws-drru.hispace.dbankcloud.cn", + "SG": "connect-ws-dra.hispace.dbankcloud.cn", + "SG_back": "connect-ws-dra.hispace.dbankcloud.com" + }, + "client": { + "cp_id": "890048000024105546", + "product_id": "736430079244736562", + "client_id": "514530959291319360", + "client_secret": "C42522DBF17D3D4BBE9D9C1783A54484B7E6844B388B7A67502D36A633A4186B", + "project_id": "736430079244736562", + "app_id": "106552551", + "api_key": "CgB6e3x9BUNiq+r8ebCHNojjjYsMT4pJSjjNDOkm9owtBb6rVI6LjnASoZBRxbjjhObcrV5gANo99fI/eKZDTbWS", + "package_name": "io.github.wulkanowy.dev" + }, + "oauth_client": { + "client_id": "106552551", + "client_type": 1 + }, + "app_info": { + "app_id": "106552551", + "package_name": "io.github.wulkanowy.dev" + }, + "service": { + "analytics": { + "collector_url": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_ru": "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com", + "collector_url_sg": "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", + "collector_url_de": "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_cn": "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", + "resource_id": "p1", + "channel_id": "" + }, + "search":{ + "url":"https://search-dre.cloud.huawei.com" + }, + "cloudstorage": { + "storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia", + "storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu", + "storage_url_de": "https://ops-dre.agcstorage.link", + "storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn", + "storage_url_sg": "https://ops-dra.agcstorage.link", + "storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn", + "storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn" + }, + "ml": { + "mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn" + } + }, + "region": "DE", + "configuration_version": "3.0", + "appInfos": [ + { + "package_name": "io.github.wulkanowy.dev", + "client": { + "app_id": "106552551" + }, + "app_info": { + "package_name": "io.github.wulkanowy.dev", + "app_id": "106552551" + }, + "oauth_client": { + "client_type": 1, + "client_id": "106552551" + } + } + ] +} diff --git a/app/google-services.json b/app/src/debug/google-services.json similarity index 100% rename from app/google-services.json rename to app/src/debug/google-services.json diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4e617c93..f43dfdd2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -155,9 +155,33 @@ android:resource="@xml/provider_paths" /> + + + + + + + + + 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..50d6c8f9 100644 --- a/app/src/main/java/io/github/wulkanowy/data/DataModule.kt +++ b/app/src/main/java/io/github/wulkanowy/data/DataModule.kt @@ -18,13 +18,17 @@ import io.github.wulkanowy.data.api.SchoolsService import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.repositories.PreferencesRepository +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AppInfo +import io.github.wulkanowy.utils.RemoteConfigHelper +import io.github.wulkanowy.utils.WebkitCookieManagerProxy import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.create +import timber.log.Timber import java.util.concurrent.TimeUnit import javax.inject.Singleton @@ -32,6 +36,23 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) internal class DataModule { + @Singleton + @Provides + fun provideSdk( + chuckerInterceptor: ChuckerInterceptor, + remoteConfig: RemoteConfigHelper, + webkitCookieManagerProxy: WebkitCookieManagerProxy, + ) = Sdk().apply { + androidVersion = android.os.Build.VERSION.RELEASE + buildTag = android.os.Build.MODEL + userAgentTemplate = remoteConfig.userAgentTemplate + setSimpleHttpLogger { Timber.d(it) } + setAdditionalCookieManager(webkitCookieManagerProxy) + + // for debug only + addInterceptor(chuckerInterceptor, network = true) + } + @Singleton @Provides fun provideChuckerCollector( diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt index 712a946f..108b0d58 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -1,17 +1,11 @@ package io.github.wulkanowy.data -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.flow.emitAll import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -20,39 +14,16 @@ import kotlinx.coroutines.flow.takeWhile import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds -sealed interface Resource { - /** - * The initial value of a resource flow. Indicates no data that is currently available to be shown, - * however with the expectation that the state will transition to another one soon. - */ - open class Loading : Resource +sealed class Resource { + + open class Loading : Resource() - /** - * A semi-loading state with some data available to be displayed (usually cached data loaded from - * the database). Still not the target state and it's expected to transition into another one soon. - */ data class Intermediate(val data: T) : Loading() - /** - * The happy-path target state. Data can either be: - * - loaded from the database - while it may seem like this case is already handled by the - * Intermediate state, the difference here is semantic. Cached data is returned as Intermediate - * when there's a API request in progress (or soon expected to be), however when there is no - * intention of immediately querying the API, the cached data is returned as a Success. - * - fetched from the API. - */ - data class Success(val data: T) : Resource + data class Success(val data: T) : Resource() - /** - * Something bad happened and we were unable to get the requested data. This can be caused by - * a database error, a network error, or really just any other error. Upon receiving this state - * the UI can either: display a full screen error, or, when it has received any data previously, - * display a snack bar informing of the problem. - */ - data class Error(val error: Throwable) : Resource + data class Error(val error: Throwable) : Resource() } val Resource.dataOrNull: T? @@ -93,22 +64,6 @@ fun Resource.mapData(block: (T) -> U) = when (this) { is Resource.Error -> Resource.Error(this.error) } -/** - * Injects another flow into this flow's resource data. - */ -inline fun Flow>.combineWithResourceData( - flow: Flow, - crossinline block: suspend (T1, T2) -> R -): Flow> = - combine(flow) { resource, inject -> - when (resource) { - is Resource.Success -> Resource.Success(block(resource.data, inject)) - is Resource.Intermediate -> Resource.Intermediate(block(resource.data, inject)) - is Resource.Loading -> Resource.Loading() - is Resource.Error -> Resource.Error(resource.error) - } - } - fun Flow>.logResourceStatus(name: String, showData: Boolean = false) = onEach { val description = when (it) { is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else "" @@ -119,29 +74,8 @@ fun Flow>.logResourceStatus(name: String, showData: Boolean = fa Timber.i("$name: $description") } -inline fun Flow>.mapResourceData(crossinline block: suspend (T) -> U) = map { - when (it) { - is Resource.Success -> Resource.Success(block(it.data)) - is Resource.Intermediate -> Resource.Intermediate(block(it.data)) - is Resource.Loading -> Resource.Loading() - is Resource.Error -> Resource.Error(it.error) - } -} - -@OptIn(ExperimentalCoroutinesApi::class) -fun Flow>.flatMapResourceData( - inheritIntermediate: Boolean = true, block: suspend (T) -> Flow> -) = flatMapLatest { - when (it) { - is Resource.Success -> block(it.data) - is Resource.Intermediate -> block(it.data).map { newRes -> - if (inheritIntermediate && newRes is Resource.Success) Resource.Intermediate(newRes.data) - else newRes - } - - is Resource.Loading -> flowOf(Resource.Loading()) - is Resource.Error -> flowOf(Resource.Error(it.error)) - } +fun Flow>.mapResourceData(block: (T) -> U) = map { + it.mapData(block) } fun Flow>.onResourceData(block: suspend (T) -> Unit) = onEach { @@ -171,13 +105,13 @@ fun Flow>.onResourceSuccess(block: suspend (T) -> Unit) = onEach } } -fun Flow>.onResourceError(block: suspend (Throwable) -> Unit) = onEach { +fun Flow>.onResourceError(block: (Throwable) -> Unit) = onEach { if (it is Resource.Error) { block(it.error) } } -fun Flow>.onResourceNotLoading(block: suspend () -> Unit) = onEach { +fun Flow>.onResourceNotLoading(block: () -> Unit) = onEach { if (it !is Resource.Loading) { block() } @@ -187,99 +121,70 @@ suspend fun Flow>.toFirstResult() = filter { it !is Resource.Loa suspend fun Flow>.waitForResult() = takeWhile { it is Resource.Loading }.collect() -// Can cause excessive amounts of `Resource.Intermediate` to be emitted. Unless that is desired, -// use `debounceIntermediates` to alleviate this behavior. -inline fun combineResourceFlows(flows: Iterable>>): Flow>> = - combine(flows) { items -> - var isIntermediate = false - val data = mutableListOf() - for (item in items) { - when (item) { - is Resource.Success -> data.add(item.data) - is Resource.Intermediate -> { - isIntermediate = true - data.add(item.data) - } - - is Resource.Loading -> return@combine Resource.Loading() - is Resource.Error -> continue - } - } - if (data.isEmpty()) { - // All items have to be errors for this to happen, so just return the first one. - // mapData is functionally useless and exists only to satisfy the type checker - items.first().mapData { listOf(it) } - } else if (isIntermediate) { - Resource.Intermediate(data) - } else { - Resource.Success(data) - } - } - -@OptIn(FlowPreview::class) -fun Flow>.debounceIntermediates(timeout: Duration = 5.seconds) = flow { - var wasIntermediate = false - - emitAll(this@debounceIntermediates.debounce { - if (it is Resource.Intermediate) { - if (!wasIntermediate) { - wasIntermediate = true - Duration.ZERO - } else { - timeout - } - } else { - wasIntermediate = false - Duration.ZERO - } - }) -} - - -inline fun networkBoundResource( +inline fun networkBoundResource( mutex: Mutex = Mutex(), - crossinline isResultEmpty: (OutputType) -> Boolean, - crossinline query: () -> Flow, - crossinline fetch: suspend () -> ApiType, - crossinline saveFetchResult: suspend (old: OutputType, new: ApiType) -> Unit, - crossinline shouldFetch: (OutputType) -> Boolean = { true }, - crossinline filterResult: (OutputType) -> OutputType = { it } -) = networkBoundResource( - mutex = mutex, - isResultEmpty = isResultEmpty, - query = query, - fetch = fetch, - saveFetchResult = saveFetchResult, - shouldFetch = shouldFetch, - mapResult = filterResult -) - -@JvmName("networkBoundResourceWithMap") -inline fun networkBoundResource( - mutex: Mutex = Mutex(), - crossinline isResultEmpty: (OutputType) -> Boolean, - crossinline query: () -> Flow, - crossinline fetch: suspend () -> ApiType, - crossinline saveFetchResult: suspend (old: DatabaseType, new: ApiType) -> Unit, - crossinline shouldFetch: (DatabaseType) -> Boolean = { true }, - crossinline mapResult: (DatabaseType) -> OutputType, + showSavedOnLoading: Boolean = true, + crossinline isResultEmpty: (ResultType) -> Boolean, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline filterResult: (ResultType) -> ResultType = { it } ) = flow { emit(Resource.Loading()) val data = query().first() - if (shouldFetch(data)) { - emit(Resource.Intermediate(data)) + emitAll(if (shouldFetch(data)) { + val filteredResult = filterResult(data) + + if (showSavedOnLoading && !isResultEmpty(filteredResult)) { + emit(Resource.Intermediate(filteredResult)) + } try { - val newData = fetch() + val newData = fetch(data) mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.Success(filterResult(it)) } } catch (throwable: Throwable) { - emit(Resource.Error(throwable)) - return@flow + onFetchFailed(throwable) + flowOf(Resource.Error(throwable)) } - } - - emitAll(query().map { Resource.Success(it) }) + } else { + query().map { Resource.Success(filterResult(it)) } + }) +} + +@JvmName("networkBoundResourceWithMap") +inline fun networkBoundResource( + mutex: Mutex = Mutex(), + showSavedOnLoading: Boolean = true, + crossinline isResultEmpty: (T) -> Boolean, + crossinline query: () -> Flow, + crossinline fetch: suspend (ResultType) -> RequestType, + crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit, + crossinline onFetchFailed: (Throwable) -> Unit = { }, + crossinline shouldFetch: (ResultType) -> Boolean = { true }, + crossinline mapResult: (ResultType) -> T, +) = flow { + emit(Resource.Loading()) + + val data = query().first() + emitAll(if (shouldFetch(data)) { + val mappedResult = mapResult(data) + + if (showSavedOnLoading && !isResultEmpty(mappedResult)) { + emit(Resource.Intermediate(mappedResult)) + } + try { + val newData = fetch(data) + mutex.withLock { saveFetchResult(query().first(), newData) } + query().map { Resource.Success(mapResult(it)) } + } catch (throwable: Throwable) { + onFetchFailed(throwable) + flowOf(Resource.Error(throwable)) + } + } else { + query().map { Resource.Success(mapResult(it)) } + }) } - .mapResourceData { mapResult(it) } - .filterNot { it is Resource.Intermediate && isResultEmpty(it.data) } diff --git a/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt b/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt deleted file mode 100644 index 6d4f9eda..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/WulkanowySdkFactory.kt +++ /dev/null @@ -1,64 +0,0 @@ -package io.github.wulkanowy.data - -import com.chuckerteam.chucker.api.ChuckerInterceptor -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.sdk.Sdk -import io.github.wulkanowy.utils.RemoteConfigHelper -import io.github.wulkanowy.utils.WebkitCookieManagerProxy -import timber.log.Timber -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class WulkanowySdkFactory @Inject constructor( - private val chuckerInterceptor: ChuckerInterceptor, - private val remoteConfig: RemoteConfigHelper, - private val webkitCookieManagerProxy: WebkitCookieManagerProxy -) { - - private val sdk = Sdk().apply { - androidVersion = android.os.Build.VERSION.RELEASE - buildTag = android.os.Build.MODEL - userAgentTemplate = remoteConfig.userAgentTemplate - setSimpleHttpLogger { Timber.d(it) } - setAdditionalCookieManager(webkitCookieManagerProxy) - - // for debug only - addInterceptor(chuckerInterceptor, network = true) - } - - fun create() = sdk - - fun create(student: Student, semester: Semester? = null): Sdk { - return create().apply { - email = student.email - password = student.password - symbol = student.symbol - schoolSymbol = student.schoolSymbol - studentId = student.studentId - classId = student.classId - emptyCookieJarInterceptor = true - - if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) { - mobileBaseUrl = student.mobileBaseUrl - } else { - scrapperBaseUrl = student.scrapperBaseUrl - domainSuffix = student.scrapperDomainSuffix - loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) - } - - mode = Sdk.Mode.valueOf(student.loginMode) - mobileBaseUrl = student.mobileBaseUrl - keyId = student.certificateKey - privatePem = student.privateKey - - if (semester != null) { - diaryId = semester.diaryId - kindergartenDiaryId = semester.kindergartenDiaryId - schoolYear = semester.schoolYear - unitId = semester.unitId - } - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/enums/AttendanceCalculatorSortingMode.kt b/app/src/main/java/io/github/wulkanowy/data/enums/AttendanceCalculatorSortingMode.kt deleted file mode 100644 index 77dd5fc4..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/enums/AttendanceCalculatorSortingMode.kt +++ /dev/null @@ -1,13 +0,0 @@ -package io.github.wulkanowy.data.enums - -enum class AttendanceCalculatorSortingMode(private val value: String) { - ALPHABETIC("alphabetic"), - ATTENDANCE("attendance_percentage"), - LESSON_BALANCE("lesson_balance"); - - companion object { - fun getByValue(value: String) = - AttendanceCalculatorSortingMode.values() - .find { it.value == value } ?: ALPHABETIC - } -} diff --git a/app/src/main/java/io/github/wulkanowy/data/pojos/AttendanceData.kt b/app/src/main/java/io/github/wulkanowy/data/pojos/AttendanceData.kt deleted file mode 100644 index 5810363c..00000000 --- a/app/src/main/java/io/github/wulkanowy/data/pojos/AttendanceData.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.github.wulkanowy.data.pojos - -data class AttendanceData( - val subjectName: String, - val lessonBalance: Int, - val presences: Int, - val absences: Int, -) { - val total: Int - get() = presences + absences - - val presencePercentage: Double - get() = if (total == 0) 0.0 else (presences.toDouble() / total) * 100 -} diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt index aa0022b0..b831ee75 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AdminMessageRepository.kt @@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.dao.AdminMessageDao import io.github.wulkanowy.data.db.entities.AdminMessage import io.github.wulkanowy.data.networkBoundResource import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -29,6 +28,6 @@ class AdminMessageRepository @Inject constructor( saveFetchResult = { oldItems, newItems -> adminMessageDao.removeOldAndSaveNew(oldItems, newItems) }, + showSavedOnLoading = false, ) - .filterNot { it is Resource.Intermediate } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt index 9b94cc10..46ea29f8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.AttendanceDao import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.entities.Attendance @@ -8,11 +7,14 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex @@ -26,7 +28,7 @@ import javax.inject.Singleton class AttendanceRepository @Inject constructor( private val attendanceDb: AttendanceDao, private val timetableDb: TimetableDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -57,7 +59,8 @@ class AttendanceRepository @Inject constructor( val lessons = timetableDb.load( semester.diaryId, semester.studentId, start.monday, end.sunday ) - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getAttendance(start.monday, end.sunday) .mapToEntities(semester, lessons) }, @@ -87,10 +90,8 @@ class AttendanceRepository @Inject constructor( } suspend fun excuseForAbsence( - student: Student, - semester: Semester, - absenceList: List, - reason: String? = null + student: Student, semester: Semester, + absenceList: List, reason: String? = null ) { val items = absenceList.map { attendance -> Absent( @@ -98,7 +99,8 @@ class AttendanceRepository @Inject constructor( timeId = attendance.timeId ) } - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .excuseForAbsence(items, reason) } } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt index 78c98169..c6cfc2f6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt @@ -1,15 +1,17 @@ package io.github.wulkanowy.data.repositories import androidx.room.withTransaction -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -18,9 +20,9 @@ import javax.inject.Singleton @Singleton class AttendanceSummaryRepository @Inject constructor( private val attendanceDb: AttendanceSummaryDao, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, private val appDatabase: AppDatabase, - private val wulkanowySdkFactory: WulkanowySdkFactory, ) { private val saveFetchResultMutex = Mutex() @@ -41,7 +43,8 @@ class AttendanceSummaryRepository @Inject constructor( }, query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getAttendanceSummary(subjectId) .mapToEntities(semester, subjectId) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt index 45a36f55..f7f86b23 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt @@ -1,15 +1,17 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.CompletedLessonsDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import java.time.LocalDate @@ -19,7 +21,7 @@ import javax.inject.Singleton @Singleton class CompletedLessonsRepository @Inject constructor( private val completedLessonsDb: CompletedLessonsDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -51,7 +53,8 @@ class CompletedLessonsRepository @Inject constructor( ) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getCompletedLessons(start.monday, end.sunday) .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt index 58ce0091..fbe57860 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt @@ -1,14 +1,16 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.ConferenceDao import io.github.wulkanowy.data.db.entities.Conference import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex @@ -19,7 +21,7 @@ import javax.inject.Singleton @Singleton class ConferenceRepository @Inject constructor( private val conferenceDb: ConferenceDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -44,7 +46,8 @@ class ConferenceRepository @Inject constructor( conferenceDb.loadAll(semester.diaryId, student.studentId, startDate) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getConferences() .mapToEntities(semester) .filter { it.date >= startDate } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt index 89dbcd5c..9b8dd02e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -1,16 +1,18 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.ExamDao import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.endExamsDay import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.startExamsDay +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex @@ -21,7 +23,7 @@ import javax.inject.Singleton @Singleton class ExamRepository @Inject constructor( private val examDb: ExamDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -54,7 +56,8 @@ class ExamRepository @Inject constructor( ) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getExams(start.startExamsDay, start.endExamsDay) .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt index e899f900..ac1ef541 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.GradeDao import io.github.wulkanowy.data.db.dao.GradeDescriptiveDao import io.github.wulkanowy.data.db.dao.GradeSummaryDao @@ -11,8 +10,11 @@ import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.toLocalDate import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow @@ -28,7 +30,7 @@ class GradeRepository @Inject constructor( private val gradeDb: GradeDao, private val gradeSummaryDb: GradeSummaryDao, private val gradeDescriptiveDb: GradeDescriptiveDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -61,7 +63,8 @@ class GradeRepository @Inject constructor( } }, fetch = { - val (details, summary, descriptive) = wulkanowySdkFactory.create(student, semester) + val (details, summary, descriptive) = sdk.init(student) + .switchSemester(semester) .getGrades(semester.semesterId) Triple( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt index f120d34f..809f92d3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao @@ -13,8 +12,11 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import java.util.Locale @@ -26,7 +28,7 @@ class GradeStatisticsRepository @Inject constructor( private val gradePartialStatisticsDb: GradePartialStatisticsDao, private val gradePointsStatisticsDb: GradePointsStatisticsDao, private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -54,7 +56,8 @@ class GradeStatisticsRepository @Inject constructor( }, query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getGradesPartialStatistics(semester.semesterId) .mapToEntities(semester) }, @@ -101,7 +104,8 @@ class GradeStatisticsRepository @Inject constructor( }, query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getGradesSemesterStatistics(semester.semesterId) .mapToEntities(semester) }, @@ -159,7 +163,8 @@ class GradeStatisticsRepository @Inject constructor( }, query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getGradesPointsStatistics(semester.semesterId) .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt index 7893ef63..1a9c7ffa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt @@ -1,16 +1,18 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.HomeworkDao import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import java.time.LocalDate @@ -20,7 +22,7 @@ import javax.inject.Singleton @Singleton class HomeworkRepository @Inject constructor( private val homeworkDb: HomeworkDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -53,7 +55,8 @@ class HomeworkRepository @Inject constructor( ) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getHomework(start.monday, end.sunday) .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt index 3636cb51..45b7f6e2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/LuckyNumberRepository.kt @@ -1,11 +1,12 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.LuckyNumberDao import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex @@ -17,7 +18,7 @@ import javax.inject.Singleton @Singleton class LuckyNumberRepository @Inject constructor( private val luckyNumberDb: LuckyNumberDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, ) { private val saveFetchResultMutex = Mutex() @@ -32,9 +33,7 @@ class LuckyNumberRepository @Inject constructor( shouldFetch = { it == null || forceRefresh }, query = { luckyNumberDb.load(student.studentId, now()) }, fetch = { - wulkanowySdkFactory.create(student) - .getLuckyNumber(student.schoolShortName) - ?.mapToEntity(student) + sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) }, saveFetchResult = { oldLuckyNumber, newLuckyNumber -> newLuckyNumber ?: return@networkBoundResource diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index f91dc63e..a4517760 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -4,7 +4,6 @@ import android.content.Context import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.Resource -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.dao.MailboxDao import io.github.wulkanowy.data.db.dao.MessageAttachmentDao @@ -30,9 +29,11 @@ import io.github.wulkanowy.data.pojos.MessageDraft import io.github.wulkanowy.data.toFirstResult import io.github.wulkanowy.data.waitForResult import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.pojo.Folder import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex @@ -47,7 +48,7 @@ class MessageRepository @Inject constructor( private val messagesDb: MessagesDao, private val mutedMessageSendersDao: MutedMessageSendersDao, private val messageAttachmentDao: MessageAttachmentDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, @ApplicationContext private val context: Context, private val refreshHelper: AutoRefreshHelper, private val sharedPrefProvider: SharedPrefProvider, @@ -81,16 +82,10 @@ class MessageRepository @Inject constructor( } else messagesDb.loadMessagesWithMutedAuthor(mailbox.globalKey, folder.id) }, fetch = { - wulkanowySdkFactory.create(student) - .getMessages( - folder = Folder.valueOf(folder.name), - mailboxKey = mailbox?.globalKey, - ) - .mapToEntities( - student = student, - mailbox = mailbox, - allMailboxes = mailboxDao.loadAll(student.email) - ) + sdk.init(student).getMessages( + folder = Folder.valueOf(folder.name), + mailboxKey = mailbox?.globalKey, + ).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email)) }, saveFetchResult = { oldWithAuthors, new -> val old = oldWithAuthors.map { it.message } @@ -120,11 +115,10 @@ class MessageRepository @Inject constructor( }, query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) }, fetch = { - wulkanowySdkFactory.create(student) - .getMessageDetails( - messageKey = message.messageGlobalKey, - markAsRead = message.unread && markAsRead, - ) + sdk.init(student).getMessageDetails( + messageKey = it!!.message.messageGlobalKey, + markAsRead = message.unread && markAsRead, + ) }, saveFetchResult = { old, new -> checkNotNull(old) { "Fetched message no longer exist!" } @@ -165,19 +159,19 @@ class MessageRepository @Inject constructor( recipients: List, mailbox: Mailbox, ) { - wulkanowySdkFactory.create(student) - .sendMessage( - subject = subject, - content = content, - recipients = recipients.mapFromEntities(), - mailboxId = mailbox.globalKey, - ) + sdk.init(student).sendMessage( + subject = subject, + content = content, + recipients = recipients.mapFromEntities(), + mailboxId = mailbox.globalKey, + ) refreshFolders(student, mailbox, listOf(SENT)) } suspend fun restoreMessages(student: Student, mailbox: Mailbox?, messages: List) { - wulkanowySdkFactory.create(student) - .restoreMessages(messages = messages.map { it.messageGlobalKey }) + sdk.init(student).restoreMessages( + messages = messages.map { it.messageGlobalKey }, + ) refreshFolders(student, mailbox) } @@ -188,11 +182,10 @@ class MessageRepository @Inject constructor( suspend fun deleteMessages(student: Student, messages: List) { val firstMessage = messages.first() - wulkanowySdkFactory.create(student) - .deleteMessages( - messages = messages.map { it.messageGlobalKey }, - removeForever = firstMessage.folderId == TRASHED.id, - ) + sdk.init(student).deleteMessages( + messages = messages.map { it.messageGlobalKey }, + removeForever = firstMessage.folderId == TRASHED.id, + ) if (firstMessage.folderId != TRASHED.id) { val deletedMessages = messages.map { @@ -237,9 +230,7 @@ class MessageRepository @Inject constructor( }, query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) }, fetch = { - wulkanowySdkFactory.create(student) - .getMailboxes() - .mapToEntities(student) + sdk.init(student).getMailboxes().mapToEntities(student) }, saveFetchResult = { old, new -> mailboxDao.deleteAll(old uniqueSubtract new) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt index 19466554..48b4fc28 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.MobileDeviceDao import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.Semester @@ -9,8 +8,11 @@ import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.MobileDeviceToken +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -19,7 +21,7 @@ import javax.inject.Singleton @Singleton class MobileDeviceRepository @Inject constructor( private val mobileDb: MobileDeviceDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -40,7 +42,8 @@ class MobileDeviceRepository @Inject constructor( }, query = { mobileDb.loadAll(student.userLoginId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getRegisteredDevices() .mapToEntities(student) }, @@ -54,14 +57,16 @@ class MobileDeviceRepository @Inject constructor( ) suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .unregisterDevice(device.deviceId) mobileDb.deleteAll(listOf(device)) } suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken { - return wulkanowySdkFactory.create(student, semester) + return sdk.init(student) + .switchSemester(semester) .getToken() .mapToMobileDeviceToken() } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt index 9551e01e..feb92c15 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt @@ -1,14 +1,16 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.NoteDao import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.toLocalDate import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow @@ -19,7 +21,7 @@ import javax.inject.Singleton @Singleton class NoteRepository @Inject constructor( private val noteDb: NoteDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -43,7 +45,8 @@ class NoteRepository @Inject constructor( }, query = { noteDb.loadAll(student.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getNotes() .mapToEntities(semester) }, 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 2bb1538c..64e60a60 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 @@ -10,7 +10,6 @@ import com.fredporciuncula.flow.preferences.Serializer import dagger.hilt.android.qualifiers.ApplicationContext import io.github.wulkanowy.R import io.github.wulkanowy.data.enums.AppTheme -import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode import io.github.wulkanowy.data.enums.GradeColorTheme import io.github.wulkanowy.data.enums.GradeExpandMode import io.github.wulkanowy.data.enums.GradeSortingMode @@ -42,27 +41,6 @@ class PreferencesRepository @Inject constructor( R.bool.pref_default_attendance_present ) - val targetAttendanceFlow: Flow - get() = flowSharedPref.getInt( - context.getString(R.string.pref_key_attendance_target), - context.resources.getInteger(R.integer.pref_default_attendance_target) - ).asFlow() - - val attendanceCalculatorSortingModeFlow: Flow - get() = flowSharedPref.getString( - context.getString(R.string.pref_key_attendance_calculator_sorting_mode), - context.resources.getString(R.string.pref_default_attendance_calculator_sorting_mode) - ).asFlow().map(AttendanceCalculatorSortingMode::getByValue) - - /** - * Subjects are empty when they don't have any attendances (total = 0, attendances = 0, absences = 0). - */ - val attendanceCalculatorShowEmptySubjects: Flow - get() = flowSharedPref.getBoolean( - context.getString(R.string.pref_key_attendance_calculator_show_empty_subjects), - context.resources.getBoolean(R.bool.pref_default_attendance_calculator_show_empty_subjects) - ).asFlow() - private val gradeAverageModePref: Preference get() = getObjectFlow( R.string.pref_key_grade_average_mode, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt index 8233d932..4a1474ce 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecipientRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.RecipientDao import io.github.wulkanowy.data.db.entities.Mailbox import io.github.wulkanowy.data.db.entities.MailboxType @@ -8,8 +7,10 @@ import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import javax.inject.Inject import javax.inject.Singleton @@ -17,15 +18,14 @@ import javax.inject.Singleton @Singleton class RecipientRepository @Inject constructor( private val recipientDb: RecipientDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { private val cacheKey = "recipient" suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) { - val new = wulkanowySdkFactory.create(student) - .getRecipients(mailbox.globalKey) + val new = sdk.init(student).getRecipients(mailbox.globalKey) .mapToEntities(mailbox.globalKey) val old = recipientDb.loadAll(type, mailbox.globalKey) @@ -60,7 +60,7 @@ class RecipientRepository @Inject constructor( ): List { mailbox ?: return emptyList() - return wulkanowySdkFactory.create(student) + return sdk.init(student) .getMessageReplayDetails(message.messageGlobalKey) .sender .let(::listOf) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt index b554bda0..5940f477 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/RecoverRepository.kt @@ -1,23 +1,17 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory +import io.github.wulkanowy.sdk.Sdk import javax.inject.Inject import javax.inject.Singleton @Singleton -class RecoverRepository @Inject constructor( - private val wulkanowySdkFactory: WulkanowySdkFactory -) { +class RecoverRepository @Inject constructor(private val sdk: Sdk) { - suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair = - wulkanowySdkFactory.create() - .getPasswordResetCaptchaCode(host, symbol) + suspend fun getReCaptchaSiteKey(host: String, symbol: String): Pair { + return sdk.getPasswordResetCaptchaCode(host, symbol) + } suspend fun sendRecoverRequest( - url: String, - symbol: String, - email: String, - reCaptchaResponse: String - ): String = wulkanowySdkFactory.create() - .sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) + url: String, symbol: String, email: String, reCaptchaResponse: String + ): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt index 6a04ce75..f09a46aa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolAnnouncementRepository.kt @@ -1,13 +1,14 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao import io.github.wulkanowy.data.db.entities.SchoolAnnouncement import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.sync.Mutex @@ -17,7 +18,7 @@ import javax.inject.Singleton @Singleton class SchoolAnnouncementRepository @Inject constructor( private val schoolAnnouncementDb: SchoolAnnouncementDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -40,7 +41,7 @@ class SchoolAnnouncementRepository @Inject constructor( schoolAnnouncementDb.loadAll(student.userLoginId) }, fetch = { - val sdk = wulkanowySdkFactory.create(student) + val sdk = sdk.init(student) val lastAnnouncements = sdk.getLastAnnouncements().mapToEntities(student) val directorInformation = sdk.getDirectorInformation().mapToEntities(student) lastAnnouncements + directorInformation diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt index c48abb6f..b42b4d57 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SchoolRepository.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.SchoolDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -15,7 +17,7 @@ import javax.inject.Singleton @Singleton class SchoolRepository @Inject constructor( private val schoolDb: SchoolDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -38,7 +40,8 @@ class SchoolRepository @Inject constructor( }, query = { schoolDb.load(semester.studentId, semester.classId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getSchool() .mapToEntity(semester) }, 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..216a8c11 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,15 +1,17 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.api.SchoolsService import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.StudentWithSemesters import io.github.wulkanowy.data.pojos.IntegrityRequest import io.github.wulkanowy.data.pojos.LoginEvent +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.modules.login.LoginData import io.github.wulkanowy.utils.IntegrityHelper import io.github.wulkanowy.utils.getCurrentOrLast +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import kotlinx.coroutines.withTimeout import timber.log.Timber import java.util.UUID @@ -21,7 +23,7 @@ import kotlin.time.Duration.Companion.seconds class SchoolsRepository @Inject constructor( private val integrityHelper: IntegrityHelper, private val schoolsService: SchoolsService, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, ) { suspend fun logSchoolLogin(loginData: LoginData, students: List) { @@ -38,9 +40,10 @@ class SchoolsRepository @Inject constructor( private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) { val requestId = UUID.randomUUID().toString() val token = integrityHelper.getIntegrityToken(requestId) ?: return - val updatedStudent = student.copy(password = loginData.password) - val schoolInfo = wulkanowySdkFactory.create(updatedStudent, semester) + val schoolInfo = sdk + .init(student.copy(password = loginData.password)) + .switchSemester(semester) .getSchool() schoolsService.logLoginEvent( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt index da21f59a..9ae22bab 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SemesterRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student @@ -8,6 +7,7 @@ import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.getCurrentOrLast +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.withContext @@ -18,7 +18,7 @@ import javax.inject.Singleton @Singleton class SemesterRepository @Inject constructor( private val semesterDb: SemesterDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val dispatchers: DispatchersProvider, ) { @@ -60,10 +60,7 @@ class SemesterRepository @Inject constructor( } private suspend fun refreshSemesters(student: Student) { - val new = wulkanowySdkFactory.create(student) - .getSemesters() - .mapToEntities(student.studentId) - + val new = sdk.init(student).getSemesters().mapToEntities(student.studentId) if (new.isEmpty()) return Timber.i("Empty semester list!") val old = semesterDb.loadAll(student.studentId, student.classId) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt index db4c0aeb..d42be180 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentInfoRepository.kt @@ -1,11 +1,13 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.StudentInfoDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntity import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import kotlinx.coroutines.sync.Mutex import javax.inject.Inject import javax.inject.Singleton @@ -13,7 +15,7 @@ import javax.inject.Singleton @Singleton class StudentInfoRepository @Inject constructor( private val studentInfoDao: StudentInfoDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, ) { private val saveFetchResultMutex = Mutex() @@ -28,9 +30,9 @@ class StudentInfoRepository @Inject constructor( shouldFetch = { it == null || forceRefresh }, query = { studentInfoDao.loadStudentInfo(student.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) - .getStudentInfo() - .mapToEntity(semester) + sdk.init(student) + .switchSemester(semester) + .getStudentInfo().mapToEntity(semester) }, saveFetchResult = { old, new -> if (old != null && new != old) { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt index 9a5ecd53..e063840c 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/StudentRepository.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.data.repositories import androidx.room.withTransaction -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.dao.SemesterDao import io.github.wulkanowy.data.db.dao.StudentDao @@ -15,7 +14,9 @@ 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.init import io.github.wulkanowy.utils.security.Scrambler +import io.github.wulkanowy.utils.switchSemester import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @@ -25,7 +26,7 @@ class StudentRepository @Inject constructor( private val dispatchers: DispatchersProvider, private val studentDb: StudentDao, private val semesterDb: SemesterDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val appDatabase: AppDatabase, private val scrambler: Scrambler, ) { @@ -36,7 +37,7 @@ class StudentRepository @Inject constructor( pin: String, symbol: String, token: String - ): RegisterUser = wulkanowySdkFactory.create() + ): RegisterUser = sdk .getStudentsFromHebe(token, pin, symbol, "") .mapToPojo(null) @@ -46,7 +47,7 @@ class StudentRepository @Inject constructor( scrapperBaseUrl: String, domainSuffix: String, symbol: String - ): RegisterUser = wulkanowySdkFactory.create() + ): RegisterUser = sdk .getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, domainSuffix, symbol) .mapToPojo(password) @@ -55,7 +56,7 @@ class StudentRepository @Inject constructor( password: String, scrapperBaseUrl: String, symbol: String - ): RegisterUser = wulkanowySdkFactory.create() + ): RegisterUser = sdk .getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol) .mapToPojo(password) @@ -148,11 +149,13 @@ class StudentRepository @Inject constructor( .distinctBy { it.student.studentName }.size == 1 suspend fun authorizePermission(student: Student, semester: Semester, pesel: String) = - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .authorizePermission(pesel) suspend fun refreshStudentName(student: Student, semester: Semester) { - val newCurrentApiStudent = wulkanowySdkFactory.create(student, semester) + val newCurrentApiStudent = sdk.init(student) + .switchSemester(semester) .getCurrentStudent() ?: return val studentName = StudentName( diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt index 573c7c14..cf7f86c2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/SubjectRepository.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.SubjectDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -16,7 +18,7 @@ import javax.inject.Singleton @Singleton class SubjectRepository @Inject constructor( private val subjectDao: SubjectDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -37,7 +39,8 @@ class SubjectRepository @Inject constructor( }, query = { subjectDao.loadAll(semester.diaryId, semester.studentId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getSubjects() .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt index a5a6e3f9..5a488b27 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TeacherRepository.kt @@ -1,13 +1,15 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.TeacherDao import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.sync.Mutex import javax.inject.Inject @@ -16,7 +18,7 @@ import javax.inject.Singleton @Singleton class TeacherRepository @Inject constructor( private val teacherDb: TeacherDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val refreshHelper: AutoRefreshHelper, ) { @@ -37,7 +39,8 @@ class TeacherRepository @Inject constructor( }, query = { teacherDb.loadAll(semester.studentId, semester.classId) }, fetch = { - wulkanowySdkFactory.create(student, semester) + sdk.init(student) + .switchSemester(semester) .getTeachers() .mapToEntities(semester) }, diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt index 33578999..0d208c1f 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.data.repositories -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao import io.github.wulkanowy.data.db.dao.TimetableDao import io.github.wulkanowy.data.db.dao.TimetableHeaderDao @@ -12,11 +11,14 @@ import io.github.wulkanowy.data.db.entities.TimetableHeader import io.github.wulkanowy.data.mappers.mapToEntities import io.github.wulkanowy.data.networkBoundResource import io.github.wulkanowy.data.pojos.TimetableFull +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper import io.github.wulkanowy.utils.AutoRefreshHelper import io.github.wulkanowy.utils.getRefreshKey +import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.switchSemester import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -31,7 +33,7 @@ class TimetableRepository @Inject constructor( private val timetableDb: TimetableDao, private val timetableAdditionalDb: TimetableAdditionalDao, private val timetableHeaderDb: TimetableHeaderDao, - private val wulkanowySdkFactory: WulkanowySdkFactory, + private val sdk: Sdk, private val schedulerHelper: TimetableNotificationSchedulerHelper, private val refreshHelper: AutoRefreshHelper, ) { @@ -72,7 +74,8 @@ class TimetableRepository @Inject constructor( }, query = { getFullTimetableFromDatabase(student, semester, start, end) }, fetch = { - val timetableFull = wulkanowySdkFactory.create(student, semester) + val timetableFull = sdk.init(student) + .switchSemester(semester) .getTimetable(start.monday, end.sunday) timetableFull.mapToEntities(semester) diff --git a/app/src/main/java/io/github/wulkanowy/domain/attendance/GetAttendanceCalculatorDataUseCase.kt b/app/src/main/java/io/github/wulkanowy/domain/attendance/GetAttendanceCalculatorDataUseCase.kt deleted file mode 100644 index 294abd1b..00000000 --- a/app/src/main/java/io/github/wulkanowy/domain/attendance/GetAttendanceCalculatorDataUseCase.kt +++ /dev/null @@ -1,106 +0,0 @@ -package io.github.wulkanowy.domain.attendance - -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.db.entities.AttendanceSummary -import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.data.db.entities.Subject -import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode -import io.github.wulkanowy.data.enums.AttendanceCalculatorSortingMode.* -import io.github.wulkanowy.data.pojos.AttendanceData -import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository -import io.github.wulkanowy.data.repositories.PreferencesRepository -import io.github.wulkanowy.data.repositories.SubjectRepository -import io.github.wulkanowy.utils.allAbsences -import io.github.wulkanowy.utils.allPresences -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject -import kotlin.math.ceil -import kotlin.math.floor - -class GetAttendanceCalculatorDataUseCase @Inject constructor( - private val subjectRepository: SubjectRepository, - private val attendanceSummaryRepository: AttendanceSummaryRepository, - private val preferencesRepository: PreferencesRepository, -) { - - operator fun invoke( - student: Student, - semester: Semester, - forceRefresh: Boolean, - ): Flow>> = - subjectRepository.getSubjects(student, semester, forceRefresh) - .mapResourceData { subjects -> subjects.sortedBy(Subject::name) } - .combineWithResourceData(preferencesRepository.targetAttendanceFlow, ::Pair) - .flatMapResourceData { (subjects, targetFreq) -> - combineResourceFlows(subjects.map { subject -> - attendanceSummaryRepository.getAttendanceSummary( - student = student, - semester = semester, - subjectId = subject.realId, - forceRefresh = forceRefresh - ).mapResourceData { summaries -> - summaries.toAttendanceData(subject.name, targetFreq) - } - }) - // Every individual combined flow causes separate network requests to update data. - // When there is N child flows, they can cause up to N-1 items to be emitted. Since all - // requests are usually completed in less than 5s, there is no need to emit multiple - // intermediates that will be visible for barely any time. - .debounceIntermediates() - } - .combineWithResourceData(preferencesRepository.attendanceCalculatorShowEmptySubjects) { attendanceDataList, showEmptySubjects -> - attendanceDataList.filter { it.total != 0 || showEmptySubjects } - } - .combineWithResourceData(preferencesRepository.attendanceCalculatorSortingModeFlow, List::sortedBy) -} - -private fun List.toAttendanceData(subjectName: String, targetFreq: Int): AttendanceData { - val presences = sumOf { it.allPresences } - val absences = sumOf { it.allAbsences } - return AttendanceData( - subjectName = subjectName, - lessonBalance = calcLessonBalance( - targetFreq.toDouble() / 100, presences, absences - ), - presences = presences, - absences = absences, - ) -} - -private fun calcLessonBalance(targetFreq: Double, presences: Int, absences: Int): Int { - val total = presences + absences - // The `+ 1` is to avoid false positives in close cases. Eg.: - // target frequency 99%, 1 presence. Without the `+ 1` this would be reported shown as - // a positive balance of +1, however that is not actually true as skipping one class - // would make it so that the balance would actually be negative (-98). The `+ 1` - // fixes this and makes sure that in situations like these, it's not reporting incorrect - // balances - return when { - presences / (total + 1f) >= targetFreq -> calcMissingAbsences( - targetFreq, absences, presences - ) - presences / (total + 0f) < targetFreq -> -calcMissingPresences( - targetFreq, absences, presences - ) - else -> 0 - } -} - -private fun calcMissingPresences(targetFreq: Double, absences: Int, presences: Int) = - calcMinRequiredPresencesFor(targetFreq, absences) - presences - -private fun calcMinRequiredPresencesFor(targetFreq: Double, absences: Int) = - ceil((targetFreq / (1 - targetFreq)) * absences).toInt() - -private fun calcMissingAbsences(targetFreq: Double, absences: Int, presences: Int) = - calcMinRequiredAbsencesFor(targetFreq, presences) - absences - -private fun calcMinRequiredAbsencesFor(targetFreq: Double, presences: Int) = - floor((presences * (1 - targetFreq)) / targetFreq).toInt() - -private fun List.sortedBy(mode: AttendanceCalculatorSortingMode) = when (mode) { - ALPHABETIC -> sortedBy(AttendanceData::subjectName) - ATTENDANCE -> sortedByDescending(AttendanceData::presencePercentage) - LESSON_BALANCE -> sortedBy(AttendanceData::lessonBalance) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt index 922c3536..10735dab 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/base/BaseActivity.kt @@ -1,7 +1,6 @@ package io.github.wulkanowy.ui.base import android.app.ActivityManager -import android.os.Build import android.os.Bundle import android.view.View import android.widget.Toast @@ -46,19 +45,11 @@ abstract class BaseActivity, VB : ViewBinding> : themeManager.applyActivityTheme(this) super.onCreate(savedInstanceState) supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleLogger, true) - applyCustomTaskDescription() - } - @Suppress("DEPRECATION") - private fun applyCustomTaskDescription() { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) return - try { - val newColor = getThemeAttrColor(R.attr.colorSurface) - val taskDescription = ActivityManager.TaskDescription(null, null, newColor) - setTaskDescription(taskDescription) - } catch (e: Exception) { - Timber.e(e) - } + @Suppress("DEPRECATION") + setTaskDescription( + ActivityManager.TaskDescription(null, null, getThemeAttrColor(R.attr.colorSurface)) + ) } override fun showError(text: String, error: Throwable) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt index f5689ec8..4e9baac3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceAdapter.kt @@ -1,6 +1,5 @@ package io.github.wulkanowy.ui.modules.attendance -import android.content.res.ColorStateList import android.graphics.Typeface import android.view.LayoutInflater import android.view.View @@ -34,17 +33,17 @@ class AttendanceAdapter @Inject constructor() : ) override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { - val context = holder.binding.root.context val item = items[position] with(holder.binding) { attendanceItemNumber.text = item.number.toString() - attendanceItemSubject.text = item.subject - .ifBlank { context.getString(R.string.all_no_data) } + attendanceItemSubject.text = item.subject.ifBlank { + root.context.getString(R.string.all_no_data) + } attendanceItemDescription.setText(item.descriptionRes) attendanceItemDescription.setTextColor( - context.getThemeAttrColor( + root.context.getThemeAttrColor( when { item.absence && !item.excused -> R.attr.colorAttendanceAbsence item.lateness && !item.excused -> R.attr.colorAttendanceLateness @@ -62,15 +61,13 @@ class AttendanceAdapter @Inject constructor() : attendanceItemAlert.isVisible = item.let { (it.absence && !it.excused) || (it.lateness && !it.excused) } - attendanceItemAlert.imageTintList = ColorStateList.valueOf( - context.getThemeAttrColor( - when { - item.absence && !item.excused -> R.attr.colorAttendanceAbsence - item.lateness && !item.excused -> R.attr.colorAttendanceLateness - else -> android.R.attr.colorPrimary - } - ) - ) + attendanceItemAlert.setColorFilter(root.context.getThemeAttrColor( + when{ + item.absence && !item.excused -> R.attr.colorAttendanceAbsence + item.lateness && !item.excused -> R.attr.colorAttendanceLateness + else -> android.R.attr.colorPrimary + } + )) attendanceItemNumber.visibility = View.GONE attendanceItemExcuseInfo.visibility = View.GONE attendanceItemExcuseCheckbox.visibility = View.GONE diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index 07649e43..6e842b4d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -14,7 +14,6 @@ import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.databinding.DialogExcuseBinding import io.github.wulkanowy.databinding.FragmentAttendanceBinding import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.attendance.calculator.AttendanceCalculatorFragment import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainView @@ -135,7 +134,6 @@ class AttendanceFragment : BaseFragment(R.layout.frag override fun onOptionsItemSelected(item: MenuItem): Boolean { return if (item.itemId == R.id.attendanceMenuSummary) presenter.onSummarySwitchSelected() - else if (item.itemId == R.id.attendanceMenuCalculator) presenter.onCalculatorSwitchSelected() else false } @@ -255,10 +253,6 @@ class AttendanceFragment : BaseFragment(R.layout.frag (activity as? MainActivity)?.pushView(AttendanceSummaryFragment.newInstance()) } - override fun openCalculatorView() { - (activity as? MainActivity)?.pushView(AttendanceCalculatorFragment.newInstance()) - } - override fun startActionMode() { actionMode = (activity as MainActivity?)?.startSupportActionMode(actionModeCallback) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 586a41ad..82fe69cb 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -1,36 +1,16 @@ package io.github.wulkanowy.ui.modules.attendance import android.annotation.SuppressLint -import io.github.wulkanowy.data.Resource +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Semester -import io.github.wulkanowy.data.flatResourceFlow -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.mapResourceData -import io.github.wulkanowy.data.onResourceData -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceIntermediate -import io.github.wulkanowy.data.onResourceLoading -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.AttendanceRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.SemesterRepository import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.data.resourceFlow import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.ErrorHandler -import io.github.wulkanowy.utils.AnalyticsHelper -import io.github.wulkanowy.utils.capitalise -import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday -import io.github.wulkanowy.utils.isExcusableOrNotExcused -import io.github.wulkanowy.utils.isHolidays -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.nextSchoolDay -import io.github.wulkanowy.utils.previousOrSameSchoolDay -import io.github.wulkanowy.utils.previousSchoolDay -import io.github.wulkanowy.utils.sunday -import io.github.wulkanowy.utils.toFormattedString +import io.github.wulkanowy.utils.* import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -215,11 +195,6 @@ class AttendancePresenter @Inject constructor( return true } - fun onCalculatorSwitchSelected(): Boolean { - view?.openCalculatorView() - return true - } - private fun loadData(forceRefresh: Boolean = false) { Timber.i("Loading attendance data started") diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index f51ce7c7..2629c217 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -56,8 +56,6 @@ interface AttendanceView : BaseView { fun openSummaryView() - fun openCalculatorView() - fun startSendMessageIntent(date: LocalDate, numbers: String, reason: String) fun startActionMode() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorAdapter.kt deleted file mode 100644 index 4b908bba..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorAdapter.kt +++ /dev/null @@ -1,67 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.calculator - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.RecyclerView -import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.AttendanceData -import io.github.wulkanowy.databinding.ItemAttendanceCalculatorHeaderBinding -import javax.inject.Inject -import kotlin.math.abs -import kotlin.math.roundToInt - -class AttendanceCalculatorAdapter @Inject constructor() : - RecyclerView.Adapter() { - - var items = emptyList() - - override fun getItemCount() = items.size - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder( - ItemAttendanceCalculatorHeaderBinding.inflate( - LayoutInflater.from(parent.context), parent, false - ) - ) - - override fun onBindViewHolder(parent: ViewHolder, position: Int) { - val context = parent.binding.root.context - val item = items[position] - - with(parent.binding) { - attendanceCalculatorPercentage.text = "${item.presencePercentage.roundToInt()}" - - attendanceCalculatorSummaryBalance.text = when { - item.lessonBalance > 0 -> { - context.getString( - R.string.attendance_calculator_summary_balance_positive, - item.lessonBalance - ) - } - - item.lessonBalance < 0 -> { - context.getString( - R.string.attendance_calculator_summary_balance_negative, - abs(item.lessonBalance) - ) - } - - else -> context.getString(R.string.attendance_calculator_summary_balance_neutral) - } - attendanceCalculatorWarning.isVisible = item.lessonBalance < 0 - attendanceCalculatorTitle.text = item.subjectName - attendanceCalculatorSummaryValues.text = if (item.total == 0) { - context.getString(R.string.attendance_calculator_summary_values_empty) - } else { - context.getString( - R.string.attendance_calculator_summary_values, - item.presences, - item.total - ) - } - } - } - - class ViewHolder(val binding: ItemAttendanceCalculatorHeaderBinding) : - RecyclerView.ViewHolder(binding.root) -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorFragment.kt deleted file mode 100644 index 2d566701..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorFragment.kt +++ /dev/null @@ -1,105 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.calculator - -import android.os.Bundle -import android.view.View -import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager -import dagger.hilt.android.AndroidEntryPoint -import io.github.wulkanowy.R -import io.github.wulkanowy.data.pojos.AttendanceData -import io.github.wulkanowy.databinding.FragmentAttendanceCalculatorBinding -import io.github.wulkanowy.ui.base.BaseFragment -import io.github.wulkanowy.ui.modules.main.MainView -import io.github.wulkanowy.ui.widgets.DividerItemDecoration -import io.github.wulkanowy.utils.getThemeAttrColor -import javax.inject.Inject - -@AndroidEntryPoint -class AttendanceCalculatorFragment : - BaseFragment(R.layout.fragment_attendance_calculator), - AttendanceCalculatorView, MainView.TitledView { - - @Inject - lateinit var presenter: AttendanceCalculatorPresenter - - @Inject - lateinit var attendanceCalculatorAdapter: AttendanceCalculatorAdapter - - override val titleStringId get() = R.string.attendance_title - - companion object { - fun newInstance() = AttendanceCalculatorFragment() - } - - override val isViewEmpty get() = attendanceCalculatorAdapter.items.isEmpty() - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentAttendanceCalculatorBinding.bind(view) - messageContainer = binding.attendanceCalculatorRecycler - presenter.onAttachView(this) - } - - override fun initView() { - with(binding.attendanceCalculatorRecycler) { - layoutManager = LinearLayoutManager(context) - adapter = attendanceCalculatorAdapter - addItemDecoration(DividerItemDecoration(context)) - } - - with(binding) { - attendanceCalculatorSwipe.setOnRefreshListener(presenter::onSwipeRefresh) - attendanceCalculatorSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) - attendanceCalculatorSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) - attendanceCalculatorErrorRetry.setOnClickListener { presenter.onRetry() } - attendanceCalculatorErrorDetails.setOnClickListener { presenter.onDetailsClick() } - } - } - - override fun updateData(data: List) { - with(attendanceCalculatorAdapter) { - items = data - notifyDataSetChanged() - } - } - - override fun clearView() { - with(attendanceCalculatorAdapter) { - items = emptyList() - notifyDataSetChanged() - } - } - - override fun showEmpty(show: Boolean) { - binding.attendanceCalculatorEmpty.isVisible = show - } - - override fun showErrorView(show: Boolean) { - binding.attendanceCalculatorError.isVisible = show - } - - override fun setErrorDetails(message: String) { - binding.attendanceCalculatorErrorMessage.text = message - } - - override fun showProgress(show: Boolean) { - binding.attendanceCalculatorProgress.isVisible = show - } - - override fun enableSwipe(enable: Boolean) { - binding.attendanceCalculatorSwipe.isEnabled = enable - } - - override fun showContent(show: Boolean) { - binding.attendanceCalculatorRecycler.isVisible = show - } - - override fun showRefresh(show: Boolean) { - binding.attendanceCalculatorSwipe.isRefreshing = show - } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorPresenter.kt deleted file mode 100644 index d292e565..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorPresenter.kt +++ /dev/null @@ -1,84 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.calculator - -import io.github.wulkanowy.data.* -import io.github.wulkanowy.data.repositories.SemesterRepository -import io.github.wulkanowy.data.repositories.StudentRepository -import io.github.wulkanowy.domain.attendance.GetAttendanceCalculatorDataUseCase -import io.github.wulkanowy.ui.base.BasePresenter -import io.github.wulkanowy.ui.base.ErrorHandler -import timber.log.Timber -import javax.inject.Inject - -class AttendanceCalculatorPresenter @Inject constructor( - errorHandler: ErrorHandler, - studentRepository: StudentRepository, - private val semesterRepository: SemesterRepository, - private val getAttendanceCalculatorData: GetAttendanceCalculatorDataUseCase, -) : BasePresenter(errorHandler, studentRepository) { - - private lateinit var lastError: Throwable - - override fun onAttachView(view: AttendanceCalculatorView) { - super.onAttachView(view) - view.initView() - Timber.i("Attendance calculator view was initialized") - errorHandler.showErrorMessage = ::showErrorViewOnError - loadData() - } - - fun onSwipeRefresh() { - Timber.i("Force refreshing the attendance calculator") - loadData(forceRefresh = true) - } - - fun onRetry() { - view?.run { - showErrorView(false) - showProgress(true) - } - loadData() - } - - fun onDetailsClick() { - view?.showErrorDetailsDialog(lastError) - } - - private fun loadData(forceRefresh: Boolean = false) { - flatResourceFlow { - val student = studentRepository.getCurrentStudent() - val semester = semesterRepository.getCurrentSemester(student) - getAttendanceCalculatorData(student, semester, forceRefresh) - } - .logResourceStatus("load attendance calculator") - .onResourceData { - view?.run { - showProgress(false) - showErrorView(false) - showContent(it.isNotEmpty()) - showEmpty(it.isEmpty()) - updateData(it) - } - } - .onResourceIntermediate { view?.showRefresh(true) } - .onResourceNotLoading { - view?.run { - enableSwipe(true) - showRefresh(false) - showProgress(false) - } - } - .onResourceError(errorHandler::dispatch) - .launch() - } - - private fun showErrorViewOnError(message: String, error: Throwable) { - view?.run { - if (isViewEmpty) { - lastError = error - setErrorDetails(message) - showErrorView(true) - showEmpty(false) - } else showError(message, error) - } - } -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorView.kt deleted file mode 100644 index 94e66121..00000000 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/calculator/AttendanceCalculatorView.kt +++ /dev/null @@ -1,29 +0,0 @@ -package io.github.wulkanowy.ui.modules.attendance.calculator - -import io.github.wulkanowy.data.pojos.AttendanceData -import io.github.wulkanowy.ui.base.BaseView - -interface AttendanceCalculatorView : BaseView { - - val isViewEmpty: Boolean - - fun initView() - - fun showRefresh(show: Boolean) - - fun showContent(show: Boolean) - - fun showProgress(show: Boolean) - - fun enableSwipe(enable: Boolean) - - fun showEmpty(show: Boolean) - - fun showErrorView(show: Boolean) - - fun setErrorDetails(message: String) - - fun updateData(data: List) - - fun clearView() -} diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt index 0f7c4234..fa29df47 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/auth/AuthDialog.kt @@ -78,9 +78,4 @@ class AuthDialog : BaseDialogFragment(), AuthView { override fun showDescriptionWithName(name: String) { binding.authDescription.text = getString(R.string.auth_description, name).parseAsHtml() } - - override fun onDestroyView() { - presenter.onDetachView() - super.onDestroyView() - } } 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..98b4fda7 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 @@ -10,8 +10,8 @@ import android.webkit.WebViewClient import androidx.core.os.bundleOf import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R -import io.github.wulkanowy.data.WulkanowySdkFactory import io.github.wulkanowy.databinding.DialogCaptchaBinding +import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.ui.base.BaseDialogFragment import io.github.wulkanowy.utils.WebkitCookieManagerProxy import timber.log.Timber @@ -21,7 +21,7 @@ import javax.inject.Inject class CaptchaDialog : BaseDialogFragment() { @Inject - lateinit var wulkanowySdkFactory: WulkanowySdkFactory + lateinit var sdk: Sdk @Inject lateinit var webkitCookieManagerProxy: WebkitCookieManagerProxy @@ -59,7 +59,7 @@ class CaptchaDialog : BaseDialogFragment() { webView = this with(settings) { javaScriptEnabled = true - userAgentString = wulkanowySdkFactory.create().userAgent + userAgentString = sdk.userAgent } webViewClient = object : WebViewClient() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt index 11d3c6c1..8bd84f2b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/mailboxchooser/MailboxChooserDialog.kt @@ -47,6 +47,7 @@ class MailboxChooserDialog : BaseDialogFragment(), } + @Suppress("UNCHECKED_CAST") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) presenter.onAttachView( diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt index 8e7c7276..75778bac 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewFragment.kt @@ -82,10 +82,10 @@ class MessagePreviewFragment : get() = getString(R.string.message_not_exists) companion object { - private const val MESSAGE_ARG_KEY = "message" + const val MESSAGE_ID_KEY = "message_id" fun newInstance(message: Message) = MessagePreviewFragment().apply { - arguments = bundleOf(MESSAGE_ARG_KEY to message) + arguments = bundleOf(MESSAGE_ID_KEY to message) } } @@ -101,7 +101,7 @@ class MessagePreviewFragment : messageContainer = binding.messagePreviewContainer presenter.onAttachView( view = this, - message = requireArguments().serializable(MESSAGE_ARG_KEY), + message = (savedInstanceState ?: arguments)?.serializable(MESSAGE_ID_KEY), ) } @@ -233,6 +233,11 @@ class MessagePreviewFragment : (activity as MainActivity).popView() } + override fun onSaveInstanceState(outState: Bundle) { + outState.putSerializable(MESSAGE_ID_KEY, presenter.messageWithAttachments) + super.onSaveInstanceState(outState) + } + override fun onDestroyView() { presenter.onDetachView() super.onDestroyView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt index 3b3b2b42..9bb0d32a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/preview/MessagePreviewPresenter.kt @@ -3,15 +3,10 @@ package io.github.wulkanowy.ui.modules.message.preview import android.annotation.SuppressLint import androidx.core.text.parseAsHtml import io.github.wulkanowy.R +import io.github.wulkanowy.data.* import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.enums.MessageFolder -import io.github.wulkanowy.data.flatResourceFlow -import io.github.wulkanowy.data.logResourceStatus -import io.github.wulkanowy.data.onResourceData -import io.github.wulkanowy.data.onResourceError -import io.github.wulkanowy.data.onResourceNotLoading -import io.github.wulkanowy.data.onResourceSuccess import io.github.wulkanowy.data.repositories.MessageRepository import io.github.wulkanowy.data.repositories.PreferencesRepository import io.github.wulkanowy.data.repositories.StudentRepository @@ -33,17 +28,17 @@ class MessagePreviewPresenter @Inject constructor( private val analytics: AnalyticsHelper ) : BasePresenter(errorHandler, studentRepository) { - private var messageWithAttachments: MessageWithAttachment? = null + var messageWithAttachments: MessageWithAttachment? = null private lateinit var lastError: Throwable private var retryCallback: () -> Unit = {} - fun onAttachView(view: MessagePreviewView, message: Message) { + fun onAttachView(view: MessagePreviewView, message: Message?) { super.onAttachView(view) view.initView() errorHandler.showErrorMessage = ::showErrorViewOnError - loadData(message) + loadData(requireNotNull(message)) } private fun onMessageLoadRetry(message: Message) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt index ba234aae..3d0c8052 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/settings/appearance/AppearanceFragment.kt @@ -4,7 +4,6 @@ import android.content.SharedPreferences import android.os.Bundle import android.view.View import androidx.preference.PreferenceFragmentCompat -import androidx.preference.SeekBarPreference import com.yariksoffice.lingver.Lingver import dagger.hilt.android.AndroidEntryPoint import io.github.wulkanowy.R @@ -37,15 +36,6 @@ class AppearanceFragment : PreferenceFragmentCompat(), override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey) - val attendanceTargetPref = - findPreference(requireContext().getString(R.string.pref_key_attendance_target))!! - attendanceTargetPref.setOnPreferenceChangeListener { _, newValueObj -> - val newValue = (((newValueObj as Int).toDouble() + 2.5) / 5).toInt() * 5 - attendanceTargetPref.value = - newValue.coerceIn(attendanceTargetPref.min, attendanceTargetPref.max) - - false - } } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt index 3cac0b48..397c9595 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -10,19 +10,19 @@ import io.github.wulkanowy.sdk.scrapper.attendance.AttendanceCategory * (https://www.vulcan.edu.pl/vulcang_files/user/AABW/AABW-PDF/uonetplus/uonetplus_Frekwencja-liczby-obecnych-nieobecnych.pdf) */ -inline val AttendanceSummary.allPresences: Int - get() = presence + absenceForSchoolReasons + lateness + latenessExcused +private inline val AttendanceSummary.allPresences: Double + get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused -inline val AttendanceSummary.allAbsences: Int - get() = absence + absenceExcused +private inline val AttendanceSummary.allAbsences: Double + get() = absence.toDouble() + absenceExcused inline val Attendance.isExcusableOrNotExcused: Boolean get() = (excusable || ((absence || lateness) && !excused)) && excuseStatus == null -fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences.toDouble(), allAbsences.toDouble()) +fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences) fun List.calculatePercentage(): Double { - return calculatePercentage(sumOf { it.allPresences.toDouble() }, sumOf { it.allAbsences.toDouble() }) + return calculatePercentage(sumOf { it.allPresences }, sumOf { it.allAbsences }) } private fun calculatePercentage(presence: Double, absence: Double): Double { diff --git a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt index b1742b4f..d3c9f800 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/BundleExtension.kt @@ -4,31 +4,30 @@ import android.content.Intent import android.os.Build import android.os.Bundle import android.os.Parcelable -import androidx.core.os.BundleCompat import java.io.Serializable -// Even though API was introduced in 33, we use 34 as 33 is bugged in some scenarios. - inline fun Bundle.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 34 -> getSerializable(key, T::class.java)!! + Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java)!! else -> @Suppress("DEPRECATION") getSerializable(key) as T } inline fun Bundle.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 34 -> getSerializable(key, T::class.java) + Build.VERSION.SDK_INT >= 33 -> getSerializable(key, T::class.java) else -> @Suppress("DEPRECATION") getSerializable(key) as T? } @Suppress("UNCHECKED_CAST") -inline fun Bundle.parcelableArray(key: String): Array? = - BundleCompat.getParcelableArray(this, key, T::class.java) as Array? +inline fun Bundle.parcelableArray(key: String): Array? = when { + Build.VERSION.SDK_INT >= 33 -> getParcelableArray(key, T::class.java) + else -> @Suppress("DEPRECATION") getParcelableArray(key) as Array? +} inline fun Intent.serializable(key: String): T = when { - Build.VERSION.SDK_INT >= 34 -> getSerializableExtra(key, T::class.java)!! + Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java)!! else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T } inline fun Intent.nullableSerializable(key: String): T? = when { - Build.VERSION.SDK_INT >= 34 -> getSerializableExtra(key, T::class.java) + Build.VERSION.SDK_INT >= 33 -> getSerializableExtra(key, T::class.java) else -> @Suppress("DEPRECATION") getSerializableExtra(key) as T? } diff --git a/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt new file mode 100644 index 00000000..9b6ca706 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/SdkExtension.kt @@ -0,0 +1,42 @@ +package io.github.wulkanowy.utils + +import io.github.wulkanowy.data.db.entities.Semester +import io.github.wulkanowy.data.db.entities.Student +import io.github.wulkanowy.sdk.Sdk +import timber.log.Timber + +fun Sdk.init(student: Student): Sdk { + email = student.email + password = student.password + symbol = student.symbol + schoolSymbol = student.schoolSymbol + studentId = student.studentId + classId = student.classId + emptyCookieJarInterceptor = true + + if (Sdk.Mode.valueOf(student.loginMode) == Sdk.Mode.HEBE) { + mobileBaseUrl = student.mobileBaseUrl + } else { + scrapperBaseUrl = student.scrapperBaseUrl + domainSuffix = student.scrapperDomainSuffix + loginType = Sdk.ScrapperLoginType.valueOf(student.loginType) + } + + mode = Sdk.Mode.valueOf(student.loginMode) + mobileBaseUrl = student.mobileBaseUrl + keyId = student.certificateKey + privatePem = student.privateKey + + Timber.d("Sdk in ${student.loginMode} mode reinitialized") + + return this +} + +fun Sdk.switchSemester(semester: Semester): Sdk { + return switchDiary( + diaryId = semester.diaryId, + kindergartenDiaryId = semester.kindergartenDiaryId, + schoolYear = semester.schoolYear, + unitId = semester.unitId, + ) +} diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 2a57977f..98c48e15 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,4 +1,4 @@ -Wersja 2.5.1 +Wersja 2.5.0 — dodaliśmy wyświetlanie ogłoszeń — dodaliśmy opcję przywracania wiadomości z kosza diff --git a/app/src/main/res/drawable/ic_menu_attendance_calculator.xml b/app/src/main/res/drawable/ic_menu_attendance_calculator.xml deleted file mode 100644 index 8a7d209a..00000000 --- a/app/src/main/res/drawable/ic_menu_attendance_calculator.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/layout/fragment_attendance_calculator.xml b/app/src/main/res/layout/fragment_attendance_calculator.xml deleted file mode 100644 index 346c6aec..00000000 --- a/app/src/main/res/layout/fragment_attendance_calculator.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_message.xml b/app/src/main/res/layout/fragment_message.xml index a437e5f9..b3dac0dd 100644 --- a/app/src/main/res/layout/fragment_message.xml +++ b/app/src/main/res/layout/fragment_message.xml @@ -29,6 +29,7 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/pref_target_attendance.xml b/app/src/main/res/layout/pref_target_attendance.xml deleted file mode 100644 index 558b0d36..00000000 --- a/app/src/main/res/layout/pref_target_attendance.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/action_menu_attendance.xml b/app/src/main/res/menu/action_menu_attendance.xml index 5c59d239..bb20c8ec 100644 --- a/app/src/main/res/menu/action_menu_attendance.xml +++ b/app/src/main/res/menu/action_menu_attendance.xml @@ -1,13 +1,6 @@ - - Abecedně - Podle data - Podle průměru - Podle procenta docházky - Podle rovnováhy docházky předmětu Světlý Tmavý @@ -36,6 +31,11 @@ 0,5 0,75 + + Abecedně + Podle data + Podle průměru + Dzienniczek+ Wulkanowy @@ -59,7 +59,7 @@ Šťastné číslo Nepřečtené zprávy - Docházka + Frekvence Lekce Známky Domácí úkoly diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 780d9351..c3c691c7 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -4,7 +4,7 @@ Přihlášení Wulkanowy Známky - Docházka + Frekvence Zkoušky Plán lekce Nastavení @@ -64,7 +64,7 @@ Symbol najdete na stránce deníku v  Uczeń→ Dostęp Mobilny → Wygeneruj kod dostępu.\n\nUjistěte se, že jste nastavili správnou variantu deníku v poli Variace deníku UONET+ na první přihlašovací obrazovce Vyberte žáky, kteří se mají do aplikace přihlásit Jiné možnosti - V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí docházky, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení + V tomto režimu nefungují následující: šťastné číslo, statistiky třídy, shrnutí frekvencí, ospravedlnění nepřítomnosti, dokončené lekce, informace o škole a prohlížení seznamu registrovaných zařízení Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka Kombinace nejlepších vlastností ostatních dvou režimů. Funguje rychleji než scraper a poskytuje funkce, které nejsou k dispozici v režimu Mobile API. Je to v experimentální fázi Ochrana osobních údajů @@ -264,12 +264,7 @@ Čas ukončení Čas ukončení musí být pozdější než čas zahájení - Shrnutí docházky - Kalkulačka docházky - %1$d nad cílem - přesně v cíli - %1$d pod cílem - %1$d/%2$d přítomnosti + Shrnutí frekvencí Nepřítomnost ze školních důvodů Omluvená nepřítomnost Neomluvená nepřítomnost @@ -287,22 +282,22 @@ Musíte vybrat alespoň jednu nepřítomnost! Ospravedlnit - Nová docházka - Nové docházky - Nové docházky - Nové docházky + Nové frekvence + Nové frekvence + Nové frekvence + Nové frekvence - %1$d nová docházka - %1$d nové docházky - %1$d nových docházek - %1$d nových docházek + %1$d nové frekvence + %1$d nové frekvence + %1$d nových frekvencí + %1$d nových frekvencí - %d docházka - %d docházky - %d docházek - %d docházek + %d frekvence + %d frekvence + %d frekvencí + %d frekvencí Společně @@ -736,8 +731,6 @@ Možnosti vypočítaného průměru Vynutit průměrný výpočet podle aplikace Zobrazit přítomnost - Cílová docházka - Třídění kalkulačky docházky Motiv Rozvíjení známek Zobrazit skupiny vedle předmětů @@ -804,7 +797,7 @@ Známky Domů Viditelnost dlaždic - Docházka + Frekvence Plán lekce Známky Vypočítaný průměr @@ -832,7 +825,7 @@ Nadcházející lekce Ladění Změny plánu lekcí - Nové docházky + Nové frekvence Černá Červená @@ -859,8 +852,8 @@ Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli Zatím přeskočit - Webová stránka deníku VULCAN vyžaduje ověření - Proč se mi to zobrazuje?\nWebová stránka deníku, ze které Wulkanowy stahuje data, zobrazuje stejnou obrazovku jako výše, takže Wulkanowy ji musí také zobrazit, aby bylo možné získávat data z této stránky. Nedá se to obejít + VULCAN\'s website requires verification + Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it Úspěšně ověřeno Žádné internetové připojení diff --git a/app/src/main/res/values-da-rDK/preferences_values.xml b/app/src/main/res/values-da-rDK/preferences_values.xml new file mode 100644 index 00000000..5aff12de --- /dev/null +++ b/app/src/main/res/values-da-rDK/preferences_values.xml @@ -0,0 +1,70 @@ + + + + Light + Dark + Black (AMOLED) + + + System language + Polski + English + Pусский + Українська + Deutsch + Čeština + Slovenčina + + + 15 minutes + 30 minutes + 1 hour + 2 hours + 6 hours + 12 hours + 24 hours + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Alphabetically + By date + By average + + + Dzienniczek+ + Wulkanowy + Grade colors in register + + + Up to 1 at once + Always expanded + Unlimited expansions + + + Average of grades only from selected semester + Average of averages from both semesters + Average of grades from the whole year + + + Don\'t show + Only between lessons + Before and between lessons + + + Lucky number + Unread messages + Attendance + Lessons + Grades + Homework + School announcements + Exams + Conferences + + diff --git a/app/src/main/res/values-de/preferences_values.xml b/app/src/main/res/values-de/preferences_values.xml index 0170acfa..d1001c74 100644 --- a/app/src/main/res/values-de/preferences_values.xml +++ b/app/src/main/res/values-de/preferences_values.xml @@ -1,10 +1,5 @@ - Alphabetically - By date - By average - By attendance percentage - By subject attendance balance Licht Dunkel @@ -36,6 +31,11 @@ 0,5 0,75 + + Alphabetisch + Nach Datum + Nach Durchschnitt + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7bc5aa99..daabc7d8 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -237,11 +237,6 @@ Endzeit muss grösser sein als Startzeit Übersicht über die Schulbesuch - Attendance calculator - %1$d over target - right on target - %1$d under target - %1$d/%2$d presences Aus schulischen Gründen abwesend Entschuldigte Abwesenheit Unentschuldigtes Abwesenheit @@ -642,8 +637,6 @@ Berechnete Durchschnittsoptionen Mittelwertberechnung durch App erzwingen Anwesendheit zeigen - Attendance target - Attendance calculator sorting Thema Steigende Sorten Gruppen neben Schulfächen anzeigen diff --git a/app/src/main/res/values-es-rES/preferences_values.xml b/app/src/main/res/values-es-rES/preferences_values.xml new file mode 100644 index 00000000..5aff12de --- /dev/null +++ b/app/src/main/res/values-es-rES/preferences_values.xml @@ -0,0 +1,70 @@ + + + + Light + Dark + Black (AMOLED) + + + System language + Polski + English + Pусский + Українська + Deutsch + Čeština + Slovenčina + + + 15 minutes + 30 minutes + 1 hour + 2 hours + 6 hours + 12 hours + 24 hours + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Alphabetically + By date + By average + + + Dzienniczek+ + Wulkanowy + Grade colors in register + + + Up to 1 at once + Always expanded + Unlimited expansions + + + Average of grades only from selected semester + Average of averages from both semesters + Average of grades from the whole year + + + Don\'t show + Only between lessons + Before and between lessons + + + Lucky number + Unread messages + Attendance + Lessons + Grades + Homework + School announcements + Exams + Conferences + + diff --git a/app/src/main/res/values-it-rIT/preferences_values.xml b/app/src/main/res/values-it-rIT/preferences_values.xml new file mode 100644 index 00000000..5aff12de --- /dev/null +++ b/app/src/main/res/values-it-rIT/preferences_values.xml @@ -0,0 +1,70 @@ + + + + Light + Dark + Black (AMOLED) + + + System language + Polski + English + Pусский + Українська + Deutsch + Čeština + Slovenčina + + + 15 minutes + 30 minutes + 1 hour + 2 hours + 6 hours + 12 hours + 24 hours + + + 0,00 + 0,25 + 0,33 + 0,5 + 0,75 + + + Alphabetically + By date + By average + + + Dzienniczek+ + Wulkanowy + Grade colors in register + + + Up to 1 at once + Always expanded + Unlimited expansions + + + Average of grades only from selected semester + Average of averages from both semesters + Average of grades from the whole year + + + Don\'t show + Only between lessons + Before and between lessons + + + Lucky number + Unread messages + Attendance + Lessons + Grades + Homework + School announcements + Exams + Conferences + + diff --git a/app/src/main/res/values-pl/preferences_values.xml b/app/src/main/res/values-pl/preferences_values.xml index 4df60b51..2f2432e9 100644 --- a/app/src/main/res/values-pl/preferences_values.xml +++ b/app/src/main/res/values-pl/preferences_values.xml @@ -1,10 +1,5 @@ - Alfabetycznie - Według daty - Według średniej - Według procentu obecności - Według balansu frekwencji przedmiotu Jasny Ciemny @@ -36,6 +31,11 @@ 0,5 0,75 + + Alfabetycznie + Według daty + Według średniej + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d1d603b6..33b715d7 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -265,11 +265,6 @@ Godzina zakończenia musi być późniejsza niż godzina rozpoczęcia Podsumowanie frekwencji - Kalkulator obecności - %1$d powyżej celu - dokładnie u celu - %1$d poniżej celu - %1$d/%2$d obecności Nieobecność z przyczyn szkolnych Nieobecność usprawiedliwiona Nieobecność nieusprawiedliwiona @@ -736,8 +731,6 @@ Opcje obliczonej średniej Wymuś obliczanie średniej przez aplikację Pokazuj obecność - Docelowa obecność - Sortowanie kalkulatora obecności Motyw Rozwijanie ocen Pokazuj grupę obok przedmiotu diff --git a/app/src/main/res/values-ru/preferences_values.xml b/app/src/main/res/values-ru/preferences_values.xml index 8d4bd8d7..df3629c0 100644 --- a/app/src/main/res/values-ru/preferences_values.xml +++ b/app/src/main/res/values-ru/preferences_values.xml @@ -1,10 +1,5 @@ - Alphabetically - By date - By average - By attendance percentage - By subject attendance balance Светлая Тёмная @@ -36,6 +31,11 @@ 0,5 0,75 + + В алфавитном порядке + По дате + По средней + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0e7e0e1d..8a5fcc40 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -265,11 +265,6 @@ Время окончания должно быть больше, чем время начала Итоговая посещаемость - Attendance calculator - %1$d over target - right on target - %1$d under target - %1$d/%2$d presences Отсутствие по школьным причинам Отсутствие по уважительной причине Отсутствие по неуважительной причине @@ -736,8 +731,6 @@ Параметры расчёта средних оценок Принудительно высчитать среднюю оценку через приложение Показывать присутствия - Attendance target - Attendance calculator sorting Тема Разворачивание оценок Показать группы рядом с темами diff --git a/app/src/main/res/values-sk/preferences_values.xml b/app/src/main/res/values-sk/preferences_values.xml index d78dd92d..6cd22154 100644 --- a/app/src/main/res/values-sk/preferences_values.xml +++ b/app/src/main/res/values-sk/preferences_values.xml @@ -1,10 +1,5 @@ - Abecedne - Podľa dátumu - Podľa priemeru - Podľa percenta dochádzky - Podľa rovnováhy dochádzky predmetu Svetlý Tmavý @@ -36,6 +31,11 @@ 0,5 0,75 + + Abecedne + Podľa dátumu + Podľa priemeru + Dzienniczek+ Wulkanowy @@ -59,7 +59,7 @@ Šťastné číslo Neprečítané správy - Dochádzka + Frekvencia Lekcie Známky Domáce úlohy diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 9dbf7282..829475d6 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -4,7 +4,7 @@ Prihlásenie Wulkanowy Známky - Dochádzka + Frekvencia Skúšky Plán lekcie Nastavenia @@ -64,7 +64,7 @@ Symbol nájdete na stránke denníka v  Uczeń→ Dostęp Mobilny → Wygeneruj kod dostępu.\n\nUistite sa, že ste nastavili správny variant denníka v poli Variácia denníka UONET+ na prvej prihlasovacej obrazovke Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť Iné možnosti - V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie dochádzky, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení + V tomto režime nefungujú nasledovné: šťastné číslo, štatistiky triedy, zhrnutie frekvencií, ospravedlnenie neprítomnosti, dokončené lekcie, informácie o škole a prezeranie zoznamu registrovaných zariadení Tento režim zobrazuje rovnaké dáta, ktoré sa zobrazujú na webových stránkach denníka Kombinácia najlepších vlastností ostatných dvoch režimov. Funguje rýchlejšie ako scraper a poskytuje funkcie, ktoré nie sú k dispozícii v režime Mobilne API. Je to v experimentálnej fáze Ochrana osobných údajov @@ -264,12 +264,7 @@ Čas ukončenia Čas ukončenia musí byť neskorší ako čas začatia - Zhrnutie dochádzky - Kalkulačka dochádzky - %1$d nad cieľom - presne v cieli - %1$d pod cieľom - %1$d/%2$d prítomnosti + Zhrnutie frekvencií Neprítomnosť zo školských dôvodov Ospravedlnená neprítomnosť Neospravedlnená neprítomnosť @@ -287,22 +282,22 @@ Musíte vybrať aspoň jednu neprítomnosť! Ospravedlniť - Nová dochádzka - Nové dochádzky - Nové dochádzky - Nové dochádzky + Nová frekvencia + Nové frekvencie + Nové frekvencie + Nové frekvencie - %1$d nová dochádzka - %1$d nové dochádzky - %1$d nových dochádzok - %1$d nových dochádzok + %1$d nová frekvencia + %1$d nové frekvencie + %1$d nových frekvencií + %1$d nových frekvencií - %d dochádzka - %d dochádzky - %d dochádzok - %d dochádzok + %d frekvencia + %d frekvencie + %d frekvencií + %d frekvencií Spoločne @@ -736,8 +731,6 @@ Možnosti vypočítaného priemeru Vynútiť priemerný výpočet podľa aplikácie Zobraziť prítomnosť - Cieľová dochádzka - Triedenie kalkulačky dochádzky Motív Rozvijanie známok Zobraziť skupiny vedľa predmetov @@ -804,7 +797,7 @@ Známky Domov Viditeľnosť dlaždíc - Dochádzka + Frekvencia Plán lekcie Známky Vypočítaný priemer @@ -832,7 +825,7 @@ Nadchádzajúce lekcie Ladenie Zmeny plánu lekcií - Nové dochádzky + Nové frekvencie Čierna Červená @@ -859,8 +852,8 @@ Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli Zatiaľ preskočiť - Webová stránka denníka VULCAN vyžaduje overenie - Prečo sa mi to zobrazuje?\nWebová stránka denníka, z ktorej Wulkanowy sťahuje dáta, zobrazuje rovnakú obrazovku ako vyššie, takže Wulkanowy ju musí tiež zobraziť, aby bolo možné získavať dáta z tejto stránky. Nedá sa to obísť + VULCAN\'s website requires verification + Why am I seeing this?\nThe register website from which Wulkanowy downloads data displays the same screen as above, so Wulkanowy must also show it to be able to download data from this website. There\'s no way around it Úspešne overené Žiadne internetové pripojenie diff --git a/app/src/main/res/values-uk/preferences_values.xml b/app/src/main/res/values-uk/preferences_values.xml index c32eedb9..c02efb54 100644 --- a/app/src/main/res/values-uk/preferences_values.xml +++ b/app/src/main/res/values-uk/preferences_values.xml @@ -1,10 +1,5 @@ - За алфавітом - За датою - За середньою - За відсотком відвідуваності - За балансом відвідування теми Світла Темна @@ -36,6 +31,11 @@ 0,5 0,75 + + За алфавітом + За датою + За середньою + Dzienniczek+ Wulkanowy diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2d8ac1f4..a0d4b6c0 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -265,11 +265,6 @@ Час завершення має бути пізніше часу початку Підсумок відвідуваності - Калькулятор відвідуваності - %1$d понад ціль - точно у цілі - %1$d під ціллю - %1$d/%2$d відвідуваності Відсутність зі шкільних причин Відсутність з поважних причин Відсутність без поважних причин @@ -736,8 +731,6 @@ Параметри розраховування середніх оцінок Примусово розраховувати середню оцінку через додаток Показувати присутність - Цільова відвідуваність - Сортування калькулятора відвідування Тема Розгортання оцінок Показувати групи поруч з темами diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 6fe16771..8ad27ad8 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,12 +1,10 @@ #d32f2f - - #9c423a - #ffffff - #ffdad6 - #410002 - + #B91B21 + #FFFFFF + #FFDAD5 + #410003 #FCFCFC #201A1A #F4DDDB @@ -16,12 +14,10 @@ #FAEEEE #FFB3AA - - #ffb4ab - #5f1411 - #7d2b25 - #ffdad6 - + #FFB3AA + #680006 + #93000E + #FFDAD5 #201A1A #ECE0E0 #534341 @@ -31,16 +27,15 @@ #201A1A #B91B21 - #9a0007 #ff5722 #e84853 - #f4ecec + #f5e8e9 #312624 #150e0e - #f2eae9 + #f5e5e6 #342826 #181010 @@ -51,15 +46,14 @@ #d32f2f #e57373 - #ff8f00 #ffd54f #d32f2f #e57373 - #ff8f00 - #ffd54f + #cd2a01 + #f05d0e #1f000000 #1fffffff diff --git a/app/src/main/res/values/preferences_defaults.xml b/app/src/main/res/values/preferences_defaults.xml index 2981e184..8e6fc7d6 100644 --- a/app/src/main/res/values/preferences_defaults.xml +++ b/app/src/main/res/values/preferences_defaults.xml @@ -2,9 +2,6 @@ 0 true - 50 - alphabetic - false only_one_semester false one diff --git a/app/src/main/res/values/preferences_keys.xml b/app/src/main/res/values/preferences_keys.xml index 080456ef..74af9262 100644 --- a/app/src/main/res/values/preferences_keys.xml +++ b/app/src/main/res/values/preferences_keys.xml @@ -2,9 +2,6 @@ default_menu_index attendance_present - attendance_target - attendance_calculator_sorting_mode - attendance_calculator_show_empty_subjects app_theme dashboard_tiles grade_color_scheme diff --git a/app/src/main/res/values/preferences_values.xml b/app/src/main/res/values/preferences_values.xml index b588ea5e..f56707c8 100644 --- a/app/src/main/res/values/preferences_values.xml +++ b/app/src/main/res/values/preferences_values.xml @@ -1,12 +1,5 @@ - - Alphabetically - By date - By average - By attendance percentage - By subject attendance balance - @string/dashboard_title @string/grade_title @@ -86,21 +79,10 @@ 0.75 - - @string/sort_alphabetically - @string/sort_by_attendance_percentage - @string/sort_by_subject_attendance_balance - - - alphabetic - attendance_percentage - lesson_balance - - - - @string/sort_alphabetically - @string/sort_by_date - @string/sort_by_average + + Alphabetically + By date + By average alphabetic diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 56cf94f0..2775365d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -258,12 +258,6 @@ Attendance summary - Attendance calculator - %1$d over target - right on target - %1$d under target - %1$d/%2$d presences - No attendances recorded Absent for school reasons Excused absence Unexcused absence @@ -721,9 +715,6 @@ Calculated average options Force average calculation by app Show presence - Attendance target - Show subjects without any attendances - Attendance calculator sorting Theme Grades expanding Show groups next to subjects diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index ca1744a7..b5b02950 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -4,22 +4,18 @@