Merge branch 'release/2.6.0'
@ -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 -PenableCrashlytics -PdisablePreDex
|
||||
command: ./gradlew publishPlayRelease --no-daemon --stacktrace --console=plain -PdisablePreDex
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
2
.github/workflows/deploy-store.yml
vendored
@ -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 -PenableFirebase --stacktrace;
|
||||
run: ./gradlew publishPlayReleaseApps --stacktrace;
|
||||
|
||||
deploy-app-gallery:
|
||||
name: AppGallery
|
||||
|
5
.github/workflows/deploy-test.yml
vendored
@ -36,8 +36,7 @@ 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/src/debug/google-services.json
|
||||
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/src/debug/agconnect-services.json
|
||||
sed -i -e "s#.dev\"#.${GITHUB_HEAD_REF//[-.\/]/_}\"#" app/google-services.json
|
||||
sed -i -e '/versionNameSuffix/d' app/build.gradle
|
||||
- name: Add signing config
|
||||
run: |
|
||||
@ -131,7 +130,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 -PenableFirebase --stacktrace
|
||||
run: ./gradlew assemblePlayDebug --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
11
.gitignore
vendored
@ -117,12 +117,13 @@ 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
|
||||
|
@ -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 -PenableFirebase --stacktrace;
|
||||
./gradlew publishPlayRelease --stacktrace;
|
||||
fi
|
||||
|
||||
after_success:
|
||||
|
@ -27,15 +27,12 @@ android {
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 34
|
||||
versionCode 157
|
||||
versionName "2.5.8"
|
||||
versionCode 160
|
||||
versionName "2.6.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
manifestPlaceholders = [
|
||||
firebase_enabled: project.hasProperty("enableFirebase"),
|
||||
admob_project_id: ""
|
||||
]
|
||||
manifestPlaceholders = [admob_project_id: ""]
|
||||
|
||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
|
||||
@ -76,7 +73,6 @@ 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"'
|
||||
}
|
||||
@ -164,8 +160,8 @@ play {
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.99d
|
||||
updatePriority = 4
|
||||
userFraction = 0.25d
|
||||
updatePriority = 1
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
@ -195,16 +191,16 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'io.github.wulkanowy:sdk:2.5.8'
|
||||
implementation 'io.github.wulkanowy:sdk:2.6.0'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.core:core-ktx:1.13.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation "androidx.activity:activity-ktx:1.8.2"
|
||||
implementation "androidx.activity:activity-ktx:1.9.0"
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.6.2"
|
||||
implementation "androidx.annotation:annotation:1.7.1"
|
||||
@ -237,7 +233,7 @@ dependencies {
|
||||
implementation 'com.github.ncapdevi:FragNav:3.3.0'
|
||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.11.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:4.12.0"
|
||||
@ -250,15 +246,15 @@ dependencies {
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||
implementation 'org.apache.commons:commons-text:1.12.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:32.7.3')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:32.8.1')
|
||||
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:22.6.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:23.0.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'
|
||||
@ -278,7 +274,7 @@ dependencies {
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.11.1'
|
||||
testImplementation 'org.robolectric:robolectric:4.12.1'
|
||||
testImplementation "androidx.test:runner:1.5.2"
|
||||
testImplementation "androidx.test.ext:junit:1.1.5"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/bin/bash -
|
||||
|
||||
content=$(cat < "app/src/main/play/release-notes/pl-PL/default.txt") || exit
|
||||
if [[ "${#content}" -gt 500 ]]; then
|
||||
content2=echo "$content" | dos2unix
|
||||
if [[ "${#content2}" -gt 500 ]]; then
|
||||
echo >&2 "Release notes content has reached the limit of 500 characters"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -1,92 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -51,7 +51,7 @@
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/WulkanowyTheme.SplashScreen"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
tools:ignore="DiscouragedApi,LockedOrientationActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@ -155,33 +155,9 @@
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
|
||||
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
|
||||
<provider
|
||||
android:name="com.google.firebase.provider.FirebaseInitProvider"
|
||||
android:authorities="${applicationId}.firebaseinitprovider"
|
||||
android:enabled="${firebase_enabled}"
|
||||
android:exported="false"
|
||||
tools:ignore="MissingClass" />
|
||||
|
||||
<meta-data
|
||||
android:name="install_channel"
|
||||
android:value="${install_channel}" />
|
||||
<meta-data
|
||||
android:name="firebase_analytics_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
<meta-data
|
||||
android:name="google_analytics_adid_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
<meta-data
|
||||
android:name="firebase_crashlytics_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
<meta-data
|
||||
android:name="firebase_messaging_auto_init_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
<meta-data
|
||||
android:name="firebase_inapp_messaging_auto_data_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_stat_all" />
|
||||
|
@ -1,11 +1,17 @@
|
||||
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
|
||||
@ -14,16 +20,39 @@ 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 class Resource<T> {
|
||||
|
||||
open class Loading<T> : Resource<T>()
|
||||
sealed interface Resource<out T> {
|
||||
/**
|
||||
* 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<T> : Resource<T>
|
||||
|
||||
/**
|
||||
* 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<T>(val data: T) : Loading<T>()
|
||||
|
||||
data class Success<T>(val data: T) : Resource<T>()
|
||||
/**
|
||||
* 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<T>(val data: T) : Resource<T>
|
||||
|
||||
data class Error<T>(val error: Throwable) : Resource<T>()
|
||||
/**
|
||||
* 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<T>(val error: Throwable) : Resource<T>
|
||||
}
|
||||
|
||||
val <T> Resource<T>.dataOrNull: T?
|
||||
@ -64,6 +93,22 @@ fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
||||
is Resource.Error -> Resource.Error(this.error)
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects another flow into this flow's resource data.
|
||||
*/
|
||||
inline fun <T1, T2, R> Flow<Resource<T1>>.combineWithResourceData(
|
||||
flow: Flow<T2>,
|
||||
crossinline block: suspend (T1, T2) -> R
|
||||
): Flow<Resource<R>> =
|
||||
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 <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||
val description = when (it) {
|
||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||
@ -74,8 +119,29 @@ fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = fa
|
||||
Timber.i("$name: $description")
|
||||
}
|
||||
|
||||
fun <T, U> Flow<Resource<T>>.mapResourceData(block: (T) -> U) = map {
|
||||
it.mapData(block)
|
||||
inline fun <T, U> Flow<Resource<T>>.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 <T, U> Flow<Resource<T>>.flatMapResourceData(
|
||||
inheritIntermediate: Boolean = true, block: suspend (T) -> Flow<Resource<U>>
|
||||
) = 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 <T> Flow<Resource<T>>.onResourceData(block: suspend (T) -> Unit) = onEach {
|
||||
@ -105,13 +171,13 @@ fun <T> Flow<Resource<T>>.onResourceSuccess(block: suspend (T) -> Unit) = onEach
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceError(block: (Throwable) -> Unit) = onEach {
|
||||
fun <T> Flow<Resource<T>>.onResourceError(block: suspend (Throwable) -> Unit) = onEach {
|
||||
if (it is Resource.Error) {
|
||||
block(it.error)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: () -> Unit) = onEach {
|
||||
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: suspend () -> Unit) = onEach {
|
||||
if (it !is Resource.Loading) {
|
||||
block()
|
||||
}
|
||||
@ -121,70 +187,99 @@ suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it !is Resource.Loa
|
||||
|
||||
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile { it is Resource.Loading }.collect()
|
||||
|
||||
inline fun <ResultType, RequestType> networkBoundResource(
|
||||
mutex: Mutex = Mutex(),
|
||||
showSavedOnLoading: Boolean = true,
|
||||
crossinline isResultEmpty: (ResultType) -> Boolean,
|
||||
crossinline query: () -> Flow<ResultType>,
|
||||
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()
|
||||
emitAll(if (shouldFetch(data)) {
|
||||
val filteredResult = filterResult(data)
|
||||
|
||||
if (showSavedOnLoading && !isResultEmpty(filteredResult)) {
|
||||
emit(Resource.Intermediate(filteredResult))
|
||||
// Can cause excessive amounts of `Resource.Intermediate` to be emitted. Unless that is desired,
|
||||
// use `debounceIntermediates` to alleviate this behavior.
|
||||
inline fun <reified T> combineResourceFlows(flows: Iterable<Flow<Resource<T>>>): Flow<Resource<List<T>>> =
|
||||
combine(flows) { items ->
|
||||
var isIntermediate = false
|
||||
val data = mutableListOf<T>()
|
||||
for (item in items) {
|
||||
when (item) {
|
||||
is Resource.Success -> data.add(item.data)
|
||||
is Resource.Intermediate -> {
|
||||
isIntermediate = true
|
||||
data.add(item.data)
|
||||
}
|
||||
|
||||
try {
|
||||
val newData = fetch(data)
|
||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||
query().map { Resource.Success(filterResult(it)) }
|
||||
} catch (throwable: Throwable) {
|
||||
onFetchFailed(throwable)
|
||||
flowOf(Resource.Error(throwable))
|
||||
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 <T> Flow<Resource<T>>.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 {
|
||||
query().map { Resource.Success(filterResult(it)) }
|
||||
wasIntermediate = false
|
||||
Duration.ZERO
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
inline fun <OutputType, ApiType> networkBoundResource(
|
||||
mutex: Mutex = Mutex(),
|
||||
crossinline isResultEmpty: (OutputType) -> Boolean,
|
||||
crossinline query: () -> Flow<OutputType>,
|
||||
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 <ResultType, RequestType, T> networkBoundResource(
|
||||
inline fun <DatabaseType, ApiType, OutputType> networkBoundResource(
|
||||
mutex: Mutex = Mutex(),
|
||||
showSavedOnLoading: Boolean = true,
|
||||
crossinline isResultEmpty: (T) -> Boolean,
|
||||
crossinline query: () -> Flow<ResultType>,
|
||||
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,
|
||||
crossinline isResultEmpty: (OutputType) -> Boolean,
|
||||
crossinline query: () -> Flow<DatabaseType>,
|
||||
crossinline fetch: suspend () -> ApiType,
|
||||
crossinline saveFetchResult: suspend (old: DatabaseType, new: ApiType) -> Unit,
|
||||
crossinline shouldFetch: (DatabaseType) -> Boolean = { true },
|
||||
crossinline mapResult: (DatabaseType) -> OutputType,
|
||||
) = flow {
|
||||
emit(Resource.Loading())
|
||||
|
||||
val data = query().first()
|
||||
emitAll(if (shouldFetch(data)) {
|
||||
val mappedResult = mapResult(data)
|
||||
if (shouldFetch(data)) {
|
||||
emit(Resource.Intermediate(data))
|
||||
|
||||
if (showSavedOnLoading && !isResultEmpty(mappedResult)) {
|
||||
emit(Resource.Intermediate(mappedResult))
|
||||
}
|
||||
try {
|
||||
val newData = fetch(data)
|
||||
val newData = fetch()
|
||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||
query().map { Resource.Success(mapResult(it)) }
|
||||
} catch (throwable: Throwable) {
|
||||
onFetchFailed(throwable)
|
||||
flowOf(Resource.Error(throwable))
|
||||
emit(Resource.Error(throwable))
|
||||
return@flow
|
||||
}
|
||||
} else {
|
||||
query().map { Resource.Success(mapResult(it)) }
|
||||
})
|
||||
}
|
||||
|
||||
emitAll(query().map { Resource.Success(it) })
|
||||
}
|
||||
.mapResourceData { mapResult(it) }
|
||||
.filterNot { it is Resource.Intermediate && isResultEmpty(it.data) }
|
||||
|
@ -4,6 +4,8 @@ import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import io.github.wulkanowy.data.serializers.SafeMessageTypeEnumListSerializer
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@ -34,6 +36,8 @@ data class AdminMessage(
|
||||
|
||||
val priority: String,
|
||||
|
||||
@SerialName("messageTypes")
|
||||
@Serializable(with = SafeMessageTypeEnumListSerializer::class)
|
||||
@ColumnInfo(name = "types", defaultValue = "[]")
|
||||
val types: List<MessageType> = emptyList(),
|
||||
|
||||
|
@ -0,0 +1,13 @@
|
||||
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
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ enum class MessageType {
|
||||
GENERAL_MESSAGE,
|
||||
DASHBOARD_MESSAGE,
|
||||
LOGIN_MESSAGE,
|
||||
LOGIN_STUDENT_SELECT_MESSAGE,
|
||||
LOGIN_SYMBOL_MESSAGE,
|
||||
PASS_RESET_MESSAGE,
|
||||
ERROR_OVERRIDE,
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class ShowAdditionalLessonsMode(val value: String) {
|
||||
NONE("none"),
|
||||
INLINE("inline"),
|
||||
BELOW("below");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = entries.find { it.value == value } ?: INLINE
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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
|
||||
}
|
@ -6,6 +6,7 @@ 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
|
||||
@ -28,6 +29,6 @@ class AdminMessageRepository @Inject constructor(
|
||||
saveFetchResult = { oldItems, newItems ->
|
||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||
},
|
||||
showSavedOnLoading = false,
|
||||
)
|
||||
.filterNot { it is Resource.Intermediate }
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ 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.ui.modules.luckynumberwidget.LuckyNumberWidgetProvider
|
||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@ -18,6 +20,7 @@ import javax.inject.Singleton
|
||||
class LuckyNumberRepository @Inject constructor(
|
||||
private val luckyNumberDb: LuckyNumberDao,
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val appWidgetUpdater: AppWidgetUpdater,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -26,6 +29,7 @@ class LuckyNumberRepository @Inject constructor(
|
||||
student: Student,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
isFromAppWidget: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
@ -44,6 +48,9 @@ class LuckyNumberRepository @Inject constructor(
|
||||
oldItems = listOfNotNull(oldLuckyNumber),
|
||||
newItems = listOf(newLuckyNumber.apply { if (notify) isNotified = false }),
|
||||
)
|
||||
if (!isFromAppWidget) {
|
||||
appWidgetUpdater.updateAllAppWidgetsByProvider(LuckyNumberWidgetProvider::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -122,7 +122,7 @@ class MessageRepository @Inject constructor(
|
||||
fetch = {
|
||||
wulkanowySdkFactory.create(student)
|
||||
.getMessageDetails(
|
||||
messageKey = it!!.message.messageGlobalKey,
|
||||
messageKey = message.messageGlobalKey,
|
||||
markAsRead = message.unread && markAsRead,
|
||||
)
|
||||
},
|
||||
|
@ -10,9 +10,11 @@ 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
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode
|
||||
import io.github.wulkanowy.data.enums.TimetableGapsMode
|
||||
import io.github.wulkanowy.data.enums.TimetableMode
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
@ -41,6 +43,27 @@ class PreferencesRepository @Inject constructor(
|
||||
R.bool.pref_default_attendance_present
|
||||
)
|
||||
|
||||
val targetAttendanceFlow: Flow<Int>
|
||||
get() = flowSharedPref.getInt(
|
||||
context.getString(R.string.pref_key_attendance_target),
|
||||
context.resources.getInteger(R.integer.pref_default_attendance_target)
|
||||
).asFlow()
|
||||
|
||||
val attendanceCalculatorSortingModeFlow: Flow<AttendanceCalculatorSortingMode>
|
||||
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<Boolean>
|
||||
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<GradeAverageMode>
|
||||
get() = getObjectFlow(
|
||||
R.string.pref_key_grade_average_mode,
|
||||
@ -191,6 +214,12 @@ class PreferencesRepository @Inject constructor(
|
||||
)
|
||||
)
|
||||
|
||||
val showAdditionalLessonsInPlan: ShowAdditionalLessonsMode
|
||||
get() = getString(
|
||||
R.string.pref_key_timetable_show_additional_lessons,
|
||||
R.string.pref_default_timetable_show_additional_lessons
|
||||
).let { ShowAdditionalLessonsMode.getByValue(it) }
|
||||
|
||||
val gradeSortingMode: GradeSortingMode
|
||||
get() = GradeSortingMode.getByValue(
|
||||
getString(
|
||||
|
@ -13,6 +13,8 @@ import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider
|
||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.monday
|
||||
@ -26,6 +28,7 @@ import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
||||
@Singleton
|
||||
class TimetableRepository @Inject constructor(
|
||||
private val timetableDb: TimetableDao,
|
||||
@ -34,6 +37,7 @@ class TimetableRepository @Inject constructor(
|
||||
private val wulkanowySdkFactory: WulkanowySdkFactory,
|
||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val appWidgetUpdater: AppWidgetUpdater,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
@ -52,7 +56,8 @@ class TimetableRepository @Inject constructor(
|
||||
forceRefresh: Boolean,
|
||||
refreshAdditional: Boolean = false,
|
||||
notify: Boolean = false,
|
||||
timetableType: TimetableType = TimetableType.NORMAL
|
||||
timetableType: TimetableType = TimetableType.NORMAL,
|
||||
isFromAppWidget: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = {
|
||||
@ -83,6 +88,9 @@ class TimetableRepository @Inject constructor(
|
||||
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
if (!isFromAppWidget) {
|
||||
appWidgetUpdater.updateAllAppWidgetsByProvider(TimetableWidgetProvider::class)
|
||||
}
|
||||
},
|
||||
filterResult = { (timetable, additional, headers) ->
|
||||
TimetableFull(
|
||||
|
@ -0,0 +1,27 @@
|
||||
package io.github.wulkanowy.data.serializers
|
||||
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.ListSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
object SafeMessageTypeEnumListSerializer : KSerializer<List<MessageType>> {
|
||||
|
||||
private val serializer = ListSerializer(String.serializer())
|
||||
|
||||
override val descriptor = serializer.descriptor
|
||||
|
||||
override fun serialize(encoder: Encoder, value: List<MessageType>) {
|
||||
encoder.encodeNotNullMark()
|
||||
serializer.serialize(encoder, value.map { it.name })
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): List<MessageType> =
|
||||
serializer.deserialize(decoder).mapNotNull { enumName ->
|
||||
MessageType.entries.find { it.name == enumName }
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
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<Resource<List<AttendanceData>>> =
|
||||
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<AttendanceData>::sortedBy)
|
||||
}
|
||||
|
||||
private fun List<AttendanceSummary>.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<AttendanceData>.sortedBy(mode: AttendanceCalculatorSortingMode) = when (mode) {
|
||||
ALPHABETIC -> sortedBy(AttendanceData::subjectName)
|
||||
ATTENDANCE -> sortedByDescending(AttendanceData::presencePercentage)
|
||||
LESSON_BALANCE -> sortedBy(AttendanceData::lessonBalance)
|
||||
}
|
@ -14,6 +14,7 @@ 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
|
||||
@ -134,6 +135,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(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
|
||||
}
|
||||
|
||||
@ -253,6 +255,10 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(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)
|
||||
}
|
||||
|
@ -1,16 +1,36 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.Resource
|
||||
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.*
|
||||
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 kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
@ -195,6 +215,11 @@ 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")
|
||||
|
||||
|
@ -56,6 +56,8 @@ interface AttendanceView : BaseView {
|
||||
|
||||
fun openSummaryView()
|
||||
|
||||
fun openCalculatorView()
|
||||
|
||||
fun startSendMessageIntent(date: LocalDate, numbers: String, reason: String)
|
||||
|
||||
fun startActionMode()
|
||||
|
@ -0,0 +1,67 @@
|
||||
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<AttendanceCalculatorAdapter.ViewHolder>() {
|
||||
|
||||
var items = emptyList<AttendanceData>()
|
||||
|
||||
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)
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.calculator
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
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.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.settings.appearance.AppearanceFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AttendanceCalculatorFragment :
|
||||
BaseFragment<FragmentAttendanceCalculatorBinding>(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()
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentAttendanceCalculatorBinding.bind(view)
|
||||
messageContainer = binding.attendanceCalculatorRecycler
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.action_menu_attendance_calculator, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return if (item.itemId == R.id.attendance_calculator_menu_settings) presenter.onSettingsSelected()
|
||||
else false
|
||||
}
|
||||
|
||||
override fun openSettingsView() {
|
||||
(activity as? MainActivity)?.pushView(AppearanceFragment.withFocusedPreference(getString(R.string.pref_key_attendance_target)))
|
||||
}
|
||||
|
||||
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<AttendanceData>) {
|
||||
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()
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
package io.github.wulkanowy.ui.modules.attendance.calculator
|
||||
|
||||
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.onResourceIntermediate
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
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<AttendanceCalculatorView>(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)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSettingsSelected(): Boolean {
|
||||
view?.openSettingsView()
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
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<AttendanceData>)
|
||||
|
||||
fun clearView()
|
||||
|
||||
fun openSettingsView()
|
||||
}
|
@ -27,8 +27,12 @@ class AuthPresenter @Inject constructor(
|
||||
|
||||
private fun loadName() {
|
||||
presenterScope.launch {
|
||||
runCatching { studentRepository.getCurrentStudent(false) }
|
||||
.onSuccess { view?.showDescriptionWithName(it.studentName) }
|
||||
runCatching {
|
||||
studentRepository.getCurrentStudent(false)
|
||||
.studentName
|
||||
.replace(" ", "\u00A0")
|
||||
}
|
||||
.onSuccess { view?.showDescriptionWithName(it) }
|
||||
.onFailure { errorHandler.dispatch(it) }
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,12 @@ import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
|
||||
@ -111,6 +113,19 @@ class LoginStudentSelectFragment :
|
||||
LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog")
|
||||
}
|
||||
|
||||
override fun showAdminMessage(adminMessage: AdminMessage?) {
|
||||
AdminMessageViewHolder(
|
||||
binding = binding.loginStudentSelectAdminMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
).bind(adminMessage)
|
||||
binding.loginStudentSelectAdminMessage.root.isVisible = adminMessage != null
|
||||
}
|
||||
|
||||
override fun openInternetBrowser(url: String) {
|
||||
requireContext().openInternetBrowser(url)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
super.onDestroyView()
|
||||
|
@ -2,16 +2,23 @@ package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
import io.github.wulkanowy.data.flatResourceFlow
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
import io.github.wulkanowy.data.mappers.mapToStudentWithSemesters
|
||||
import io.github.wulkanowy.data.onResourceData
|
||||
import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.pojos.RegisterStudent
|
||||
import io.github.wulkanowy.data.pojos.RegisterSymbol
|
||||
import io.github.wulkanowy.data.pojos.RegisterUnit
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SchoolsRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||
import io.github.wulkanowy.sdk.scrapper.exception.StudentGraduateException
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.services.sync.SyncManager
|
||||
@ -33,6 +40,8 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
private val syncManager: SyncManager,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val appInfo: AppInfo,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase
|
||||
) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
private var lastError: Throwable? = null
|
||||
@ -65,6 +74,7 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
this.loginData = loginData
|
||||
this.registerUser = registerUser
|
||||
loadData()
|
||||
loadAdminMessage()
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
@ -88,7 +98,20 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
refreshItems()
|
||||
}
|
||||
}
|
||||
}.launch()
|
||||
}.launch("load_data")
|
||||
}
|
||||
|
||||
private fun loadAdminMessage() {
|
||||
flatResourceFlow {
|
||||
getAppropriateAdminMessageUseCase(
|
||||
scrapperBaseUrl = registerUser.scrapperBaseUrl.orEmpty(),
|
||||
type = MessageType.LOGIN_STUDENT_SELECT_MESSAGE,
|
||||
)
|
||||
}
|
||||
.logResourceStatus("load login admin message")
|
||||
.onResourceData { view?.showAdminMessage(it) }
|
||||
.onResourceError { view?.showAdminMessage(null) }
|
||||
.launch("load_admin_message")
|
||||
}
|
||||
|
||||
private fun getStudentsWithCurrentlyActiveSemesters(): List<LoginStudentSelectItem.Student> {
|
||||
@ -341,4 +364,14 @@ class LoginStudentSelectPresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAdminMessageSelected(url: String?) {
|
||||
url?.let { view?.openInternetBrowser(it) }
|
||||
}
|
||||
|
||||
fun onAdminMessageDismissed(adminMessage: AdminMessage) {
|
||||
preferencesRepository.dismissedAdminMessageIds += adminMessage.id
|
||||
|
||||
view?.showAdminMessage(null)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.login.studentselect
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
|
||||
@ -25,4 +26,8 @@ interface LoginStudentSelectView : BaseView {
|
||||
fun openDiscordInvite()
|
||||
|
||||
fun openEmail(supportInfo: LoginSupportInfo)
|
||||
|
||||
fun showAdminMessage(adminMessage: AdminMessage?)
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
}
|
||||
|
@ -9,13 +9,16 @@ import android.view.inputmethod.EditorInfo.IME_NULL
|
||||
import android.widget.ArrayAdapter
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
|
||||
@ -179,4 +182,17 @@ class LoginSymbolFragment :
|
||||
override fun openSupportDialog(supportInfo: LoginSupportInfo) {
|
||||
LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog")
|
||||
}
|
||||
|
||||
override fun showAdminMessage(adminMessage: AdminMessage?) {
|
||||
AdminMessageViewHolder(
|
||||
binding = binding.loginSymbolAdminMessage,
|
||||
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
|
||||
onAdminMessageClickListener = presenter::onAdminMessageSelected,
|
||||
).bind(adminMessage)
|
||||
binding.loginSymbolAdminMessage.root.isVisible = adminMessage != null
|
||||
}
|
||||
|
||||
override fun openInternetBrowser(url: String) {
|
||||
requireContext().openInternetBrowser(url)
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,18 @@ package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.enums.MessageType
|
||||
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.pojos.RegisterUser
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.resourceFlow
|
||||
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
|
||||
import io.github.wulkanowy.sdk.scrapper.getNormalizedSymbol
|
||||
import io.github.wulkanowy.sdk.scrapper.login.InvalidSymbolException
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -21,7 +29,9 @@ import javax.inject.Inject
|
||||
class LoginSymbolPresenter @Inject constructor(
|
||||
studentRepository: StudentRepository,
|
||||
private val loginErrorHandler: LoginErrorHandler,
|
||||
private val analytics: AnalyticsHelper
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase,
|
||||
) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository) {
|
||||
|
||||
private var lastError: Throwable? = null
|
||||
@ -43,6 +53,21 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
clearAndFocusSymbol()
|
||||
showSoftKeyboard()
|
||||
}
|
||||
|
||||
loadAdminMessage()
|
||||
}
|
||||
|
||||
private fun loadAdminMessage() {
|
||||
flatResourceFlow {
|
||||
getAppropriateAdminMessageUseCase(
|
||||
scrapperBaseUrl = loginData.baseUrl,
|
||||
type = MessageType.LOGIN_SYMBOL_MESSAGE,
|
||||
)
|
||||
}
|
||||
.logResourceStatus("load login admin message")
|
||||
.onResourceData { view?.showAdminMessage(it) }
|
||||
.onResourceError { view?.showAdminMessage(null) }
|
||||
.launch("load_admin_message")
|
||||
}
|
||||
|
||||
fun onSymbolTextChanged() {
|
||||
@ -166,4 +191,14 @@ class LoginSymbolPresenter @Inject constructor(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun onAdminMessageSelected(url: String?) {
|
||||
url?.let { view?.openInternetBrowser(it) }
|
||||
}
|
||||
|
||||
fun onAdminMessageDismissed(adminMessage: AdminMessage) {
|
||||
preferencesRepository.dismissedAdminMessageIds += adminMessage.id
|
||||
|
||||
view?.showAdminMessage(null)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.login.symbol
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
import io.github.wulkanowy.ui.modules.login.LoginData
|
||||
@ -44,4 +45,8 @@ interface LoginSymbolView : BaseView {
|
||||
fun openFaqPage()
|
||||
|
||||
fun openSupportDialog(supportInfo: LoginSupportInfo)
|
||||
|
||||
fun showAdminMessage(adminMessage: AdminMessage?)
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
}
|
||||
|
@ -145,7 +145,11 @@ class LuckyNumberWidgetProvider : AppWidgetProvider() {
|
||||
}
|
||||
|
||||
if (currentStudent != null) {
|
||||
luckyNumberRepository.getLuckyNumber(currentStudent, forceRefresh = false)
|
||||
luckyNumberRepository.getLuckyNumber(
|
||||
student = currentStudent,
|
||||
forceRefresh = false,
|
||||
isFromAppWidget = true
|
||||
)
|
||||
.toFirstResult()
|
||||
.dataOrThrow
|
||||
} else null
|
||||
|
@ -3,7 +3,9 @@ package io.github.wulkanowy.ui.modules.settings.appearance
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SeekBarPreference
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
@ -29,13 +31,31 @@ class AppearanceFragment : PreferenceFragmentCompat(),
|
||||
|
||||
override val titleStringId get() = R.string.pref_settings_appearance_title
|
||||
|
||||
companion object {
|
||||
fun withFocusedPreference(key: String) = AppearanceFragment().apply {
|
||||
arguments = bundleOf(FOCUSED_KEY to key)
|
||||
}
|
||||
|
||||
private const val FOCUSED_KEY = "focusedKey"
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
presenter.onAttachView(this)
|
||||
arguments?.getString(FOCUSED_KEY)?.let { scrollToPreference(it) }
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.scheme_preferences_appearance, rootKey)
|
||||
val attendanceTargetPref =
|
||||
findPreference<SeekBarPreference>(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?) {
|
||||
|
@ -7,20 +7,21 @@ import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.databinding.ItemTimetableBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableEmptyBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableMainAdditionalBinding
|
||||
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
|
||||
import io.github.wulkanowy.utils.SyncListAdapter
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import javax.inject.Inject
|
||||
|
||||
class TimetableAdapter @Inject constructor() :
|
||||
ListAdapter<TimetableItem, RecyclerView.ViewHolder>(differ) {
|
||||
SyncListAdapter<TimetableItem, RecyclerView.ViewHolder>(Differ) {
|
||||
|
||||
override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
|
||||
|
||||
@ -39,6 +40,10 @@ class TimetableAdapter @Inject constructor() :
|
||||
TimetableItemType.EMPTY -> EmptyViewHolder(
|
||||
ItemTimetableEmptyBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
|
||||
TimetableItemType.ADDITIONAL -> AdditionalViewHolder(
|
||||
ItemTimetableMainAdditionalBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,16 +66,30 @@ class TimetableAdapter @Inject constructor() :
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Small,
|
||||
)
|
||||
|
||||
is NormalViewHolder -> bindNormalView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Normal,
|
||||
)
|
||||
|
||||
is EmptyViewHolder -> bindEmptyView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Empty,
|
||||
)
|
||||
|
||||
is AdditionalViewHolder -> bindAdditionalView(
|
||||
binding = holder.binding,
|
||||
item = getItem(position) as TimetableItem.Additional,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAdditionalView(
|
||||
binding: ItemTimetableMainAdditionalBinding,
|
||||
item: TimetableItem.Additional
|
||||
) {
|
||||
with(binding) {
|
||||
timetableItemSubject.text = item.additional.subject
|
||||
timetableItemTimeStart.text = item.additional.start.toFormattedString("HH:mm")
|
||||
timetableItemTimeFinish.text = item.additional.end.toFormattedString("HH:mm")
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,8 +326,10 @@ class TimetableAdapter @Inject constructor() :
|
||||
private class EmptyViewHolder(val binding: ItemTimetableEmptyBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
private val differ = object : DiffUtil.ItemCallback<TimetableItem>() {
|
||||
private class AdditionalViewHolder(val binding: ItemTimetableMainAdditionalBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private object Differ : DiffUtil.ItemCallback<TimetableItem>() {
|
||||
override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
|
||||
when {
|
||||
oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
|
||||
@ -333,5 +354,4 @@ class TimetableAdapter @Inject constructor() :
|
||||
} else super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,11 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
|
||||
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.openMaterialDatePicker
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -104,8 +108,11 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<TimetableItem>) {
|
||||
timetableAdapter.submitList(data)
|
||||
override fun updateData(data: List<TimetableItem>, isDayChanged: Boolean) {
|
||||
when {
|
||||
isDayChanged -> timetableAdapter.recreate(data)
|
||||
else -> timetableAdapter.submitList(data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearData() {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import java.time.Duration
|
||||
|
||||
sealed class TimetableItem(val type: TimetableItemType) {
|
||||
@ -23,6 +24,10 @@ sealed class TimetableItem(val type: TimetableItemType) {
|
||||
val numFrom: Int,
|
||||
val numTo: Int
|
||||
) : TimetableItem(TimetableItemType.EMPTY)
|
||||
|
||||
data class Additional(
|
||||
val additional: TimetableAdditional,
|
||||
) : TimetableItem(TimetableItemType.ADDITIONAL)
|
||||
}
|
||||
|
||||
data class TimeLeft(
|
||||
@ -34,5 +39,6 @@ data class TimeLeft(
|
||||
enum class TimetableItemType {
|
||||
SMALL,
|
||||
NORMAL,
|
||||
EMPTY
|
||||
EMPTY,
|
||||
ADDITIONAL,
|
||||
}
|
||||
|
@ -4,6 +4,9 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode.BELOW
|
||||
import io.github.wulkanowy.data.enums.ShowAdditionalLessonsMode.NONE
|
||||
import io.github.wulkanowy.data.enums.TimetableGapsMode.BETWEEN_AND_BEFORE_LESSONS
|
||||
import io.github.wulkanowy.data.enums.TimetableGapsMode.NO_GAPS
|
||||
import io.github.wulkanowy.data.enums.TimetableMode
|
||||
@ -14,6 +17,7 @@ import io.github.wulkanowy.data.onResourceError
|
||||
import io.github.wulkanowy.data.onResourceIntermediate
|
||||
import io.github.wulkanowy.data.onResourceNotLoading
|
||||
import io.github.wulkanowy.data.onResourceSuccess
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
@ -81,7 +85,7 @@ class TimetablePresenter @Inject constructor(
|
||||
} else currentDate?.previousSchoolDay
|
||||
|
||||
reloadView(date ?: return)
|
||||
loadData()
|
||||
loadData(isDayChanged = true)
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
@ -90,7 +94,7 @@ class TimetablePresenter @Inject constructor(
|
||||
} else currentDate?.nextSchoolDay
|
||||
|
||||
reloadView(date ?: return)
|
||||
loadData()
|
||||
loadData(isDayChanged = true)
|
||||
}
|
||||
|
||||
fun onPickDate() {
|
||||
@ -104,7 +108,7 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
fun onSwipeRefresh() {
|
||||
Timber.i("Force refreshing the timetable")
|
||||
loadData(true)
|
||||
loadData(forceRefresh = true)
|
||||
}
|
||||
|
||||
fun onRetry() {
|
||||
@ -112,7 +116,7 @@ class TimetablePresenter @Inject constructor(
|
||||
showErrorView(false)
|
||||
showProgress(true)
|
||||
}
|
||||
loadData(true)
|
||||
loadData(forceRefresh = true)
|
||||
}
|
||||
|
||||
fun onDetailsClick() {
|
||||
@ -145,7 +149,7 @@ class TimetablePresenter @Inject constructor(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadData(forceRefresh: Boolean = false) {
|
||||
private fun loadData(forceRefresh: Boolean = false, isDayChanged: Boolean = false) {
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
@ -169,9 +173,9 @@ class TimetablePresenter @Inject constructor(
|
||||
enableSwipe(true)
|
||||
showProgress(false)
|
||||
showErrorView(false)
|
||||
showContent(it.lessons.isNotEmpty())
|
||||
showEmpty(it.lessons.isEmpty())
|
||||
updateData(it.lessons)
|
||||
updateData(it, isDayChanged)
|
||||
showContent(it.lessons.isNotEmpty() || it.additional.isNotEmpty())
|
||||
showEmpty(it.lessons.isEmpty() && it.additional.isEmpty())
|
||||
setDayHeaderMessage(it.headers.find { header -> header.date == currentDate }?.content)
|
||||
reloadNavigation()
|
||||
}
|
||||
@ -216,67 +220,97 @@ class TimetablePresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateData(lessons: List<Timetable>) {
|
||||
private fun updateData(lessons: TimetableFull, isDayChanged: Boolean) {
|
||||
tickTimer?.cancel()
|
||||
|
||||
if (currentDate != now()) {
|
||||
view?.updateData(createItems(lessons))
|
||||
} else {
|
||||
tickTimer = timer(period = 2_000) {
|
||||
view?.updateData(createItems(lessons), isDayChanged)
|
||||
if (currentDate == now()) {
|
||||
tickTimer = timer(period = 2_000, initialDelay = 2_000) {
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
view?.updateData(createItems(lessons))
|
||||
view?.updateData(createItems(lessons), isDayChanged)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createItems(items: List<Timetable>): List<TimetableItem> {
|
||||
val filteredItems = items
|
||||
.filter {
|
||||
private sealed class Item(
|
||||
val isStudentPlan: Boolean,
|
||||
val start: Instant,
|
||||
val number: Int?,
|
||||
) {
|
||||
class Lesson(val lesson: Timetable) :
|
||||
Item(lesson.isStudentPlan, lesson.start, lesson.number)
|
||||
|
||||
class Additional(val additional: TimetableAdditional) : Item(true, additional.start, null)
|
||||
}
|
||||
|
||||
private fun createItems(fullTimetable: TimetableFull): List<TimetableItem> {
|
||||
val showAdditionalLessonsInPlan = prefRepository.showAdditionalLessonsInPlan
|
||||
val allItems =
|
||||
fullTimetable.lessons.map(Item::Lesson) + fullTimetable.additional.map(Item::Additional)
|
||||
.takeIf { showAdditionalLessonsInPlan != NONE }.orEmpty()
|
||||
|
||||
val filteredItems = allItems.filter {
|
||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||
it.isStudentPlan
|
||||
} else true
|
||||
}
|
||||
.sortedWith(compareBy({ item -> item.start }, { item -> !item.isStudentPlan }))
|
||||
}.sortedWith(
|
||||
(compareBy<Item> { it is Item.Additional }
|
||||
.takeIf { showAdditionalLessonsInPlan == BELOW } ?: EmptyComparator())
|
||||
.thenBy { it.start }
|
||||
.thenBy { !it.isStudentPlan }
|
||||
)
|
||||
|
||||
var prevNum = when (prefRepository.showTimetableGaps) {
|
||||
BETWEEN_AND_BEFORE_LESSONS -> 0
|
||||
else -> null
|
||||
}
|
||||
var prevIsAdditional = false
|
||||
return buildList {
|
||||
filteredItems.forEachIndexed { i, it ->
|
||||
if (prefRepository.showTimetableGaps != NO_GAPS && prevNum != null && it.number > prevNum!! + 1) {
|
||||
if (prefRepository.showTimetableGaps != NO_GAPS) {
|
||||
if (prevNum != null && it.number != null && it.number > prevNum!! + 1) {
|
||||
if (!prevIsAdditional) {
|
||||
// Additional lessons do count as a lesson so don't add empty lessons
|
||||
// when there is an additional lesson present
|
||||
val emptyLesson = TimetableItem.Empty(
|
||||
numFrom = prevNum!! + 1,
|
||||
numTo = it.number - 1
|
||||
numFrom = prevNum!! + 1, numTo = it.number - 1
|
||||
)
|
||||
add(emptyLesson)
|
||||
}
|
||||
}
|
||||
prevNum = it.number
|
||||
prevIsAdditional = it is Item.Additional
|
||||
}
|
||||
|
||||
if (it is Item.Lesson) {
|
||||
if (it.isStudentPlan) {
|
||||
val normalLesson = TimetableItem.Normal(
|
||||
lesson = it,
|
||||
lesson = it.lesson,
|
||||
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||
timeLeft = filteredItems.getTimeLeftForLesson(it.lesson, i),
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(normalLesson)
|
||||
} else {
|
||||
val smallLesson = TimetableItem.Small(
|
||||
lesson = it,
|
||||
lesson = it.lesson,
|
||||
onClick = ::onTimetableItemSelected,
|
||||
isLessonNumberVisible = !isEduOne
|
||||
)
|
||||
add(smallLesson)
|
||||
}
|
||||
|
||||
prevNum = it.number
|
||||
} else if (it is Item.Additional) {
|
||||
// If the user disabled showing additional lessons, they would've been filtered
|
||||
// out already, so there's no need to check it again.
|
||||
add(TimetableItem.Additional(it.additional))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<Timetable>.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
|
||||
private fun List<Item>.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
|
||||
val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index))
|
||||
return TimeLeft(
|
||||
until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil },
|
||||
@ -285,11 +319,20 @@ class TimetablePresenter @Inject constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun List<Timetable>.getPreviousLesson(position: Int): Instant? {
|
||||
return filter { it.isStudentPlan }
|
||||
.getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
|
||||
private fun List<Item>.getPreviousLesson(position: Int): Instant? {
|
||||
val lessonAdditionalOffset = filterIndexed { i, item ->
|
||||
i < position && item is Item.Additional
|
||||
}.size
|
||||
val lessonStudentPlanOffset = filterIndexed { i, item ->
|
||||
i < position && !item.isStudentPlan
|
||||
}.size
|
||||
val lessonIndex = position - 1 - lessonAdditionalOffset - lessonStudentPlanOffset
|
||||
|
||||
return filterIsInstance<Item.Lesson>()
|
||||
.filter { it.isStudentPlan }
|
||||
.getOrNull(lessonIndex)
|
||||
?.let {
|
||||
if (!it.canceled && it.isStudentPlan) it.end
|
||||
if (!it.lesson.canceled && it.isStudentPlan) it.lesson.end
|
||||
else null
|
||||
}
|
||||
}
|
||||
@ -342,3 +385,7 @@ class TimetablePresenter @Inject constructor(
|
||||
super.onDetachView()
|
||||
}
|
||||
}
|
||||
|
||||
private class EmptyComparator<T> : Comparator<T> {
|
||||
override fun compare(o1: T, o2: T) = 0
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ interface TimetableView : BaseView {
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<TimetableItem>)
|
||||
fun updateData(data: List<TimetableItem>, isDayChanged: Boolean)
|
||||
|
||||
fun updateNavigationDay(date: String)
|
||||
|
||||
|
@ -13,7 +13,11 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
import io.github.wulkanowy.ui.modules.timetable.additional.add.AdditionalLessonAddDialog
|
||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.openMaterialDatePicker
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -132,8 +136,12 @@ class AdditionalLessonsFragment :
|
||||
binding.additionalLessonsNextButton.visibility = if (show) View.VISIBLE else View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun showAddAdditionalLessonDialog() {
|
||||
(activity as? MainActivity)?.showDialogFragment(AdditionalLessonAddDialog.newInstance())
|
||||
override fun showAddAdditionalLessonDialog(currentDate: LocalDate) {
|
||||
(activity as? MainActivity)?.showDialogFragment(
|
||||
AdditionalLessonAddDialog.newInstance(
|
||||
currentDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun showDatePickerDialog(selectedDate: LocalDate) {
|
||||
|
@ -1,14 +1,27 @@
|
||||
package io.github.wulkanowy.ui.modules.timetable.additional
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
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.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||
import io.github.wulkanowy.domain.timetable.IsStudentHasLessonsOnWeekendUseCase
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.capitalise
|
||||
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
|
||||
import io.github.wulkanowy.utils.isHolidays
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import io.github.wulkanowy.utils.nextSchoolDay
|
||||
import io.github.wulkanowy.utils.previousSchoolDay
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -22,11 +35,14 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val timetableRepository: TimetableRepository,
|
||||
private val isStudentHasLessonsOnWeekendUseCase: IsStudentHasLessonsOnWeekendUseCase,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<AdditionalLessonsView>(errorHandler, studentRepository) {
|
||||
|
||||
private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay
|
||||
|
||||
private var isWeekendHasLessons: Boolean = false
|
||||
|
||||
lateinit var currentDate: LocalDate
|
||||
private set
|
||||
|
||||
@ -43,12 +59,18 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onPreviousDay() {
|
||||
loadData(currentDate.previousSchoolDay)
|
||||
val date = if (isWeekendHasLessons) {
|
||||
currentDate.minusDays(1)
|
||||
} else currentDate.previousSchoolDay
|
||||
loadData(date)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
fun onNextDay() {
|
||||
loadData(currentDate.nextSchoolDay)
|
||||
val date = if (isWeekendHasLessons) {
|
||||
currentDate.plusDays(1)
|
||||
} else currentDate.nextSchoolDay
|
||||
loadData(date)
|
||||
reloadView()
|
||||
}
|
||||
|
||||
@ -57,7 +79,7 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onAdditionalLessonAddButtonClicked() {
|
||||
view?.showAddAdditionalLessonDialog()
|
||||
view?.showAddAdditionalLessonDialog(currentDate)
|
||||
}
|
||||
|
||||
fun onDateSet(year: Int, month: Int, day: Int) {
|
||||
@ -131,6 +153,8 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
|
||||
isWeekendHasLessons = isStudentHasLessonsOnWeekendUseCase(semester, currentDate)
|
||||
timetableRepository.getTimetable(
|
||||
student = student,
|
||||
semester = semester,
|
||||
|
@ -36,7 +36,7 @@ interface AdditionalLessonsView : BaseView {
|
||||
|
||||
fun showDatePickerDialog(selectedDate: LocalDate)
|
||||
|
||||
fun showAddAdditionalLessonDialog()
|
||||
fun showAddAdditionalLessonDialog(currentDate: LocalDate)
|
||||
|
||||
fun showSuccessMessage()
|
||||
|
||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.timetable.additional.add
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
@ -26,9 +27,11 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
lateinit var presenter: AdditionalLessonAddPresenter
|
||||
|
||||
companion object {
|
||||
fun newInstance() = AdditionalLessonAddDialog()
|
||||
const val ARGUMENT_KEY = "additional_lesson_default_date"
|
||||
fun newInstance(defaultDate: LocalDate) = AdditionalLessonAddDialog().apply {
|
||||
arguments = bundleOf(ARGUMENT_KEY to defaultDate.toEpochDay())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||
@ -40,10 +43,13 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
arguments?.getLong(ARGUMENT_KEY)?.let(LocalDate::ofEpochDay)?.let {
|
||||
presenter.onDateSelected(it)
|
||||
}
|
||||
presenter.onAttachView(this)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
override fun initView(selectedDate: LocalDate) {
|
||||
with(binding) {
|
||||
additionalLessonDialogStartEdit.doOnTextChanged { _, _, _, _ ->
|
||||
additionalLessonDialogStart.isErrorEnabled = false
|
||||
@ -53,6 +59,7 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
additionalLessonDialogEnd.isErrorEnabled = false
|
||||
additionalLessonDialogEnd.error = null
|
||||
}
|
||||
additionalLessonDialogDateEdit.setText(selectedDate.toFormattedString())
|
||||
additionalLessonDialogDateEdit.doOnTextChanged { _, _, _, _ ->
|
||||
additionalLessonDialogDate.isErrorEnabled = false
|
||||
additionalLessonDialogDate.error = null
|
||||
@ -61,7 +68,6 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
additionalLessonDialogContent.isErrorEnabled = false
|
||||
additionalLessonDialogContent.error = null
|
||||
}
|
||||
|
||||
additionalLessonDialogAdd.setOnClickListener {
|
||||
presenter.onAddAdditionalClicked(
|
||||
start = additionalLessonDialogStartEdit.text?.toString(),
|
||||
@ -155,8 +161,10 @@ class AdditionalLessonAddDialog : BaseDialogFragment<DialogAdditionalAddBinding>
|
||||
.build()
|
||||
|
||||
timePicker.addOnPositiveButtonClickListener {
|
||||
if (isAdded) {
|
||||
onTimeSelected(LocalTime.of(timePicker.hour, timePicker.minute))
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentFragmentManager.isStateSaved) {
|
||||
timePicker.show(parentFragmentManager, null)
|
||||
|
@ -10,9 +10,12 @@ import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
||||
import io.github.wulkanowy.utils.toLocalDate
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.time.*
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import javax.inject.Inject
|
||||
|
||||
class AdditionalLessonAddPresenter @Inject constructor(
|
||||
@ -30,7 +33,7 @@ class AdditionalLessonAddPresenter @Inject constructor(
|
||||
|
||||
override fun onAttachView(view: AdditionalLessonAddView) {
|
||||
super.onAttachView(view)
|
||||
view.initView()
|
||||
view.initView(selectedDate)
|
||||
Timber.i("AdditionalLesson details view was initialized")
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ import java.time.LocalTime
|
||||
|
||||
interface AdditionalLessonAddView : BaseView {
|
||||
|
||||
fun initView()
|
||||
fun initView(selectedDate: LocalDate)
|
||||
|
||||
fun closeDialog()
|
||||
|
||||
|
@ -101,7 +101,14 @@ class TimetableWidgetFactory(
|
||||
private suspend fun getLessons(
|
||||
student: Student, semester: Semester, date: LocalDate
|
||||
): List<Timetable> {
|
||||
val timetable = timetableRepository.getTimetable(student, semester, date, date, false)
|
||||
val timetable = timetableRepository.getTimetable(
|
||||
student = student,
|
||||
semester = semester,
|
||||
start = date,
|
||||
end = date,
|
||||
forceRefresh = false,
|
||||
isFromAppWidget = true
|
||||
)
|
||||
val lessons = timetable.toFirstResult().dataOrThrow.lessons
|
||||
return lessons.sortedBy { it.start }
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class AppWidgetUpdater @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val appWidgetManager: AppWidgetManager
|
||||
) {
|
||||
|
||||
fun updateAllAppWidgetsByProvider(providerClass: KClass<out BroadcastReceiver>) {
|
||||
try {
|
||||
val ids = appWidgetManager.getAppWidgetIds(ComponentName(context, providerClass.java))
|
||||
if (ids.isEmpty()) return
|
||||
|
||||
val intent = Intent(context, providerClass.java)
|
||||
.apply {
|
||||
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||
}
|
||||
|
||||
context.sendBroadcast(intent)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to update all widgets for provider $providerClass")
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
*/
|
||||
|
||||
private inline val AttendanceSummary.allPresences: Double
|
||||
get() = presence.toDouble() + absenceForSchoolReasons + lateness + latenessExcused
|
||||
inline val AttendanceSummary.allPresences: Int
|
||||
get() = presence + absenceForSchoolReasons + lateness + latenessExcused
|
||||
|
||||
private inline val AttendanceSummary.allAbsences: Double
|
||||
get() = absence.toDouble() + absenceExcused
|
||||
inline val AttendanceSummary.allAbsences: Int
|
||||
get() = absence + absenceExcused
|
||||
|
||||
inline val Attendance.isExcusableOrNotExcused: Boolean
|
||||
get() = (excusable || ((absence || lateness) && !excused)) && excuseStatus == null
|
||||
|
||||
fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences, allAbsences)
|
||||
fun AttendanceSummary.calculatePercentage() = calculatePercentage(allPresences.toDouble(), allAbsences.toDouble())
|
||||
|
||||
fun List<AttendanceSummary>.calculatePercentage(): Double {
|
||||
return calculatePercentage(sumOf { it.allPresences }, sumOf { it.allAbsences })
|
||||
return calculatePercentage(sumOf { it.allPresences.toDouble() }, sumOf { it.allAbsences.toDouble() })
|
||||
}
|
||||
|
||||
private fun calculatePercentage(presence: Double, absence: Double): Double {
|
||||
|
@ -0,0 +1,66 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* Custom alternative to androidx.recyclerview.widget.ListAdapter. ListAdapter is asynchronous which
|
||||
* caused data race problems in views when a Resource.Error arrived shortly after
|
||||
* Resource.Intermediate/Success - occasionally in that case the user could see both the Resource's
|
||||
* data and an error message one on top of the other. This is synchronized by design to avoid that
|
||||
* problem, however it retains the quality of life improvements of the original.
|
||||
*/
|
||||
abstract class SyncListAdapter<T : Any, VH : RecyclerView.ViewHolder> private constructor(
|
||||
private val updateStrategy: SyncListAdapter<T, VH>.(List<T>) -> Unit
|
||||
) : RecyclerView.Adapter<VH>() {
|
||||
|
||||
constructor(differ: DiffUtil.ItemCallback<T>) : this({ newItems ->
|
||||
val diffResult = DiffUtil.calculateDiff(toCallback(differ, items, newItems))
|
||||
items = newItems
|
||||
diffResult.dispatchUpdatesTo(this)
|
||||
})
|
||||
|
||||
var items = emptyList<T>()
|
||||
private set
|
||||
|
||||
final override fun getItemCount() = items.size
|
||||
|
||||
fun getItem(position: Int): T {
|
||||
return items[position]
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all items, same as submitList, however also disables animations temporarily.
|
||||
* This prevents a flashing effect on some views. Should be used in favor of submitList when
|
||||
* all data is changed (e.g. the selected day changes in timetable causing all lessons to change).
|
||||
*/
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun recreate(data: List<T>) {
|
||||
items = data
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun submitList(data: List<T>) {
|
||||
updateStrategy(data.toList())
|
||||
}
|
||||
|
||||
private fun <T : Any> toCallback(
|
||||
itemCallback: DiffUtil.ItemCallback<T>,
|
||||
old: List<T>,
|
||||
new: List<T>,
|
||||
) = object : DiffUtil.Callback() {
|
||||
override fun getOldListSize() = old.size
|
||||
|
||||
override fun getNewListSize() = new.size
|
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
itemCallback.areItemsTheSame(old[oldItemPosition], new[newItemPosition])
|
||||
|
||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||
itemCallback.areContentsTheSame(old[oldItemPosition], new[newItemPosition])
|
||||
|
||||
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int) =
|
||||
itemCallback.getChangePayload(old[oldItemPosition], new[newItemPosition])
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ Zvýrazněné vlastnosti a funkce:
|
||||
- šťastné číslo,
|
||||
- náhled na další a dokončené lekce,
|
||||
- tmavý motiv,
|
||||
- žádné reklamy,
|
||||
- volitelné reklamy,
|
||||
- offline režim,
|
||||
- upozornění.
|
||||
|
||||
|
@ -6,7 +6,7 @@ Wyróżnione cechy i funkcje:
|
||||
- szczęśliwy numerek,
|
||||
- podgląd lekcji dodatkowych i zrealizowanych,
|
||||
- ciemny motyw.
|
||||
- brak reklam,
|
||||
- opcjonalne reklam,
|
||||
- tryb offline,
|
||||
- powiadomienia.
|
||||
|
||||
|
Before Width: | Height: | Size: 342 KiB After Width: | Height: | Size: 184 KiB |
Before Width: | Height: | Size: 445 KiB After Width: | Height: | Size: 261 KiB |
Before Width: | Height: | Size: 363 KiB After Width: | Height: | Size: 227 KiB |
Before Width: | Height: | Size: 270 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 469 KiB After Width: | Height: | Size: 251 KiB |
Before Width: | Height: | Size: 346 KiB After Width: | Height: | Size: 204 KiB |
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 414 KiB After Width: | Height: | Size: 251 KiB |
@ -6,7 +6,7 @@ Zvýraznené vlastnosti a funkcie:
|
||||
- šťastné číslo,
|
||||
- náhľad na ďalšie a dokončené lekcie,
|
||||
- tmavý motív,
|
||||
- žiadne reklamy,
|
||||
- voliteľné reklamy,
|
||||
- offline režim,
|
||||
- upozornenia.
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
Wersja 2.5.8
|
||||
Wersja 2.6.0
|
||||
|
||||
— obeszliśmy próby blokowania Wulkanowego przez firmę VULCAN, o czymś pewnie zapomnieliśmy, ale nie miejcie nam tego za złe
|
||||
— dodaliśmy kalkulator frekwencji
|
||||
— dodaliśmy wyświetlanie lekcji dodatkowych w planie lekcji
|
||||
— ulepszyliśmy wyjaśnienie na ekranie z miejscem na wpisanie numeru PESEL
|
||||
— naprawiliśmy rzadkie sytuacje, gdy plan lekcji nakładał się na informację o jego braku
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,7H9C7.9,7 7,7.9 7,9v10c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V9C21,7.9 20.1,7 19,7zM19,9v2H9V9H19zM13,15v-2h2v2H13zM15,17v2h-2v-2H15zM11,15H9v-2h2V15zM17,13h2v2h-2V13zM9,17h2v2H9V17zM17,19v-2h2v2H17zM6,17H5c-1.1,0 -2,-0.9 -2,-2V5c0,-1.1 0.9,-2 2,-2h10c1.1,0 2,0.9 2,2v1h-2V5H5v10h1V17z"/>
|
||||
</vector>
|
@ -32,12 +32,11 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintTop_toBottomOf="@id/auth_title"
|
||||
app:lineHeight="24sp"
|
||||
app:lineHeight="18sp"
|
||||
tools:text="@string/auth_description" />
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/auth_input_layout"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
|
103
app/src/main/res/layout/fragment_attendance_calculator.xml
Normal file
@ -0,0 +1,103 @@
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.modules.attendance.calculator.AttendanceCalculatorFragment">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/attendanceCalculatorSwipe"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/attendanceCalculatorRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_attendance_calculator_header" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||
android:id="@+id/attendanceCalculatorProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/attendanceCalculatorEmpty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
tools:ignore="UseCompoundDrawables">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
app:srcCompat="@drawable/ic_main_attendance"
|
||||
app:tint="?colorOnBackground"
|
||||
tools:ignore="contentDescription" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/attendance_no_items"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/attendanceCalculatorError"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
tools:ignore="UseCompoundDrawables"
|
||||
tools:visibility="invisible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
app:srcCompat="@drawable/ic_error"
|
||||
app:tint="?colorOnBackground"
|
||||
tools:ignore="contentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorErrorMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:text="@string/error_unknown"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/attendanceCalculatorErrorDetails"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:text="@string/all_details" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/attendanceCalculatorErrorRetry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/all_retry" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
@ -11,6 +11,18 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include
|
||||
android:id="@+id/login_student_select_admin_message"
|
||||
layout="@layout/item_dashboard_admin_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginStudentSelectHeader"
|
||||
android:layout_width="match_parent"
|
||||
@ -28,7 +40,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@id/loginStudentSelectRecycler"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/login_student_select_admin_message"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -95,6 +95,18 @@
|
||||
android:background="?android:attr/listDivider" />
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/login_symbol_admin_message"
|
||||
layout="@layout/item_dashboard_admin_message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginSymbolContact"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/loginSymbolHeader"
|
||||
android:layout_width="match_parent"
|
||||
@ -111,7 +123,7 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/loginSymbolContact"
|
||||
app:layout_constraintTop_toBottomOf="@+id/login_symbol_admin_message"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
|
111
app/src/main/res/layout/item_attendance_calculator_header.xml
Normal file
@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorPercentage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right|center_vertical"
|
||||
android:minWidth="32dp"
|
||||
android:minHeight="36dp"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="50" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorPercentagePercentSign"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="%"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
app:layout_constraintBaseline_toBaselineOf="@+id/attendanceCalculatorPercentage"
|
||||
app:layout_constraintStart_toEndOf="@+id/attendanceCalculatorPercentage" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorSummaryValues"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/attendanceCalculatorSummaryDot"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="@+id/attendanceCalculatorTitle"
|
||||
app:layout_constraintTop_toBottomOf="@+id/attendanceCalculatorTitle"
|
||||
tools:text="11/123 obecności" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorSummaryDot"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:gravity="center"
|
||||
android:text="·"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/attendanceCalculatorSummaryBalance"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/attendanceCalculatorSummaryValues"
|
||||
app:layout_constraintTop_toBottomOf="@+id/attendanceCalculatorTitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorSummaryBalance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
android:maxLines="1"
|
||||
android:minWidth="24dp"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/attendanceCalculatorWarning"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/attendanceCalculatorSummaryDot"
|
||||
app:layout_constraintTop_toBottomOf="@+id/attendanceCalculatorTitle"
|
||||
tools:text="12 powyżej celu" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/attendanceCalculatorTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@+id/attendanceCalculatorWarning"
|
||||
app:layout_constraintStart_toEndOf="@+id/attendanceCalculatorPercentagePercentSign"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Informatyka" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/attendanceCalculatorWarning"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription=""
|
||||
android:layout_marginStart="12dp"
|
||||
android:gravity="center_vertical|right"
|
||||
android:minWidth="24dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_all_round_mark"
|
||||
app:tint="?colorPrimary"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
153
app/src/main/res/layout/item_timetable_main_additional.xml
Normal file
@ -0,0 +1,153 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="6dp"
|
||||
tools:context=".ui.modules.timetable.TimetableAdapter">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemNumber"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLength="2"
|
||||
android:minWidth="40dp"
|
||||
android:minHeight="40dp"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="32sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemSubject"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/timetableItemTimeBarrier"
|
||||
app:layout_constraintStart_toEndOf="@+id/timetableItemTimeStart"
|
||||
app:layout_constraintTop_toTopOf="@id/timetableItemTimeStart"
|
||||
tools:text="@tools:sample/lorem" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemTimeStart"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/timetableItemTimeFinish"
|
||||
app:layout_constraintStart_toEndOf="@id/timetableItemNumber"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="11:11" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemTimeFinish"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/timetableItemNumber"
|
||||
app:layout_constraintTop_toBottomOf="@id/timetableItemTimeStart"
|
||||
tools:text="12:00" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemTeacher"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="13sp"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/timetableItemTimeStart"
|
||||
app:layout_constraintTop_toTopOf="@id/timetableItemTimeFinish"
|
||||
android:text="@string/timetable_additional_lesson"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:textColor="?colorTimetableChange"
|
||||
android:textSize="13sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/timetableItemTimeFinish"
|
||||
app:layout_constraintTop_toTopOf="@id/timetableItemTimeFinish"
|
||||
tools:text="Lekcja odwołana: uczniowie zwolnieni do domu"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/timetableItemTimeBarrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="start"
|
||||
app:constraint_referenced_ids="timetableItemTimeUntil,timetableItemTimeLeft" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemTimeUntil"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingRight="4dp"
|
||||
android:textColor="?colorPrimary"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="za 15 min"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableItemTimeLeft"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginStart="4dp"
|
||||
android:background="@drawable/background_timetable_time_left"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:paddingLeft="7dp"
|
||||
android:paddingTop="2dp"
|
||||
android:paddingRight="7dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:textColor="?colorOnPrimary"
|
||||
android:textSize="13sp"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="?colorPrimary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/timetableItemTimeStart"
|
||||
tools:text="jeszcze 15 min"
|
||||
tools:visibility="visible" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
140
app/src/main/res/layout/pref_target_attendance.xml
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Taken from https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-camerax-release/preference/preference/res/layout/preference_widget_seekbar_material.xml.
|
||||
|
||||
~ Copyright (C) 2018 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<!-- Layout used by SeekBarPreference for the seekbar widget style. -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:baselineAligned="false">
|
||||
<include layout="@layout/image_frame"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:ellipsize="marquee"/>
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@android:id/title"
|
||||
android:layout_alignLeft="@android:id/title"
|
||||
android:layout_alignStart="@android:id/title"
|
||||
android:layout_gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:maxLines="4"
|
||||
style="@style/PreferenceSummaryTextStyle"/>
|
||||
</RelativeLayout>
|
||||
<!-- Using UnPressableLinearLayout as a workaround to disable the pressed state propagation
|
||||
to the children of this container layout. Otherwise, the animated pressed state will also
|
||||
play for the thumb in the AbsSeekBar in addition to the preference's ripple background.
|
||||
The background of the SeekBar is also set to null to disable the ripple background -->
|
||||
<androidx.preference.UnPressableLinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false">
|
||||
<!-- The total height of the Seekbar widget's area should be 48dp - this allows for an
|
||||
increased touch area so you do not need to exactly tap the thumb to move it. However,
|
||||
setting the Seekbar height directly causes the thumb and seekbar to be misaligned on
|
||||
API 22 and 23 - so instead we just set 15dp padding above and below, to account for the
|
||||
18dp default height of the Seekbar thumb for a total of 48dp.
|
||||
Note: we set 0dp padding at the start and end of this seekbar to allow it to properly
|
||||
fit into the layout, but this means that there's no leeway on either side for touch
|
||||
input - this might be something we should reconsider down the line. -->
|
||||
<SeekBar
|
||||
android:id="@+id/seekbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/preference_seekbar_padding_horizontal"
|
||||
android:paddingStart="@dimen/preference_seekbar_padding_horizontal"
|
||||
android:paddingRight="@dimen/preference_seekbar_padding_horizontal"
|
||||
android:paddingEnd="@dimen/preference_seekbar_padding_horizontal"
|
||||
android:paddingTop="@dimen/preference_seekbar_padding_vertical"
|
||||
android:paddingBottom="@dimen/preference_seekbar_padding_vertical"
|
||||
|
||||
android:background="@null"/>
|
||||
<!-- If the value is shown, we reserve a minimum width of 36dp to allow for consistent
|
||||
seekbar width for smaller values. If the value is ~4 or more digits, it will expand
|
||||
into the seekbar width. -->
|
||||
<TextView
|
||||
android:id="@+id/seekbar_value"
|
||||
android:minWidth="@dimen/preference_seekbar_value_minWidth"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingRight="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="right"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:scrollbars="none"/>
|
||||
<!-- Wulkanowy start -->
|
||||
<TextView
|
||||
android:minWidth="0dp"
|
||||
android:paddingLeft="0dp"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingRight="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="left"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:scrollbars="none"
|
||||
android:text="%"
|
||||
/>
|
||||
<!-- Wulkanowy end -->
|
||||
</androidx.preference.UnPressableLinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -1,6 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/attendanceMenuCalculator"
|
||||
android:icon="@drawable/ic_menu_attendance_calculator"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/attendance_calculator_button"
|
||||
app:iconTint="@color/material_on_surface_emphasis_medium"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/attendanceMenuSummary"
|
||||
android:icon="@drawable/ic_menu_attendance_summary"
|
||||
|
11
app/src/main/res/menu/action_menu_attendance_calculator.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/attendance_calculator_menu_settings"
|
||||
android:icon="@drawable/ic_more_settings"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/pref_attendance_calculator_appearance_settings_title"
|
||||
app:iconTint="?colorControlNormal"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">Abecedně</string>
|
||||
<string name="sort_by_date">Podle data</string>
|
||||
<string name="sort_by_average">Podle průměru</string>
|
||||
<string name="sort_by_attendance_percentage">Podle procenta docházky</string>
|
||||
<string name="sort_by_subject_attendance_balance">Podle rovnováhy docházky předmětu</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Světlý</item>
|
||||
<item>Tmavý</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Abecedně</item>
|
||||
<item>Podle data</item>
|
||||
<item>Podle průměru</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,10 +56,15 @@
|
||||
<item>Pouze mezi lekcemi</item>
|
||||
<item>Před a mezi lekcemi</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Nezobrazovat</item>
|
||||
<item>Zobrazit v řadě</item>
|
||||
<item>Zobrazit pod pravidelnými hodinami</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Šťastné číslo</item>
|
||||
<item>Nepřečtené zprávy</item>
|
||||
<item>Frekvence</item>
|
||||
<item>Docházka</item>
|
||||
<item>Lekce</item>
|
||||
<item>Známky</item>
|
||||
<item>Domácí úkoly</item>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<string name="login_title">Přihlášení</string>
|
||||
<string name="main_title">Wulkanowy</string>
|
||||
<string name="grade_title">Známky</string>
|
||||
<string name="attendance_title">Frekvence</string>
|
||||
<string name="attendance_title">Docházka</string>
|
||||
<string name="exam_title">Zkoušky</string>
|
||||
<string name="timetable_title">Plán lekce</string>
|
||||
<string name="settings_title">Nastavení</string>
|
||||
@ -31,7 +31,7 @@
|
||||
<!--Subtitles-->
|
||||
<string name="grade_subtitle">Semestr %1$d, %2$d/%3$d</string>
|
||||
<!--Login-->
|
||||
<string name="login_header_default">Přihlaste se pomocí studentského nebo rodičovského účtu</string>
|
||||
<string name="login_header_default">Přihlaste se pomocí žákovského nebo rodičovského účtu</string>
|
||||
<string name="login_header_symbol">Zadejte symbol ze stránky deníku: <b>%1$s</b></string>
|
||||
<string name="login_nickname_hint">Uživatelské jméno</string>
|
||||
<string name="login_email_hint">Email</string>
|
||||
@ -64,7 +64,7 @@
|
||||
<string name="login_symbol_helper">Symbol najdete na stránce deníku v  <b>Uczeń</b>→ <b>Dostęp Mobilny</b> → <b>Wygeneruj kod dostępu</b>.\n\nUjistěte se, že jste nastavili správnou variantu deníku v poli <b>Variace deníku UONET+</b> na první přihlašovací obrazovce</string>
|
||||
<string name="login_select_student">Vyberte žáky, kteří se mají do aplikace přihlásit</string>
|
||||
<string name="login_advanced">Jiné možnosti</string>
|
||||
<string name="login_advanced_warning_mobile_api">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í</string>
|
||||
<string name="login_advanced_warning_mobile_api">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í</string>
|
||||
<string name="login_advanced_warning_scraper">Tento režim zobrazuje stejná data, která se zobrazují na webových stránkách deníka</string>
|
||||
<string name="login_advanced_warning_hybrid">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</string>
|
||||
<string name="login_privacy_policy">Ochrana osobních údajů</string>
|
||||
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Komentář</string>
|
||||
<string name="grade_number_new_items">Počet nových známek: %1$d</string>
|
||||
<string name="grade_average">Průměr: %1$.2f</string>
|
||||
<string name="grade_average_year">Roční: %1$.2f</string>
|
||||
<string name="grade_points_sum">Body: %s</string>
|
||||
<string name="grade_no_average">Bez průměru</string>
|
||||
<string name="grade_summary_average_semester">Pololetní průměr</string>
|
||||
<string name="grade_summary_average_year">Roční průměr</string>
|
||||
<string name="grade_summary_points">Součet bodů</string>
|
||||
<string name="grade_summary_final_grade">Konečná známka</string>
|
||||
<string name="grade_summary_predicted_grade">Předpokládaná známka</string>
|
||||
<string name="grade_summary_descriptive">Popisná známka</string>
|
||||
<string name="grade_summary_calculated_average">Vypočítaný průměr</string>
|
||||
<string name="grade_summary_calculated_average">Vypočítaný pololetní průměr</string>
|
||||
<string name="grade_summary_calculated_average_annual">Vypočítaný roční průměr</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Jak funguje vypočítaný průměr?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Vypočítaný průměr je aritmetický průměr vypočítaný z průměrů předmětů. Umožňuje vám to znát přibližný konečný průměr. Vypočítává se způsobem zvoleným uživatelem v nastavení aplikaci. Doporučuje se vybrat příslušnou možnost. Důvodem je rozdílný výpočet školních průměrů. Pokud vaše škola navíc uvádí průměr předmětů na stránce deníku Vulcan, aplikace si je stáhne a tyto průměry nepočítá. To lze změnit vynucením výpočtu průměru v nastavení aplikaci.\n\n<b>Průměr známek pouze z vybraného semestru</b>:\n1. Výpočet váženého průměru pro každý předmět v daném semestru\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů\n\n<b>Průměr průměrů z obou semestrů</b>:\n1. Výpočet váženého průměru pro každý předmět v semestru 1 a 2\n2. Výpočet aritmetického průměru vypočítaných průměrů za semestry 1 a 2 pro každý předmět.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru sečtených průměrů\n\n<b>Průměr známek z celého roku:</b>\n1. Výpočet váženého průměru za rok pro každý předmět. Konečný průměr v 1. semestru je nepodstatný.\n2. Sčítání vypočítaných průměrů\n3. Výpočet aritmetického průměru součtených průměrů</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Jak funguje konečný průměr?</string>
|
||||
@ -194,6 +198,7 @@
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Lekce</string>
|
||||
<string name="timetable_additional_lesson">Další lekce</string>
|
||||
<string name="timetable_room">Učebna</string>
|
||||
<string name="timetable_group">Skupina</string>
|
||||
<string name="timetable_time">Hodiny</string>
|
||||
@ -264,7 +269,13 @@
|
||||
<string name="additional_lessons_end">Čas ukončení</string>
|
||||
<string name="additional_lessons_end_time_error">Čas ukončení musí být pozdější než čas zahájení</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Shrnutí frekvencí</string>
|
||||
<string name="attendance_summary_button">Shrnutí docházky</string>
|
||||
<string name="attendance_calculator_button">Kalkulačka docházky</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> nad cílem</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">přesně v cíli</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> pod cílem</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d přítomnosti</string>
|
||||
<string name="attendance_calculator_summary_values_empty">Nebyla zaznamenána žádná docházka</string>
|
||||
<string name="attendance_absence_school">Nepřítomnost ze školních důvodů</string>
|
||||
<string name="attendance_absence_excused">Omluvená nepřítomnost</string>
|
||||
<string name="attendance_absence_unexcused">Neomluvená nepřítomnost</string>
|
||||
@ -282,22 +293,22 @@
|
||||
<string name="attendance_excuse_no_selection">Musíte vybrat alespoň jednu nepřítomnost!</string>
|
||||
<string name="attendance_excuse_title">Ospravedlnit</string>
|
||||
<plurals name="attendance_notify_new_items_title">
|
||||
<item quantity="one">Nové frekvence</item>
|
||||
<item quantity="few">Nové frekvence</item>
|
||||
<item quantity="many">Nové frekvence</item>
|
||||
<item quantity="other">Nové frekvence</item>
|
||||
<item quantity="one">Nová docházka</item>
|
||||
<item quantity="few">Nové docházky</item>
|
||||
<item quantity="many">Nové docházky</item>
|
||||
<item quantity="other">Nové docházky</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_notify_new_items">
|
||||
<item quantity="one">%1$d nové frekvence</item>
|
||||
<item quantity="few">%1$d nové frekvence</item>
|
||||
<item quantity="many">%1$d nových frekvencí</item>
|
||||
<item quantity="other">%1$d nových frekvencí</item>
|
||||
<item quantity="one">%1$d nová docházka</item>
|
||||
<item quantity="few">%1$d nové docházky</item>
|
||||
<item quantity="many">%1$d nových docházek</item>
|
||||
<item quantity="other">%1$d nových docházek</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_number_item">
|
||||
<item quantity="one">%d frekvence</item>
|
||||
<item quantity="few">%d frekvence</item>
|
||||
<item quantity="many">%d frekvencí</item>
|
||||
<item quantity="other">%d frekvencí</item>
|
||||
<item quantity="one">%d docházka</item>
|
||||
<item quantity="few">%d docházky</item>
|
||||
<item quantity="many">%d docházek</item>
|
||||
<item quantity="other">%d docházek</item>
|
||||
</plurals>
|
||||
<!--Attendance summary-->
|
||||
<string name="attendance_summary_total">Společně</string>
|
||||
@ -731,9 +742,13 @@
|
||||
<string name="pref_view_grade_average_mode">Možnosti vypočítaného průměru</string>
|
||||
<string name="pref_view_grade_average_force_calc">Vynutit průměrný výpočet podle aplikace</string>
|
||||
<string name="pref_view_present">Zobrazit přítomnost</string>
|
||||
<string name="pref_attendance_target">Cílová docházka</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Zobrazit předměty bez docházek</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Třídění kalkulačky docházky</string>
|
||||
<string name="pref_view_app_theme">Motiv</string>
|
||||
<string name="pref_view_expand_grade">Rozvíjení známek</string>
|
||||
<string name="pref_view_timetable_show_groups">Zobrazit skupiny vedle předmětů</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Zobrazit další lekce</string>
|
||||
<string name="pref_view_timetable_show_gaps">Zobrazit prázdné dlaždice, kde není žádná lekce</string>
|
||||
<string name="pref_view_grade_statistics_list">Zobrazit seznam grafů v známkách třídy</string>
|
||||
<string name="pref_view_subjects_without_grades">Zobrazit předměty bez známek</string>
|
||||
@ -797,7 +812,9 @@
|
||||
<string name="pref_grades_appearance_header">Známky</string>
|
||||
<string name="pref_dashboard_appearance_header">Domů</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Viditelnost dlaždic</string>
|
||||
<string name="pref_attendance_appearance_view">Frekvence</string>
|
||||
<string name="pref_attendance_appearance_view">Docházka</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Kalkulačka docházky</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Nastavení</string>
|
||||
<string name="pref_timetable_appearance_view">Plán lekce</string>
|
||||
<string name="pref_grades_advanced_header">Známky</string>
|
||||
<string name="pref_counted_average_advanced_header">Vypočítaný průměr</string>
|
||||
@ -825,7 +842,7 @@
|
||||
<string name="channel_upcoming_lessons">Nadcházející lekce</string>
|
||||
<string name="channel_debug">Ladění</string>
|
||||
<string name="channel_change_timetable">Změny plánu lekcí</string>
|
||||
<string name="channel_new_attendance">Nové frekvence</string>
|
||||
<string name="channel_new_attendance">Nové docházky</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Černá</string>
|
||||
<string name="all_red">Červená</string>
|
||||
@ -849,7 +866,7 @@
|
||||
<string name="auth_button">Autorizovat</string>
|
||||
<string name="auth_success">Autorizace byla úspěšně dokončena</string>
|
||||
<string name="auth_title">Autorizace</string>
|
||||
<string name="auth_description">Pro provoz aplikace potřebujeme potvrdit vaši identitu. Zadejte PESEL žáka <b>%1$s</b> v níže uvedeném poli</string>
|
||||
<string name="auth_description">Vážený rodiči,<br/><br/>Chcete-li autorizovat a zajistit bezpečnost dat, prosíme Vás, abyste níže zadali PESEL číslo žáka <b>%1$s</b>. Tyto detaily jsou nutné pro správné přidělování přístupu k osobním údajům a jejich ochranu v souladu s platnými předpisy.<br/><br/>Po zadání údajů budou data ověřena, čímž se zajistí, že přístup do systému VULCAN získají pouze autorizované osoby. Pokud máte jakékoliv pochybnosti nebo problémy, kontaktujte prosím školního správce deníku pro objasnění situace.<br/><br/>Udržujeme nejvyšší standardy ochrany osobních údajů a zajišťujeme, aby byly všechny poskytnuté informace chráněné. Wulkanowy neukládá ani nezpracovává číslo PESEL.<br/><br/>Připomínáme, že poskytování úplných a přesných údajů je nutné a nezbytné k používání systému VULCAN.</string>
|
||||
<string name="auth_button_skip">Zatím přeskočit</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">Webová stránka deníku VULCAN vyžaduje ověření</string>
|
||||
|
@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Light</item>
|
||||
<item>Dark</item>
|
||||
<item>Black (AMOLED)</item>
|
||||
</string-array>
|
||||
<string-array name="app_language_entries">
|
||||
<item>System language</item>
|
||||
<item>Polski</item>
|
||||
<item>English</item>
|
||||
<item>Pусский</item>
|
||||
<item>Українська</item>
|
||||
<item>Deutsch</item>
|
||||
<item>Čeština</item>
|
||||
<item>Slovenčina</item>
|
||||
</string-array>
|
||||
<string-array name="services_interval_entries">
|
||||
<item>15 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
<item>6 hours</item>
|
||||
<item>12 hours</item>
|
||||
<item>24 hours</item>
|
||||
</string-array>
|
||||
<string-array name="grade_modifier_entries">
|
||||
<item>0,00</item>
|
||||
<item>0,25</item>
|
||||
<item>0,33</item>
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alphabetically</item>
|
||||
<item>By date</item>
|
||||
<item>By average</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
<item>Grade colors in register</item>
|
||||
</string-array>
|
||||
<string-array name="default_expand_grade_entries">
|
||||
<item>Up to 1 at once</item>
|
||||
<item>Always expanded</item>
|
||||
<item>Unlimited expansions</item>
|
||||
</string-array>
|
||||
<string-array name="grade_average_mode_entries">
|
||||
<item>Average of grades only from selected semester</item>
|
||||
<item>Average of averages from both semesters</item>
|
||||
<item>Average of grades from the whole year</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_gaps_entries">
|
||||
<item>Don\'t show</item>
|
||||
<item>Only between lessons</item>
|
||||
<item>Before and between lessons</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Lucky number</item>
|
||||
<item>Unread messages</item>
|
||||
<item>Attendance</item>
|
||||
<item>Lessons</item>
|
||||
<item>Grades</item>
|
||||
<item>Homework</item>
|
||||
<item>School announcements</item>
|
||||
<item>Exams</item>
|
||||
<item>Conferences</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">Alphabetisch</string>
|
||||
<string name="sort_by_date">Nach Datum</string>
|
||||
<string name="sort_by_average">Nach Durchschnitt</string>
|
||||
<string name="sort_by_attendance_percentage">Nach Anwesenheitsprozent</string>
|
||||
<string name="sort_by_subject_attendance_balance">Nach Subjekt Anwesenheitssaldo</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Licht</item>
|
||||
<item>Dunkel</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alphabetisch</item>
|
||||
<item>Nach Datum</item>
|
||||
<item>Nach Durchschnitt</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,6 +56,11 @@
|
||||
<item>Only between lessons</item>
|
||||
<item>Before and between lessons</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Nicht zeigen</item>
|
||||
<item>Inline anzeigen</item>
|
||||
<item>Unterhalb der regulären Lektionen anzeigen</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Glückszahl</item>
|
||||
<item>Ungelesene Nachrichten</item>
|
||||
|
@ -10,10 +10,10 @@
|
||||
<string name="settings_title">Einstellungen</string>
|
||||
<string name="more_title">Mehr</string>
|
||||
<string name="about_title">Über die Applikation</string>
|
||||
<string name="logviewer_title">Log viewer</string>
|
||||
<string name="logviewer_title">Log Viewer</string>
|
||||
<string name="debug_title">Debuggen</string>
|
||||
<string name="notification_debug_title">Benachrichtigungen debuggen</string>
|
||||
<string name="debug_cookies_clear">Clear webview cookies</string>
|
||||
<string name="debug_cookies_clear">Webview-Cookies löschen</string>
|
||||
<string name="contributors_title">Mitarbeiter</string>
|
||||
<string name="license_title">Lizenzen</string>
|
||||
<string name="message_title">Nachrichten</string>
|
||||
@ -38,14 +38,14 @@
|
||||
<string name="login_login_pesel_email_hint">Anmeldung, PESEL oder e-mail</string>
|
||||
<string name="login_password_hint">Passwort</string>
|
||||
<string name="login_host_hint">UONET+ Registervariante</string>
|
||||
<string name="login_domain_suffix_hint">Custom domain suffix</string>
|
||||
<string name="login_domain_suffix_hint">Benutzerdefinierte Domeisensuffixe</string>
|
||||
<string name="login_type_api">Mobile API</string>
|
||||
<string name="login_type_scrapper">Scraper</string>
|
||||
<string name="login_type_hybrid">Hybride</string>
|
||||
<string name="login_token_hint">Token</string>
|
||||
<string name="login_pin_hint">PIN</string>
|
||||
<string name="login_symbol_hint">Symbol</string>
|
||||
<string name="login_symbol_placeholder">E.g. \"lodz\" or \"powiatjaroslawski\"</string>
|
||||
<string name="login_symbol_placeholder">Zum Beispiel \"lodz\" oder \"powiatjaroslawski\"</string>
|
||||
<string name="login_sign_in">Anmelden</string>
|
||||
<string name="login_invalid_password">Passwort ist zu kurz</string>
|
||||
<string name="login_incorrect_password_default">Anmeldedaten sind falsch</string>
|
||||
@ -56,9 +56,9 @@
|
||||
<string name="login_invalid_email">Ungültige email</string>
|
||||
<string name="login_invalid_login">Den zugewiesenen Login anstelle von email verwenden</string>
|
||||
<string name="login_invalid_custom_email">Benutze den zugewiesenen Login oder E-Mail in @%1$s</string>
|
||||
<string name="login_invalid_domain_suffix">Invalid domain suffix</string>
|
||||
<string name="login_invalid_symbol">Invalid symbol. If you cannot find it, please contact the school</string>
|
||||
<string name="login_invalid_symbol_definitely">Don\'t make this up! If you cannot find it, please contact the school</string>
|
||||
<string name="login_invalid_domain_suffix">Ungültiges Domain-Suffix</string>
|
||||
<string name="login_invalid_symbol">Ungültiges Symbol. Wenn Sie es nicht finden können, wenden Sie sich bitte an die Schule</string>
|
||||
<string name="login_invalid_symbol_definitely">Denken Sie sich das nicht aus! Wenn Sie es nicht finden können, wenden Sie sich bitte an die Schule</string>
|
||||
<string name="login_incorrect_symbol">Schüler nicht gefunden. Überprüfen Sie das Symbol und die gewählte Variation des UONET+ Registers</string>
|
||||
<string name="login_duplicate_student">Ausgewählter Student ist bereits angemeldet.</string>
|
||||
<string name="login_symbol_helper">Das Symbol kann auf der Registerseite in <b>Student </b>→ <b>Tost Möbeln</b> → <b>Registrieren Sie Ihr Mobilgerät</b>gefunden werden.\n\nStellen Sie sicher, dass Sie die entsprechende Registervariante im Feld <b>UONET+ Registervariante</b> auf dem vorherigen Bildschirm festgelegt haben</string>
|
||||
@ -73,7 +73,7 @@
|
||||
<string name="login_contact_discord">Discord</string>
|
||||
<string name="login_email_intent_title">email senden</string>
|
||||
<string name="login_recover_warning">Stellen Sie sicher, dass Sie die richtige UONET+ Registervariation wählen!</string>
|
||||
<string name="login_recover_button">Reset password</string>
|
||||
<string name="login_recover_button">Passwort zurücksetzen</string>
|
||||
<string name="login_recover_title">Ihr Konto wiederherstellen</string>
|
||||
<string name="login_recover">Wiederherstellen</string>
|
||||
<string name="login_signed_in">Student ist bereits angemeldet</string>
|
||||
@ -81,13 +81,13 @@
|
||||
<string name="login_other_search_locations">Andere Suchorte</string>
|
||||
<string name="login_no_active_student">Keine aktiven Schüler gefunden</string>
|
||||
<string name="login_symbol_enter">Geben Sie ein anderes Symbol ein</string>
|
||||
<string name="login_support_title">Get help</string>
|
||||
<string name="login_support_school_hint">Full school name with the town (required)</string>
|
||||
<string name="login_support_school_placeholder">Np. ZSTiO Jarosław lub SP nr 99 w Łodzi</string>
|
||||
<string name="login_support_school_invalid">Enter correct name of the school</string>
|
||||
<string name="login_support_additional_hint">Additional information in Polish (optional)</string>
|
||||
<string name="login_support_additional_placeholder">Np. \"Ostatnio zmieniłem szkołę i…\" albo \"Jestem rodzicem i nie widzę drugiego dziecka…\"</string>
|
||||
<string name="login_support_submit">Submit</string>
|
||||
<string name="login_support_title">Hilfe anfragen</string>
|
||||
<string name="login_support_school_hint">Vollschulname mit der Stadt (erforderlich)</string>
|
||||
<string name="login_support_school_placeholder">Z. B. ZSTiO Jarosław oder SP nr 99 w Łodzi</string>
|
||||
<string name="login_support_school_invalid">Geben Sie den richtigen Namen der Schule ein</string>
|
||||
<string name="login_support_additional_hint">Zusätzliche Informationen auf Polnisch (fakultativ)</string>
|
||||
<string name="login_support_additional_placeholder">Z. B. „Ich habe kürzlich die Schule gewechselt und...“ oder „Ich bin ein Elternteil und kann das Konto des anderen Kindes nicht sehen...“</string>
|
||||
<string name="login_support_submit">Einreichen</string>
|
||||
<!--Notifications-->
|
||||
<string name="notifications_header_title">Benachrichtigungen aktivieren</string>
|
||||
<string name="notifications_header_description">Aktivieren Sie Benachrichtigungen, damit Sie keine Nachricht vom Lehrer oder eine neue Klasse verpassen</string>
|
||||
@ -98,8 +98,8 @@
|
||||
<string name="main_log_in">Anmelden</string>
|
||||
<string name="main_session_expired">Die Sitzung ist abgelaufen</string>
|
||||
<string name="main_session_relogin">Die Sitzung ist abgelaufen, bitte loggen Sie sich erneut ein</string>
|
||||
<string name="main_expired_credentials_title">Password has expired or been changed</string>
|
||||
<string name="main_expired_credentials_description">Your account password has expired or been changed. You will need to log in to Wulkanowy again</string>
|
||||
<string name="main_expired_credentials_title">Das Passwort ist abgelaufen oder wurde geändert</string>
|
||||
<string name="main_expired_credentials_description">Ihr Passwort ist abgelaufen oder wurde geändert. Sie müssen sich erneut bei Wulkanowy anmelden</string>
|
||||
<string name="main_support_title">Anwendungsunterstützung</string>
|
||||
<string name="main_support_description">Gefällt Ihnen diese App? Unterstützen Sie ihre Entwicklung, indem Sie nicht-invasive Werbung aktivieren, die Sie jederzeit deaktivieren können</string>
|
||||
<string name="main_support_positive">Werbung aktivieren</string>
|
||||
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Kommentar</string>
|
||||
<string name="grade_number_new_items">Anzahl der neuen Bewertungen: %1$d</string>
|
||||
<string name="grade_average">Durchschnitt: %1$.2f</string>
|
||||
<string name="grade_average_year">Jährlich: %1$.2f</string>
|
||||
<string name="grade_points_sum">Punkte: %s</string>
|
||||
<string name="grade_no_average">Kein Durchschnitt</string>
|
||||
<string name="grade_summary_average_semester">Semesterdurchschnitt</string>
|
||||
<string name="grade_summary_average_year">Jahresdurchschnitt</string>
|
||||
<string name="grade_summary_points">Gesamtpunkte</string>
|
||||
<string name="grade_summary_final_grade">Finaler Note</string>
|
||||
<string name="grade_summary_predicted_grade">Vorhergesagte Note</string>
|
||||
<string name="grade_summary_descriptive">Descriptive grade</string>
|
||||
<string name="grade_summary_calculated_average">Berechnender Durchschnitt</string>
|
||||
<string name="grade_summary_descriptive">Deskriptive Note</string>
|
||||
<string name="grade_summary_calculated_average">Berechneter Semesterdurchschnitt</string>
|
||||
<string name="grade_summary_calculated_average_annual">Berechneter Jahresdurchschnitt</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Wie funktioniert der berechnete Durchschnitt?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Der berechnete Mittelwert ist das arithmetische Mittel, das aus den Durchschnittswerten der Probanden errechnet wird. Es erlaubt Ihnen, den ungefähre endgültigen Durchschnitt zu kennen. Sie wird auf eine vom Anwender in den Anwendungseinstellungen gewählte Weise berechnet. Es wird empfohlen, die entsprechende Option zu wählen. Das liegt daran, dass die Berechnung der Schuldurchschnitte unterschiedlich ist. Wenn Ihre Schule den Durchschnitt der Fächer auf der Vulcan-Seite angibt, lädt die Anwendung diese Fächer herunter und berechnet nicht den Durchschnitt. Dies kann geändert werden, indem die Berechnung des Durchschnitts in den Anwendungseinstellungen erzwungen wird. \n\n<b>Durchschnitt der Noten nur aus dem ausgewählten Semester </b>:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in einem bestimmten Semester\n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Durchschnitte\n<b>Durchschnitt der Durchschnitte aus beiden Semestern</b>:\n1. Berechnung des gewichteten Durchschnitts für jedes Fach in Semester 1 und 2\n2. Berechnung des arithmetischen Mittels der berechneten Durchschnitte für Semester 1 und 2 für jedes Fach. \n3. Hinzufügen von berechneten Durchschnittswerten\n4. Berechnung des arithmetischen Mittels der summierten Durchschnitte\n<b>Durchschnitt der Noten aus dem ganzen Jahr:</b>\n1. Berechnung des gewichteten Jahresdurchschnitts für jedes Fach. Der Abschlussdurchschnitt im 1. Semester ist irrelevant. \n2. Addition der berechneten Durchschnittswerte\n3. Berechnung des arithmetischen Mittels der summierten Mittelwerte</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Wie funktioniert der endgültige Durchschnitt?</string>
|
||||
@ -155,8 +159,8 @@
|
||||
<item quantity="other">Neue Abschlussnoten</item>
|
||||
</plurals>
|
||||
<plurals name="grade_new_items_descriptive">
|
||||
<item quantity="one">New descriptive grade</item>
|
||||
<item quantity="other">New descriptive grades</item>
|
||||
<item quantity="one">Neuer Deskriptive Grade</item>
|
||||
<item quantity="other">Neuer Deskriptive Grades</item>
|
||||
</plurals>
|
||||
<plurals name="grade_notify_new_items">
|
||||
<item quantity="one">Du hast %1$d Note bekommen</item>
|
||||
@ -171,11 +175,12 @@
|
||||
<item quantity="other">Sie haben %1$d Abschlussnoten bekommen</item>
|
||||
</plurals>
|
||||
<plurals name="grade_notify_new_items_descriptive">
|
||||
<item quantity="one">You received %1$d descriptive grade</item>
|
||||
<item quantity="other">You received %1$d descriptive grades</item>
|
||||
<item quantity="one">Sie haben %1$d deskriptive Grade erhalten</item>
|
||||
<item quantity="other">Sie haben %1$d deskriptive Grades erhalten</item>
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Lektion</string>
|
||||
<string name="timetable_additional_lesson">Zusätzliche Lektion</string>
|
||||
<string name="timetable_room">Klassenzimmer</string>
|
||||
<string name="timetable_group">Gruppe</string>
|
||||
<string name="timetable_time">Stunden</string>
|
||||
@ -194,8 +199,8 @@
|
||||
<string name="timetable_notify_change_teacher">Wechsel des Lehrers von %1$s zu %2$s</string>
|
||||
<string name="timetable_notify_change_subject">Thema von %1$s zu %2$s wechseln</string>
|
||||
<plurals name="timetable_no_lesson">
|
||||
<item quantity="one">No lesson</item>
|
||||
<item quantity="other">No lessons</item>
|
||||
<item quantity="one">Keine Lektion</item>
|
||||
<item quantity="other">Keine Lektionen</item>
|
||||
</plurals>
|
||||
<plurals name="timetable_notify_new_items_title">
|
||||
<item quantity="one">Änderung des Zeitplans</item>
|
||||
@ -237,6 +242,12 @@
|
||||
<string name="additional_lessons_end_time_error">Endzeit muss grösser sein als Startzeit</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Übersicht über die Schulbesuch</string>
|
||||
<string name="attendance_calculator_button">Anwesenheitsrechner</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> Über Ziel</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">direkt am ziel</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> Unter Ziel</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d Präsenzen</string>
|
||||
<string name="attendance_calculator_summary_values_empty">Keine Anwesenheit verzeichnet</string>
|
||||
<string name="attendance_absence_school">Aus schulischen Gründen abwesend</string>
|
||||
<string name="attendance_absence_excused">Entschuldigte Abwesenheit</string>
|
||||
<string name="attendance_absence_unexcused">Unentschuldigtes Abwesenheit</string>
|
||||
@ -296,10 +307,10 @@
|
||||
<string name="message_forward">Weiterleiten</string>
|
||||
<string name="message_select_all">Alle auswählen</string>
|
||||
<string name="message_unselect_all">Alle abwählen</string>
|
||||
<string name="message_restore_from_trash">Restore from trash</string>
|
||||
<string name="message_restore_from_trash">Wiederherstellen aus dem Papierkorb</string>
|
||||
<string name="message_move_to_trash">In Papierkorb verschieben</string>
|
||||
<string name="message_delete_forever">Dauerhaft löschen</string>
|
||||
<string name="message_restore_success">Message restored successfully</string>
|
||||
<string name="message_restore_success">Nachricht erfolgreich wiederhergestellt</string>
|
||||
<string name="message_delete_success">Nachricht erfolgreich gelöscht</string>
|
||||
<string name="message_mailbox_type_student">schüler</string>
|
||||
<string name="message_mailbox_type_parent">Eltern</string>
|
||||
@ -337,10 +348,10 @@
|
||||
<item quantity="other">%1$d ausgewählt</item>
|
||||
</plurals>
|
||||
<string name="message_messages_deleted">Nachrichten gelöscht</string>
|
||||
<string name="message_messages_restored">Messages restored</string>
|
||||
<string name="message_messages_restored">Wiederhergestellte Nachrichten</string>
|
||||
<string name="message_mailbox_chooser_title">Postfach auswählen</string>
|
||||
<string name="message_incognito_mode_on">Incognito mode is on</string>
|
||||
<string name="message_incognito_description">Thanks to incognito mode sender is not notified when you read the message</string>
|
||||
<string name="message_incognito_mode_on">Inkognito-Modus ist aktiviert</string>
|
||||
<string name="message_incognito_description">Dank des Inkognito-Modus wird der Absender nicht benachrichtigt, wenn Sie die Nachricht lesen</string>
|
||||
<!--Note-->
|
||||
<string name="note_no_items">Keine Informationen über Eintragen</string>
|
||||
<string name="note_points">Punkte</string>
|
||||
@ -637,10 +648,14 @@
|
||||
<string name="pref_view_grade_average_mode">Berechnete Durchschnittsoptionen</string>
|
||||
<string name="pref_view_grade_average_force_calc">Mittelwertberechnung durch App erzwingen</string>
|
||||
<string name="pref_view_present">Anwesendheit zeigen</string>
|
||||
<string name="pref_attendance_target">Anwesenheitsziel</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Lektion ohne Anwesenheit anzeigen</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Anwesenheitsrechner Sortierung</string>
|
||||
<string name="pref_view_app_theme">Thema</string>
|
||||
<string name="pref_view_expand_grade">Steigende Sorten</string>
|
||||
<string name="pref_view_timetable_show_groups">Gruppen neben Schulfächen anzeigen</string>
|
||||
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Zusätzliche Lektionen anzeigen</string>
|
||||
<string name="pref_view_timetable_show_gaps">Leere Kacheln anzeigen, wenn es keinen Lektionen gibt</string>
|
||||
<string name="pref_view_grade_statistics_list">Liste der Diagramme in Klassenbewertungen anzeigen</string>
|
||||
<string name="pref_view_subjects_without_grades">Schulfächer ohne Noten anzeigen</string>
|
||||
<string name="pref_view_grade_color_scheme">Farbschema der Noten</string>
|
||||
@ -681,12 +696,12 @@
|
||||
<string name="pref_other_grade_modifier_minus">Wert des Minus</string>
|
||||
<string name="pref_other_fill_message_content">Antwort mit Nachrichtenhistorie</string>
|
||||
<string name="pref_other_optional_arithmetic_average">Arithmetisches Mittel anzeigen, wenn keine Gewichte angegeben sind</string>
|
||||
<string name="pref_other_incognito_mode">Incognito mode</string>
|
||||
<string name="pref_other_incognito_mode_summary">Do not inform about reading the message</string>
|
||||
<string name="pref_other_incognito_mode">Inkognito-Modus</string>
|
||||
<string name="pref_other_incognito_mode_summary">Nicht über das Lesen der Nachricht informieren</string>
|
||||
<string name="pref_ads_support_category_name">Unterstützung</string>
|
||||
<string name="pref_ads_privacy_policy">Datenschutz-Bestimmungen</string>
|
||||
<string name="pref_ads_agreements">Vereinbarungen</string>
|
||||
<string name="pref_ads_consent">Show consent to data processing</string>
|
||||
<string name="pref_ads_consent">Einwilligung zur Datenverarbeitung zeigen</string>
|
||||
<string name="pref_ads_show_in_app">Anzeigen in der App anzeigen</string>
|
||||
<string name="pref_ads_support">Einzelanzeige ansehen, um Projekt zu unterstützen</string>
|
||||
<string name="pref_ads_privacy_title">Einwilligung in die Datenverarbeitung</string>
|
||||
@ -704,6 +719,8 @@
|
||||
<string name="pref_dashboard_appearance_header">Dashboard</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Sichtbarkeit der Kacheln</string>
|
||||
<string name="pref_attendance_appearance_view">Schulbesuch</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Anwesenheits-Rechner</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Einstellungen</string>
|
||||
<string name="pref_timetable_appearance_view">Stundenplan</string>
|
||||
<string name="pref_grades_advanced_header">Noten</string>
|
||||
<string name="pref_counted_average_advanced_header">Berechneter Durchschnitt</string>
|
||||
@ -749,37 +766,37 @@
|
||||
<string name="menu_order_confirm_content">Die Anwendung muss neu gestartet werden, damit die Änderungen gespeichert werden</string>
|
||||
<string name="menu_order_confirm_restart">Restart</string>
|
||||
<!--Auth-->
|
||||
<string name="auth_api_error">Authorization has been rejected. The data provided does not match the records in the secretary\'s office.</string>
|
||||
<string name="auth_invalid_error">Invalid PESEL</string>
|
||||
<string name="auth_api_error">Die Autorisierung wurde abgelehnt. Die vorgelegten Daten stimmen nicht mit denen des Sekretariats überein.</string>
|
||||
<string name="auth_invalid_error">Ungültig PESEL</string>
|
||||
<string name="auth_pesel">PESEL</string>
|
||||
<string name="auth_button">Authorize</string>
|
||||
<string name="auth_success">Authorization completed successfully</string>
|
||||
<string name="auth_title">Authorization</string>
|
||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||
<string name="auth_button_skip">Skip for now</string>
|
||||
<string name="auth_button">Autorisieren Sie</string>
|
||||
<string name="auth_success">Autorisierung erfolgreich abgeschlossen</string>
|
||||
<string name="auth_title">Autorisierung</string>
|
||||
<string name="auth_description">Liebes Elternteil,<br/><br/>Um die Sicherheit der Daten zu gewährleisten, bitten wir Sie, die PESEL-Nummer des Schülers/der Schülerin anzugeben<b>%1$s</b>Diese Angaben sind für die ordnungsgemäße Zuweisung des Zugriffs und den Schutz der personenbezogenen Daten gemäß den geltenden Vorschriften unerlässlich.<br/><br/>Nach der Eingabe der Daten werden diese überprüft, um sicherzustellen, dass nur berechtigte Personen Zugang zum VULCAN-System erhalten. Wenn Sie Zweifel oder Probleme haben, wenden Sie sich bitte an den Administrator des Schülerkalenders, um die Situation zu klären.<br/><br/>Wir halten die höchsten Standards für den Schutz personenbezogener Daten ein und gewährleisten, dass alle bereitgestellten Informationen sicher sind. Die Wulkanowy-App speichert und verarbeitet die PESEL-Nummer nicht.<br/><br/>Wir erinnern Sie daran, dass die Angabe vollständiger und korrekter Daten obligatorisch und notwendig für die Nutzung des VULCAN-Systems ist.</string>
|
||||
<string name="auth_button_skip">Vorerst überspringen</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">VULCAN\'s website requires verification</string>
|
||||
<string name="captcha_dialog_description"><b>Why am I seeing this?</b>\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</string>
|
||||
<string name="captcha_verified_message">Verified successfully</string>
|
||||
<string name="captcha_dialog_title">VULCAN\'s Website erfordert Überprüfung</string>
|
||||
<string name="captcha_dialog_description"><b>Warum sehe ich das?</b>\nDie Website des Registers, von der Wulkanowy Daten herunterlädt, zeigt denselben Bildschirm wie oben an, so dass Wulkanowy ihn ebenfalls anzeigen muss, um Daten von dieser Website herunterladen zu können. Es gibt keinen Ausweg</string>
|
||||
<string name="captcha_verified_message">Erfolgreich verifiziert</string>
|
||||
<!--Errors-->
|
||||
<string name="error_no_internet">Keine Internetverbindung</string>
|
||||
<string name="error_invalid_device_datetime">Es ist ein Fehler aufgetreten. Überprüfen Sie Ihre Geräteuhr</string>
|
||||
<string name="error_account_inactive">This account is inactive. Try logging in again</string>
|
||||
<string name="error_account_inactive">Dieses Konto ist inaktiv. Versuchen Sie, sich erneut anzumelden</string>
|
||||
<string name="error_timeout">Registrierungsverbindung fehlgeschlagen. Server können überlastet sein. Bitte versuchen Sie es später noch einmal</string>
|
||||
<string name="error_login_failed">Das Laden der Daten ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal</string>
|
||||
<string name="error_password_invalid">Your password has expired or been changed. Please log in again</string>
|
||||
<string name="error_password_invalid">Ihr Passwort ist abgelaufen oder wurde geändert. Bitte melden Sie sich erneut an</string>
|
||||
<string name="error_password_change_required">Passwortänderung für Registrierung erforderlich</string>
|
||||
<string name="error_service_unavailable">Wartung im Gange UONET + Klassenbuch. Versuchen Sie es später noch einmal</string>
|
||||
<string name="error_unknown_uonet">Unbekannter UONET + Registerfehler. Versuchen Sie es später erneut</string>
|
||||
<string name="error_unknown_app">Unbekannter Anwendungsfehler. Bitte versuchen Sie es später noch einmal</string>
|
||||
<string name="error_cloudflare_captcha">Captcha verification required</string>
|
||||
<string name="error_cloudflare_captcha">Captcha-Verifizierung erforderlich</string>
|
||||
<string name="error_unknown">Ein unerwarteter Fehler ist aufgetreten</string>
|
||||
<string name="error_feature_disabled">Funktion, die von Ihrer Schule deaktiviert wurde</string>
|
||||
<string name="error_feature_not_available">Feature in diesem Modus nicht verfügbar</string>
|
||||
<string name="error_field_required">Dieses Feld ist erforderlich</string>
|
||||
<!-- Mute system -->
|
||||
<string name="message_mute">Mute</string>
|
||||
<string name="message_unmute">Unmute</string>
|
||||
<string name="message_mute_success">You have muted this user</string>
|
||||
<string name="message_unmute_success">You have unmuted this user</string>
|
||||
<string name="message_mute">Stumm</string>
|
||||
<string name="message_unmute">Stummschaltung aufheben</string>
|
||||
<string name="message_mute_success">Sie haben diesen Benutzer stummgeschaltet</string>
|
||||
<string name="message_unmute_success">Sie haben die Stummschaltung dieses Benutzers aufgehoben</string>
|
||||
</resources>
|
||||
|
@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Light</item>
|
||||
<item>Dark</item>
|
||||
<item>Black (AMOLED)</item>
|
||||
</string-array>
|
||||
<string-array name="app_language_entries">
|
||||
<item>System language</item>
|
||||
<item>Polski</item>
|
||||
<item>English</item>
|
||||
<item>Pусский</item>
|
||||
<item>Українська</item>
|
||||
<item>Deutsch</item>
|
||||
<item>Čeština</item>
|
||||
<item>Slovenčina</item>
|
||||
</string-array>
|
||||
<string-array name="services_interval_entries">
|
||||
<item>15 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
<item>6 hours</item>
|
||||
<item>12 hours</item>
|
||||
<item>24 hours</item>
|
||||
</string-array>
|
||||
<string-array name="grade_modifier_entries">
|
||||
<item>0,00</item>
|
||||
<item>0,25</item>
|
||||
<item>0,33</item>
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alphabetically</item>
|
||||
<item>By date</item>
|
||||
<item>By average</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
<item>Grade colors in register</item>
|
||||
</string-array>
|
||||
<string-array name="default_expand_grade_entries">
|
||||
<item>Up to 1 at once</item>
|
||||
<item>Always expanded</item>
|
||||
<item>Unlimited expansions</item>
|
||||
</string-array>
|
||||
<string-array name="grade_average_mode_entries">
|
||||
<item>Average of grades only from selected semester</item>
|
||||
<item>Average of averages from both semesters</item>
|
||||
<item>Average of grades from the whole year</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_gaps_entries">
|
||||
<item>Don\'t show</item>
|
||||
<item>Only between lessons</item>
|
||||
<item>Before and between lessons</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Lucky number</item>
|
||||
<item>Unread messages</item>
|
||||
<item>Attendance</item>
|
||||
<item>Lessons</item>
|
||||
<item>Grades</item>
|
||||
<item>Homework</item>
|
||||
<item>School announcements</item>
|
||||
<item>Exams</item>
|
||||
<item>Conferences</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Light</item>
|
||||
<item>Dark</item>
|
||||
<item>Black (AMOLED)</item>
|
||||
</string-array>
|
||||
<string-array name="app_language_entries">
|
||||
<item>System language</item>
|
||||
<item>Polski</item>
|
||||
<item>English</item>
|
||||
<item>Pусский</item>
|
||||
<item>Українська</item>
|
||||
<item>Deutsch</item>
|
||||
<item>Čeština</item>
|
||||
<item>Slovenčina</item>
|
||||
</string-array>
|
||||
<string-array name="services_interval_entries">
|
||||
<item>15 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
<item>6 hours</item>
|
||||
<item>12 hours</item>
|
||||
<item>24 hours</item>
|
||||
</string-array>
|
||||
<string-array name="grade_modifier_entries">
|
||||
<item>0,00</item>
|
||||
<item>0,25</item>
|
||||
<item>0,33</item>
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alphabetically</item>
|
||||
<item>By date</item>
|
||||
<item>By average</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
<item>Grade colors in register</item>
|
||||
</string-array>
|
||||
<string-array name="default_expand_grade_entries">
|
||||
<item>Up to 1 at once</item>
|
||||
<item>Always expanded</item>
|
||||
<item>Unlimited expansions</item>
|
||||
</string-array>
|
||||
<string-array name="grade_average_mode_entries">
|
||||
<item>Average of grades only from selected semester</item>
|
||||
<item>Average of averages from both semesters</item>
|
||||
<item>Average of grades from the whole year</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_gaps_entries">
|
||||
<item>Don\'t show</item>
|
||||
<item>Only between lessons</item>
|
||||
<item>Before and between lessons</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Lucky number</item>
|
||||
<item>Unread messages</item>
|
||||
<item>Attendance</item>
|
||||
<item>Lessons</item>
|
||||
<item>Grades</item>
|
||||
<item>Homework</item>
|
||||
<item>School announcements</item>
|
||||
<item>Exams</item>
|
||||
<item>Conferences</item>
|
||||
</string-array>
|
||||
</resources>
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">Alfabetycznie</string>
|
||||
<string name="sort_by_date">Według daty</string>
|
||||
<string name="sort_by_average">Według średniej</string>
|
||||
<string name="sort_by_attendance_percentage">Według procentu obecności</string>
|
||||
<string name="sort_by_subject_attendance_balance">Według balansu frekwencji przedmiotu</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Jasny</item>
|
||||
<item>Ciemny</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alfabetycznie</item>
|
||||
<item>Według daty</item>
|
||||
<item>Według średniej</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,6 +56,11 @@
|
||||
<item>Tylko między lekcjami</item>
|
||||
<item>Przed i między lekcjami</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Nie pokazuj</item>
|
||||
<item>Pokaż razem</item>
|
||||
<item>Pokaż poniżej zwykłych lekcji</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Szczęśliwy numerek</item>
|
||||
<item>Nieprzeczytane wiadomości</item>
|
||||
|
@ -198,6 +198,7 @@
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Lekcja</string>
|
||||
<string name="timetable_additional_lesson">Dodatkowa lekcja</string>
|
||||
<string name="timetable_room">Sala</string>
|
||||
<string name="timetable_group">Grupa</string>
|
||||
<string name="timetable_time">Godziny</string>
|
||||
@ -269,6 +270,12 @@
|
||||
<string name="additional_lessons_end_time_error">Godzina zakończenia musi być późniejsza niż godzina rozpoczęcia</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Podsumowanie frekwencji</string>
|
||||
<string name="attendance_calculator_button">Kalkulator obecności</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> powyżej celu</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">dokładnie u celu</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> poniżej celu</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d obecności</string>
|
||||
<string name="attendance_calculator_summary_values_empty">Nie odnotowano żadnej frekwencji</string>
|
||||
<string name="attendance_absence_school">Nieobecność z przyczyn szkolnych</string>
|
||||
<string name="attendance_absence_excused">Nieobecność usprawiedliwiona</string>
|
||||
<string name="attendance_absence_unexcused">Nieobecność nieusprawiedliwiona</string>
|
||||
@ -735,9 +742,13 @@
|
||||
<string name="pref_view_grade_average_mode">Opcje obliczonej średniej</string>
|
||||
<string name="pref_view_grade_average_force_calc">Wymuś obliczanie średniej przez aplikację</string>
|
||||
<string name="pref_view_present">Pokazuj obecność</string>
|
||||
<string name="pref_attendance_target">Docelowa obecność</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Pokazuj przedmioty bez frekwencji</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Sortowanie kalkulatora obecności</string>
|
||||
<string name="pref_view_app_theme">Motyw</string>
|
||||
<string name="pref_view_expand_grade">Rozwijanie ocen</string>
|
||||
<string name="pref_view_timetable_show_groups">Pokazuj grupę obok przedmiotu</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Pokaż dodatkowe lekcje</string>
|
||||
<string name="pref_view_timetable_show_gaps">Pokazuj puste kafelki gdzie nie ma lekcji</string>
|
||||
<string name="pref_view_grade_statistics_list">Pokazuj listę wykresów w ocenach klasy</string>
|
||||
<string name="pref_view_subjects_without_grades">Pokazuj przedmioty bez ocen</string>
|
||||
@ -802,6 +813,8 @@
|
||||
<string name="pref_dashboard_appearance_header">Start</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Widoczność kafelków</string>
|
||||
<string name="pref_attendance_appearance_view">Frekwencja</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Kalkulator frekwencji</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Ustawienia</string>
|
||||
<string name="pref_timetable_appearance_view">Plan lekcji</string>
|
||||
<string name="pref_grades_advanced_header">Oceny</string>
|
||||
<string name="pref_counted_average_advanced_header">Obliczona średnia</string>
|
||||
@ -853,7 +866,7 @@
|
||||
<string name="auth_button">Potwierdź</string>
|
||||
<string name="auth_success">Autoryzacja zakończona pomyślnie</string>
|
||||
<string name="auth_title">Autoryzacja</string>
|
||||
<string name="auth_description">Rodzicu, musimy mieć pewność, że Twój adres e-mail został powiązany z prawidłowym kontem ucznia. W celu autoryzacji konta podaj numer PESEL ucznia <b>%1$s</b> w polu poniżej</string>
|
||||
<string name="auth_description">Szanowny Rodzicu,<br/><br/>W celu autoryzacji i zapewnienia bezpieczeństwa danych, uprzejmie prosimy o wprowadzenie poniżej numeru PESEL ucznia <b>%1$s</b>. Te informacje są niezbędne do prawidłowego przypisania dostępu i ochrony danych osobowych zgodnie z obowiązującymi przepisami.<br/><br/>Po wprowadzeniu danych, będą one weryfikowane w celu zapewnienia, że dostęp do systemu VULCAN jest przyznawany wyłącznie upoważnionym osobom. W przypadku jakichkolwiek wątpliwości lub problemów, prosimy o kontakt z administratorem dziennika szkolnego w celu wyjaśnienia sytuacji.<br/><br/>Zachowujemy najwyższe standardy ochrony danych osobowych i zapewniamy, że wszelkie przekazane informacje są chronione. Wulkanowy nie przechowuje ani nie przetwarza numeru PESEL.<br/><br/>Przypominamy, że podanie pełnych i prawdziwych danych jest obowiązkowe i konieczne do korzystania z systemu VULCAN.</string>
|
||||
<string name="auth_button_skip">Na razie pomiń</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">Strona dziennika VULCAN wymaga weryfikacji</string>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">Alphabetically</string>
|
||||
<string name="sort_by_date">By date</string>
|
||||
<string name="sort_by_average">By average</string>
|
||||
<string name="sort_by_attendance_percentage">By attendance percentage</string>
|
||||
<string name="sort_by_subject_attendance_balance">By subject attendance balance</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Светлая</item>
|
||||
<item>Тёмная</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>В алфавитном порядке</item>
|
||||
<item>По дате</item>
|
||||
<item>По средней</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,6 +56,11 @@
|
||||
<item>Only between lessons</item>
|
||||
<item>Before and between lessons</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Don\'t show</item>
|
||||
<item>Show inline</item>
|
||||
<item>Show below regular lessons</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Счастливый номер</item>
|
||||
<item>Непрочитанные письма</item>
|
||||
|
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Комментарий</string>
|
||||
<string name="grade_number_new_items">Количество новых оценок: %1$d</string>
|
||||
<string name="grade_average">Средняя оценка: %1$.2f</string>
|
||||
<string name="grade_average_year">Annual: %1$.2f</string>
|
||||
<string name="grade_points_sum">Баллы: %s</string>
|
||||
<string name="grade_no_average">Нет средней оценки</string>
|
||||
<string name="grade_summary_average_semester">Semester average</string>
|
||||
<string name="grade_summary_average_year">Annual average</string>
|
||||
<string name="grade_summary_points">Сумма баллов</string>
|
||||
<string name="grade_summary_final_grade">Итоговая оценка</string>
|
||||
<string name="grade_summary_predicted_grade">Ожидаемая оценка</string>
|
||||
<string name="grade_summary_descriptive">Descriptive grade</string>
|
||||
<string name="grade_summary_calculated_average">Рассчитанная средняя оценка</string>
|
||||
<string name="grade_summary_calculated_average">Calculated semester average</string>
|
||||
<string name="grade_summary_calculated_average_annual">Calculated annual average</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Как работает \"Рассчитанная средняя оценка\"?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Рассчитанная средняя оценка - это среднее арифметическое, рассчитанное на основе средних оценок по предметам. Это позволяет узнать приблизительную итоговую среднюю оценку. Она рассчитывается способом, выбранным пользователем в настройках приложения. Рекомендуется выбрать подходящий вариант, так как каждая школа по разному считает среднюю оценку. Кроме того, если ваша школа выставляет средние оценки по предметам на странице Vulcan, приложение просто загрузит их. Это можно изменить, заставив приложение считать среднюю оценку в настройках.\n\n<b>Средняя из оценок выбранного семестра</b>:\n1. Вычисление средневзвешенного значения по каждому предмету за семестр\n2.Суммирование вычисленных значений\n3. Вычисление среднего арифметического суммированных значений\n\n<b>Средняя из средних оценок семестров</b>:\n1.Расчет средневзвешенного значения для каждого предмета в семестрах. \n2. Вычисление среднего арифметического из средневзвешенных значений для каждого предмета в семестрах.\n3. Суммирование средних арифметических\n4. Вычисление среднего арифматического из суммированных значений\n\n<b>Средняя из оценок со всего года:</b>\n1. Расчет средневзвешенного значения по каждому предмету за год. Итоговое среднее значение за 1 семестр не имеет значения.\n2. Суммирование вычисленных средних\n3. Расчет среднего арифметического суммированных чисел</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Как работает \"Итоговая средняя оценка\"?</string>
|
||||
@ -194,6 +198,7 @@
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Урок</string>
|
||||
<string name="timetable_additional_lesson">Additional lesson</string>
|
||||
<string name="timetable_room">Аудитория</string>
|
||||
<string name="timetable_group">Группа</string>
|
||||
<string name="timetable_time">Часы</string>
|
||||
@ -265,6 +270,12 @@
|
||||
<string name="additional_lessons_end_time_error">Время окончания должно быть больше, чем время начала</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Итоговая посещаемость</string>
|
||||
<string name="attendance_calculator_button">Attendance calculator</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> over target</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">right on target</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> under target</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d presences</string>
|
||||
<string name="attendance_calculator_summary_values_empty">No attendances recorded</string>
|
||||
<string name="attendance_absence_school">Отсутствие по школьным причинам</string>
|
||||
<string name="attendance_absence_excused">Отсутствие по уважительной причине</string>
|
||||
<string name="attendance_absence_unexcused">Отсутствие по неуважительной причине</string>
|
||||
@ -731,9 +742,13 @@
|
||||
<string name="pref_view_grade_average_mode">Параметры расчёта средних оценок</string>
|
||||
<string name="pref_view_grade_average_force_calc">Принудительно высчитать среднюю оценку через приложение</string>
|
||||
<string name="pref_view_present">Показывать присутствия</string>
|
||||
<string name="pref_attendance_target">Attendance target</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Show subjects without any attendances</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Attendance calculator sorting</string>
|
||||
<string name="pref_view_app_theme">Тема</string>
|
||||
<string name="pref_view_expand_grade">Разворачивание оценок</string>
|
||||
<string name="pref_view_timetable_show_groups">Показать группы рядом с темами</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Show additional lessons</string>
|
||||
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
|
||||
<string name="pref_view_grade_statistics_list">Показывать диаграммы в оценках класса</string>
|
||||
<string name="pref_view_subjects_without_grades">Показать предметы без оценок</string>
|
||||
@ -798,6 +813,8 @@
|
||||
<string name="pref_dashboard_appearance_header">Главная</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Видимость плиток</string>
|
||||
<string name="pref_attendance_appearance_view">Посещаемость</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Attendance calculator</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Settings</string>
|
||||
<string name="pref_timetable_appearance_view">Расписание</string>
|
||||
<string name="pref_grades_advanced_header">Оценки</string>
|
||||
<string name="pref_counted_average_advanced_header">Рассчитанная средняя оценка</string>
|
||||
@ -849,7 +866,7 @@
|
||||
<string name="auth_button">Авторизовать</string>
|
||||
<string name="auth_success">Авторизация прошла успешно</string>
|
||||
<string name="auth_title">Авторизация</string>
|
||||
<string name="auth_description">Для работы приложения нам необходимо подтвердить вашу личность. Введите PESEL учащегося <b>%1$s</b> в поле ниже</string>
|
||||
<string name="auth_description">Dear Parent,<br /><br />To authorize and ensure the security of data, we kindly ask you to enter below PESEL number of student <b>%1$s</b>. These details are essential for the proper assignment of access and protection of personal data in accordance with applicable regulations.<br /><br />After entering the data, it will be verified to ensure that access to the VULCAN system is granted exclusively to authorized individuals. Should you have any doubts or problems, please contact the school diary administrator to clarify the situation.<br /><br />We maintain the highest standards of personal data protection and ensure that all information provided is secure. Wulkanowy app does not store or process the PESEL number.<br /><br />We remind you that providing full and accurate data is mandatory and necessary for the use of the VULCAN system.</string>
|
||||
<string name="auth_button_skip">Пропустить сейчас</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">VULCAN\'s website requires verification</string>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">Abecedne</string>
|
||||
<string name="sort_by_date">Podľa dátumu</string>
|
||||
<string name="sort_by_average">Podľa priemeru</string>
|
||||
<string name="sort_by_attendance_percentage">Podľa percenta dochádzky</string>
|
||||
<string name="sort_by_subject_attendance_balance">Podľa rovnováhy dochádzky predmetu</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Svetlý</item>
|
||||
<item>Tmavý</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Abecedne</item>
|
||||
<item>Podľa dátumu</item>
|
||||
<item>Podľa priemeru</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,10 +56,15 @@
|
||||
<item>Iba medzi lekciami</item>
|
||||
<item>Pred a medzi lekciami</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Nezobrazovať</item>
|
||||
<item>Zobraziť v rade</item>
|
||||
<item>Zobraziť pod pravidelnými hodinami</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Šťastné číslo</item>
|
||||
<item>Neprečítané správy</item>
|
||||
<item>Frekvencia</item>
|
||||
<item>Dochádzka</item>
|
||||
<item>Lekcie</item>
|
||||
<item>Známky</item>
|
||||
<item>Domáce úlohy</item>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<string name="login_title">Prihlásenie</string>
|
||||
<string name="main_title">Wulkanowy</string>
|
||||
<string name="grade_title">Známky</string>
|
||||
<string name="attendance_title">Frekvencia</string>
|
||||
<string name="attendance_title">Dochádzka</string>
|
||||
<string name="exam_title">Skúšky</string>
|
||||
<string name="timetable_title">Plán lekcie</string>
|
||||
<string name="settings_title">Nastavenia</string>
|
||||
@ -31,7 +31,7 @@
|
||||
<!--Subtitles-->
|
||||
<string name="grade_subtitle">Semester %1$d, %2$d/%3$d</string>
|
||||
<!--Login-->
|
||||
<string name="login_header_default">Prihláste sa pomocou študentského alebo rodičovského konta</string>
|
||||
<string name="login_header_default">Prihláste sa pomocou žiackeho alebo rodičovského účtu</string>
|
||||
<string name="login_header_symbol">Zadajte symbol zo stránky denníka: <b>%1$s</b></string>
|
||||
<string name="login_nickname_hint">Užívateľské meno</string>
|
||||
<string name="login_email_hint">Email</string>
|
||||
@ -64,7 +64,7 @@
|
||||
<string name="login_symbol_helper">Symbol nájdete na stránke denníka v  <b>Uczeń</b>→ <b>Dostęp Mobilny</b> → <b>Wygeneruj kod dostępu</b>.\n\nUistite sa, že ste nastavili správny variant denníka v poli <b>Variácia denníka UONET+</b> na prvej prihlasovacej obrazovke</string>
|
||||
<string name="login_select_student">Vyberte žiakov, ktorí sa majú do aplikácie prihlásiť</string>
|
||||
<string name="login_advanced">Iné možnosti</string>
|
||||
<string name="login_advanced_warning_mobile_api">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í</string>
|
||||
<string name="login_advanced_warning_mobile_api">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í</string>
|
||||
<string name="login_advanced_warning_scraper">Tento režim zobrazuje rovnaké dáta, ktoré sa zobrazujú na webových stránkach denníka</string>
|
||||
<string name="login_advanced_warning_hybrid">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</string>
|
||||
<string name="login_privacy_policy">Ochrana osobných údajov</string>
|
||||
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Komentár</string>
|
||||
<string name="grade_number_new_items">Počet nových známok %1$d</string>
|
||||
<string name="grade_average">Priemer: %1$.2f</string>
|
||||
<string name="grade_average_year">Ročný: %1$.2f</string>
|
||||
<string name="grade_points_sum">Body: %s</string>
|
||||
<string name="grade_no_average">Bez priemeru</string>
|
||||
<string name="grade_summary_average_semester">Polročný priemer</string>
|
||||
<string name="grade_summary_average_year">Ročný priemer</string>
|
||||
<string name="grade_summary_points">Súčet bodov</string>
|
||||
<string name="grade_summary_final_grade">Konečná známka</string>
|
||||
<string name="grade_summary_predicted_grade">Predpokladaná známka</string>
|
||||
<string name="grade_summary_descriptive">Popisná známka</string>
|
||||
<string name="grade_summary_calculated_average">Vypočítaný priemer</string>
|
||||
<string name="grade_summary_calculated_average">Vypočítaný polročný priemer</string>
|
||||
<string name="grade_summary_calculated_average_annual">Vypočítaný ročný priemer</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Ako funguje vypočítaný priemer?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Vypočítaný priemer je aritmetický priemer vypočítaný z priemerov predmetov. Umožňuje vám to poznať približný konečný priemer. Vypočítava sa spôsobom zvoleným užívateľom v nastaveniach aplikácii. Odporúča sa vybrať príslušnú možnosť. Dôvodom je rozdielny výpočet školských priemerov. Ak vaša škola navyše uvádza priemer predmetov na stránke denníka Vulcan, aplikácia si ich stiahne a tieto priemery nepočíta. To možno zmeniť vynútením výpočtu priemeru v nastavení aplikácii.\n\n<b>Priemer známok iba z vybraného semestra</b>:\n1. Výpočet váženého priemeru pre každý predmet v danom semestri\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov\n\n<b>Priemer priemerov z oboch semestrov</b>:\n1. Výpočet váženého priemeru pre každý predmet v semestri 1 a 2\n2. Výpočet aritmetického priemeru vypočítaných priemerov za semestre 1 a 2 pre každý predmet.\n3. Sčítanie vypočítaných priemerov\n4. Výpočet aritmetického priemeru součtených priemerov\n\n<b>Priemer známok z celého roka:</b>\n1. Výpočet váženého priemeru za rok pre každý predmet. Konečný priemer v 1. semestri je nepodstatný.\n2. Sčítanie vypočítaných priemerov\n3. Výpočet aritmetického priemeru součtených priemerov</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Ako funguje konečný priemer?</string>
|
||||
@ -194,6 +198,7 @@
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Lekcia</string>
|
||||
<string name="timetable_additional_lesson">Ďalšia lekcia</string>
|
||||
<string name="timetable_room">Učebňa</string>
|
||||
<string name="timetable_group">Skupina</string>
|
||||
<string name="timetable_time">Hodiny</string>
|
||||
@ -264,7 +269,13 @@
|
||||
<string name="additional_lessons_end">Čas ukončenia</string>
|
||||
<string name="additional_lessons_end_time_error">Čas ukončenia musí byť neskorší ako čas začatia</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Zhrnutie frekvencií</string>
|
||||
<string name="attendance_summary_button">Zhrnutie dochádzky</string>
|
||||
<string name="attendance_calculator_button">Kalkulačka dochádzky</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> nad cieľom</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">presne v cieli</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> pod cieľom</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d prítomnosti</string>
|
||||
<string name="attendance_calculator_summary_values_empty">Nebola zaznamenaná žiadna dochádzka</string>
|
||||
<string name="attendance_absence_school">Neprítomnosť zo školských dôvodov</string>
|
||||
<string name="attendance_absence_excused">Ospravedlnená neprítomnosť</string>
|
||||
<string name="attendance_absence_unexcused">Neospravedlnená neprítomnosť</string>
|
||||
@ -282,22 +293,22 @@
|
||||
<string name="attendance_excuse_no_selection">Musíte vybrať aspoň jednu neprítomnosť!</string>
|
||||
<string name="attendance_excuse_title">Ospravedlniť</string>
|
||||
<plurals name="attendance_notify_new_items_title">
|
||||
<item quantity="one">Nová frekvencia</item>
|
||||
<item quantity="few">Nové frekvencie</item>
|
||||
<item quantity="many">Nové frekvencie</item>
|
||||
<item quantity="other">Nové frekvencie</item>
|
||||
<item quantity="one">Nová dochádzka</item>
|
||||
<item quantity="few">Nové dochádzky</item>
|
||||
<item quantity="many">Nové dochádzky</item>
|
||||
<item quantity="other">Nové dochádzky</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_notify_new_items">
|
||||
<item quantity="one">%1$d nová frekvencia</item>
|
||||
<item quantity="few">%1$d nové frekvencie</item>
|
||||
<item quantity="many">%1$d nových frekvencií</item>
|
||||
<item quantity="other">%1$d nových frekvencií</item>
|
||||
<item quantity="one">%1$d nová dochádzka</item>
|
||||
<item quantity="few">%1$d nové dochádzky</item>
|
||||
<item quantity="many">%1$d nových dochádzok</item>
|
||||
<item quantity="other">%1$d nových dochádzok</item>
|
||||
</plurals>
|
||||
<plurals name="attendance_number_item">
|
||||
<item quantity="one">%d frekvencia</item>
|
||||
<item quantity="few">%d frekvencie</item>
|
||||
<item quantity="many">%d frekvencií</item>
|
||||
<item quantity="other">%d frekvencií</item>
|
||||
<item quantity="one">%d dochádzka</item>
|
||||
<item quantity="few">%d dochádzky</item>
|
||||
<item quantity="many">%d dochádzok</item>
|
||||
<item quantity="other">%d dochádzok</item>
|
||||
</plurals>
|
||||
<!--Attendance summary-->
|
||||
<string name="attendance_summary_total">Spoločne</string>
|
||||
@ -731,9 +742,13 @@
|
||||
<string name="pref_view_grade_average_mode">Možnosti vypočítaného priemeru</string>
|
||||
<string name="pref_view_grade_average_force_calc">Vynútiť priemerný výpočet podľa aplikácie</string>
|
||||
<string name="pref_view_present">Zobraziť prítomnosť</string>
|
||||
<string name="pref_attendance_target">Cieľová dochádzka</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Zobraziť predmety bez dochádzok</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Triedenie kalkulačky dochádzky</string>
|
||||
<string name="pref_view_app_theme">Motív</string>
|
||||
<string name="pref_view_expand_grade">Rozvijanie známok</string>
|
||||
<string name="pref_view_timetable_show_groups">Zobraziť skupiny vedľa predmetov</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Zobraziť ďalšie lekcie</string>
|
||||
<string name="pref_view_timetable_show_gaps">Zobraziť prázdne dlaždice, kde nie je žiadne lekcie</string>
|
||||
<string name="pref_view_grade_statistics_list">Zobraziť zoznam grafov v známkach triedy</string>
|
||||
<string name="pref_view_subjects_without_grades">Zobraziť predmety bez známok</string>
|
||||
@ -797,7 +812,9 @@
|
||||
<string name="pref_grades_appearance_header">Známky</string>
|
||||
<string name="pref_dashboard_appearance_header">Domov</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Viditeľnosť dlaždíc</string>
|
||||
<string name="pref_attendance_appearance_view">Frekvencia</string>
|
||||
<string name="pref_attendance_appearance_view">Dochádzka</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Kalkulačka dochádzky</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Nastavenia</string>
|
||||
<string name="pref_timetable_appearance_view">Plán lekcie</string>
|
||||
<string name="pref_grades_advanced_header">Známky</string>
|
||||
<string name="pref_counted_average_advanced_header">Vypočítaný priemer</string>
|
||||
@ -825,7 +842,7 @@
|
||||
<string name="channel_upcoming_lessons">Nadchádzajúce lekcie</string>
|
||||
<string name="channel_debug">Ladenie</string>
|
||||
<string name="channel_change_timetable">Zmeny plánu lekcií</string>
|
||||
<string name="channel_new_attendance">Nové frekvencie</string>
|
||||
<string name="channel_new_attendance">Nové dochádzky</string>
|
||||
<!--Colors-->
|
||||
<string name="all_black">Čierna</string>
|
||||
<string name="all_red">Červená</string>
|
||||
@ -849,7 +866,7 @@
|
||||
<string name="auth_button">Autorizovať</string>
|
||||
<string name="auth_success">Autorizácia bola úspešne dokončená</string>
|
||||
<string name="auth_title">Autorizácia</string>
|
||||
<string name="auth_description">Na prevádzku aplikácie potrebujeme potvrdiť vašu identitu. Zadajte PESEL žiaka <b>%1$s</b> v nižšie uvedenom poli</string>
|
||||
<string name="auth_description">Vážený rodiči,<br/><br/>Ak chcete autorizovať a zaistiť bezpečnosť dát, prosíme Vás, aby ste nižšie zadali PESEL číslo žiaka <b>%1$s</b>. Tieto detaily sú nutné pre správne prideľovanie prístupu k osobným údajom a ich ochranu v súlade s platnými predpismi.<br/><br/>Po zadaní údajov budú dáta overené, čím sa zaistí, že prístup do systému VULCAN získajú iba autorizované osoby. Pokiaľ máte akékoľvek pochybnosti alebo problémy, kontaktujte prosím školského správcu denníka pre objasnenie situácie.<br/><br/>Udržujeme najvyššie štandardy ochrany osobných údajov a zaisťujeme, aby boli všetky poskytnuté informácie chránené. Wulkanowy neukladá ani nespracováva číslo PESEL.<br/><br/>Pripomíname, že poskytovanie úplných a presných údajov je nutné a nevyhnutné na používanie systému VULCAN.</string>
|
||||
<string name="auth_button_skip">Zatiaľ preskočiť</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">Webová stránka denníka VULCAN vyžaduje overenie</string>
|
||||
|
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="sort_alphabetically">За алфавітом</string>
|
||||
<string name="sort_by_date">За датою</string>
|
||||
<string name="sort_by_average">За середньою</string>
|
||||
<string name="sort_by_attendance_percentage">За відсотком відвідуваності</string>
|
||||
<string name="sort_by_subject_attendance_balance">За балансом відвідування теми</string>
|
||||
<string-array name="app_theme_entries" tools:ignore="InconsistentArrays">
|
||||
<item>Світла</item>
|
||||
<item>Темна</item>
|
||||
@ -31,11 +36,6 @@
|
||||
<item>0,5</item>
|
||||
<item>0,75</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>За алфавітом</item>
|
||||
<item>За датою</item>
|
||||
<item>За середньою</item>
|
||||
</string-array>
|
||||
<string-array name="grade_color_scheme_entries">
|
||||
<item>Dzienniczek+</item>
|
||||
<item>Wulkanowy</item>
|
||||
@ -56,6 +56,11 @@
|
||||
<item>Тільки між уроками</item>
|
||||
<item>Перед і між уроками</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Не показувати</item>
|
||||
<item>Показати у рядку</item>
|
||||
<item>Показати нижче стандартних уроків</item>
|
||||
</string-array>
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Щасливий номер</item>
|
||||
<item>Непрочитані листи</item>
|
||||
|
@ -113,13 +113,17 @@
|
||||
<string name="grade_comment">Коментар</string>
|
||||
<string name="grade_number_new_items">Кількість нових оцінок: %1$d</string>
|
||||
<string name="grade_average">Середня оцінка: %1$.2f</string>
|
||||
<string name="grade_average_year">Підсумкова: %1$.2f</string>
|
||||
<string name="grade_points_sum">Бали: %s</string>
|
||||
<string name="grade_no_average">Середня оцінка відсутня</string>
|
||||
<string name="grade_summary_average_semester">Середня за семестр</string>
|
||||
<string name="grade_summary_average_year">Підсумкова середня оцінка</string>
|
||||
<string name="grade_summary_points">Всього балів</string>
|
||||
<string name="grade_summary_final_grade">Підсумкова оцінка</string>
|
||||
<string name="grade_summary_predicted_grade">Передбачувана оцінка</string>
|
||||
<string name="grade_summary_descriptive">Описова оцінка</string>
|
||||
<string name="grade_summary_calculated_average">Розрахована середня оцінка</string>
|
||||
<string name="grade_summary_calculated_average">Розрахована середня за семестр</string>
|
||||
<string name="grade_summary_calculated_average_annual">Розрахована підсумкова середня оцінка</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Як працює \"Розрахована середня оцінка\"?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Розрахована середня оцінка - це середнє арифметичне, обчислене з середніх оцінок з предметів. Це дозволяє дізнатися приблизну кінцеву середню оцінку. Вона розраховується спосібом, обраним користувачем у налаштуваннях програми. Рекомендується вибрати відповідний варіант, тому що кожна школа по різному розраховує середню оцінку. Крім того, якщо у вашій школі повідомляється середня оцінка з предметів на сторінці Vulcan, програма тільки завантажує ці оцінки і не розраховує їх самостійно. Це можна змінити шляхом примусового розрахунку середньоЇ оцінки в налаштуваннях програми.\n\n<b>Середні оцінки тільки за обраний семестр</b>:\n1. Розрахунок середньозваженого числа для кожного предмета в даному семестрі\n2. Сумування розрахованих числ\n3. Розрахунок середнього арифметичного з сумованих чисел\n\n<b>Середнє значення з обох семестрів</b>:\n1. Обчислення середньозваженого числа для кожного предмета у 1 та 2 семестрі\n2. Обчислення середнього арифметичного з розрахованих середньозважених числ за 1 та 2 семестри для кожного предмета.\n3. Додавання розрахованих середніх\n4. Розрахунок середнього арифметичного підсумованих середніх значень\n\n<b>Середнє значення оцінок за весь рік: </b>\n1. Розрахунок середньозваженого числа за рік для кожного предмета. Підсумковий середній показник у 1-му семестрі не має значення.\n2. Сумування розрахованих середніх\n3. Обчислення середнього арифметичного з суммованих середніх</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Як працює \"Підсумкова середня оцінка\"?</string>
|
||||
@ -194,6 +198,7 @@
|
||||
</plurals>
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Урок</string>
|
||||
<string name="timetable_additional_lesson">Додатковий урок</string>
|
||||
<string name="timetable_room">Аудиторія</string>
|
||||
<string name="timetable_group">Група</string>
|
||||
<string name="timetable_time">Години</string>
|
||||
@ -265,6 +270,12 @@
|
||||
<string name="additional_lessons_end_time_error">Час завершення має бути пізніше часу початку</string>
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Підсумок відвідуваності</string>
|
||||
<string name="attendance_calculator_button">Калькулятор відвідуваності</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> понад ціль</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">точно у цілі</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> під ціллю</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d відвідуваності</string>
|
||||
<string name="attendance_calculator_summary_values_empty">Немає жодних записаних відвідувань</string>
|
||||
<string name="attendance_absence_school">Відсутність зі шкільних причин</string>
|
||||
<string name="attendance_absence_excused">Відсутність з поважних причин</string>
|
||||
<string name="attendance_absence_unexcused">Відсутність без поважних причин</string>
|
||||
@ -731,9 +742,13 @@
|
||||
<string name="pref_view_grade_average_mode">Параметри розраховування середніх оцінок</string>
|
||||
<string name="pref_view_grade_average_force_calc">Примусово розраховувати середню оцінку через додаток</string>
|
||||
<string name="pref_view_present">Показувати присутність</string>
|
||||
<string name="pref_attendance_target">Цільова відвідуваність</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Показувати уроки без відвідувань</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Сортування калькулятора відвідування</string>
|
||||
<string name="pref_view_app_theme">Тема</string>
|
||||
<string name="pref_view_expand_grade">Розгортання оцінок</string>
|
||||
<string name="pref_view_timetable_show_groups">Показувати групи поруч з темами</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Показати додаткові уроки</string>
|
||||
<string name="pref_view_timetable_show_gaps">Показувати порожні плитки там, де немає уроків</string>
|
||||
<string name="pref_view_grade_statistics_list">Показувати діаграми в оцінках класу</string>
|
||||
<string name="pref_view_subjects_without_grades">Показати предмети без оцінок</string>
|
||||
@ -798,6 +813,8 @@
|
||||
<string name="pref_dashboard_appearance_header">Головна</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Видимість плиток</string>
|
||||
<string name="pref_attendance_appearance_view">Відвідуваність</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Калькулятор відвідуваності</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Налаштування</string>
|
||||
<string name="pref_timetable_appearance_view">Розклад</string>
|
||||
<string name="pref_grades_advanced_header">Оцінки</string>
|
||||
<string name="pref_counted_average_advanced_header">Розрахована середня оцінка</string>
|
||||
@ -849,7 +866,7 @@
|
||||
<string name="auth_button">Авторизовать</string>
|
||||
<string name="auth_success">Авторизація пройшла успішно</string>
|
||||
<string name="auth_title">Авторизувати</string>
|
||||
<string name="auth_description">Для роботи програми нам потрібно підтвердити вашу особу. Будь ласка, введіть число PESEL <b>%1$s</b> студента в поле нижче</string>
|
||||
<string name="auth_description">Шановні батьки,<br/><br/>Для авторизації та забезпечення безпеки даних просимо Вас ввести нижче PESEL номер учня <b>%1$s</b>. Ці дані необхідні для правильного призначення доступу та захисту персональних даних відповідно до чинного законодавства.<br/><br/>Після введення даних буде проведена перевірка, щоб переконатися, що доступ до системи VULCAN надається виключно уповноваженим особам. У разі виникнення будь-яких сумнівів або проблем, будь ласка, зв\'яжіться з адміністратором шкільного щоденника для з\'ясування ситуації.<br/><br/>Ми підтримуємо найвищі стандарти захисту персональних даних і гарантуємо, що вся надана інформація є безпечною. Додаток Wulkanowy не зберігає і не обробляє номер PESEL.<br/><br/>Нагадуємо, що надання повних і точних даних є обов\'язковим і необхідним для використання системи VULCAN.</string>
|
||||
<string name="auth_button_skip">Поки що пропустити</string>
|
||||
<!--Captcha-->
|
||||
<string name="captcha_dialog_title">Веб-сайт VULCAN потребує підтвердження</string>
|
||||
|
@ -2,6 +2,9 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
<string name="pref_default_startup">0</string>
|
||||
<bool name="pref_default_attendance_present">true</bool>
|
||||
<integer name="pref_default_attendance_target">50</integer>
|
||||
<string name="pref_default_attendance_calculator_sorting_mode">alphabetic</string>
|
||||
<bool name="pref_default_attendance_calculator_show_empty_subjects">false</bool>
|
||||
<string name="pref_default_grade_average_mode">only_one_semester</string>
|
||||
<bool name="pref_default_grade_average_force_calc">false</bool>
|
||||
<string name="pref_default_expand_grade_mode">one</string>
|
||||
@ -19,7 +22,8 @@
|
||||
<string name="pref_default_grade_modifier_plus">0.33</string>
|
||||
<string name="pref_default_grade_modifier_minus">0.33</string>
|
||||
<bool name="pref_default_fill_message_content">true</bool>
|
||||
<bool name="pref_default_timetable_show_groups">false</bool>
|
||||
<bool name="pref_default_timetable_show_groups">true</bool>
|
||||
<string name="pref_default_timetable_show_additional_lessons">below</string>
|
||||
<string name="pref_default_timetable_show_whole_class">no</string>
|
||||
<string name="pref_default_grade_sorting_mode">alphabetic</string>
|
||||
<string name="pref_default_timetable_show_gaps">between</string>
|
||||
|
@ -1,7 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
|
||||
<string name="pref_key_start_menu">default_menu_index</string>
|
||||
<string name="pref_key_attendance_calculator">attendance_calculator</string>
|
||||
<string name="pref_key_attendance_present">attendance_present</string>
|
||||
<string name="pref_key_attendance_target">attendance_target</string>
|
||||
<string name="pref_key_attendance_calculator_sorting_mode">attendance_calculator_sorting_mode</string>
|
||||
<string name="pref_key_attendance_calculator_show_empty_subjects">attendance_calculator_show_empty_subjects</string>
|
||||
<string name="pref_key_app_theme">app_theme</string>
|
||||
<string name="pref_key_dashboard_tiles">dashboard_tiles</string>
|
||||
<string name="pref_key_grade_color_scheme">grade_color_scheme</string>
|
||||
@ -27,6 +31,7 @@
|
||||
<string name="pref_key_grade_sorting_mode">grade_sorting_mode</string>
|
||||
<string name="pref_key_timetable_show_whole_class">show_whole_class_plan</string>
|
||||
<string name="pref_key_timetable_show_groups">show_groups_in_plan</string>
|
||||
<string name="pref_key_timetable_show_additional_lessons">show_additional_lessons</string>
|
||||
<string name="pref_key_timetable_show_gaps">timetable_show_gaps</string>
|
||||
<string name="pref_key_subjects_without_grades">subjects_without_grades</string>
|
||||
<string name="pref_key_optional_arithmetic_average">optional_arithmetic_average</string>
|
||||
|
@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="sort_alphabetically">Alphabetically</string>
|
||||
<string name="sort_by_date">By date</string>
|
||||
<string name="sort_by_average">By average</string>
|
||||
<string name="sort_by_attendance_percentage">By attendance percentage</string>
|
||||
<string name="sort_by_subject_attendance_balance">By subject attendance balance</string>
|
||||
|
||||
<string-array name="startup_tab_entries" translatable="false">
|
||||
<item>@string/dashboard_title</item>
|
||||
<item>@string/grade_title</item>
|
||||
@ -79,10 +86,21 @@
|
||||
<item>0.75</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="grade_sorting_mode_entries">
|
||||
<item>Alphabetically</item>
|
||||
<item>By date</item>
|
||||
<item>By average</item>
|
||||
<string-array name="attendance_calculator_sorting_mode_entries" translatable="false">
|
||||
<item>@string/sort_alphabetically</item>
|
||||
<item>@string/sort_by_attendance_percentage</item>
|
||||
<item>@string/sort_by_subject_attendance_balance</item>
|
||||
</string-array>
|
||||
<string-array name="attendance_calculator_sorting_mode_values" translatable="false">
|
||||
<item>alphabetic</item>
|
||||
<item>attendance_percentage</item>
|
||||
<item>lesson_balance</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="grade_sorting_mode_entries" translatable="false">
|
||||
<item>@string/sort_alphabetically</item>
|
||||
<item>@string/sort_by_date</item>
|
||||
<item>@string/sort_by_average</item>
|
||||
</string-array>
|
||||
<string-array name="grade_sorting_mode_values" translatable="false">
|
||||
<item>alphabetic</item>
|
||||
@ -134,6 +152,17 @@
|
||||
<item>before_and_between</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="timetable_show_additional_lessons_entries">
|
||||
<item>Don\'t show</item>
|
||||
<item>Show inline</item>
|
||||
<item>Show below regular lessons</item>
|
||||
</string-array>
|
||||
<string-array name="timetable_show_additional_lessons_values" translatable="false">
|
||||
<item>none</item>
|
||||
<item>inline</item>
|
||||
<item>below</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dashboard_tile_entries">
|
||||
<item>Lucky number</item>
|
||||
<item>Unread messages</item>
|
||||
|
@ -195,6 +195,7 @@
|
||||
|
||||
<!--Timetable-->
|
||||
<string name="timetable_lesson">Lesson</string>
|
||||
<string name="timetable_additional_lesson">Additional lesson</string>
|
||||
<string name="timetable_room">Room</string>
|
||||
<string name="timetable_group">Group</string>
|
||||
<string name="timetable_time">Hours</string>
|
||||
@ -262,6 +263,12 @@
|
||||
|
||||
<!--Attendance-->
|
||||
<string name="attendance_summary_button">Attendance summary</string>
|
||||
<string name="attendance_calculator_button">Attendance calculator</string>
|
||||
<string name="attendance_calculator_summary_balance_positive"><b>%1$d</b> over target</string>
|
||||
<string name="attendance_calculator_summary_balance_neutral">right on target</string>
|
||||
<string name="attendance_calculator_summary_balance_negative"><b>%1$d</b> under target</string>
|
||||
<string name="attendance_calculator_summary_values">%1$d/%2$d presences</string>
|
||||
<string name="attendance_calculator_summary_values_empty">No attendances recorded</string>
|
||||
<string name="attendance_absence_school">Absent for school reasons</string>
|
||||
<string name="attendance_absence_excused">Excused absence</string>
|
||||
<string name="attendance_absence_unexcused">Unexcused absence</string>
|
||||
@ -719,9 +726,13 @@
|
||||
<string name="pref_view_grade_average_mode">Calculated average options</string>
|
||||
<string name="pref_view_grade_average_force_calc">Force average calculation by app</string>
|
||||
<string name="pref_view_present">Show presence</string>
|
||||
<string name="pref_attendance_target">Attendance target</string>
|
||||
<string name="pref_attendance_calculator_show_empty_subjects">Show subjects without any attendances</string>
|
||||
<string name="pref_view_attendance_calculator_sorting_mode">Attendance calculator sorting</string>
|
||||
<string name="pref_view_app_theme">Theme</string>
|
||||
<string name="pref_view_expand_grade">Grades expanding</string>
|
||||
<string name="pref_view_timetable_show_groups">Show groups next to subjects</string>
|
||||
<string name="pref_view_timetable_show_additional_lessons">Show additional lessons</string>
|
||||
<string name="pref_view_timetable_show_gaps">Show empty tiles where there\'s no lesson</string>
|
||||
<string name="pref_view_grade_statistics_list">Show chart list in class grades</string>
|
||||
<string name="pref_view_subjects_without_grades">Show subjects without grades</string>
|
||||
@ -787,6 +798,8 @@
|
||||
<string name="pref_dashboard_appearance_header">Dashboard</string>
|
||||
<string name="pref_dashboard_appearance_tiles_title">Tiles visibility</string>
|
||||
<string name="pref_attendance_appearance_view">Attendance</string>
|
||||
<string name="pref_attendance_calculator_appearance_view">Attendance calculator</string>
|
||||
<string name="pref_attendance_calculator_appearance_settings_title">Settings</string>
|
||||
<string name="pref_timetable_appearance_view">Timetable</string>
|
||||
<string name="pref_grades_advanced_header">Grades</string>
|
||||
<string name="pref_counted_average_advanced_header">Calculated average</string>
|
||||
@ -847,7 +860,7 @@
|
||||
<string name="auth_button">Authorize</string>
|
||||
<string name="auth_success">Authorization completed successfully</string>
|
||||
<string name="auth_title">Authorization</string>
|
||||
<string name="auth_description">To operate the application, we need to confirm your identity. Please enter the student\'s PESEL <b>%1$s</b> in the field below</string>
|
||||
<string name="auth_description">Dear Parent,<br /><br />To authorize and ensure the security of data, we kindly ask you to enter below PESEL number of student <b>%1$s</b>. These details are essential for the proper assignment of access and protection of personal data in accordance with applicable regulations.<br /><br />After entering the data, it will be verified to ensure that access to the VULCAN system is granted exclusively to authorized individuals. Should you have any doubts or problems, please contact the school diary administrator to clarify the situation.<br /><br />We maintain the highest standards of personal data protection and ensure that all information provided is secure. Wulkanowy app does not store or process the PESEL number.<br /><br />We remind you that providing full and accurate data is mandatory and necessary for the use of the VULCAN system.</string>
|
||||
<string name="auth_button_skip">Skip for now</string>
|
||||
|
||||
|
||||
|
@ -86,6 +86,34 @@
|
||||
app:key="@string/pref_key_attendance_present"
|
||||
app:title="@string/pref_view_present" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_attendance_calculator"
|
||||
app:title="@string/pref_attendance_calculator_appearance_view">
|
||||
<SeekBarPreference
|
||||
android:layout="@layout/pref_target_attendance"
|
||||
android:max="99"
|
||||
app:defaultValue="@integer/pref_default_attendance_target"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_attendance_target"
|
||||
app:min="1"
|
||||
app:showSeekBarValue="true"
|
||||
app:title="@string/pref_attendance_target"
|
||||
app:updatesContinuously="true" />
|
||||
<ListPreference
|
||||
app:defaultValue="@string/pref_default_attendance_calculator_sorting_mode"
|
||||
app:entries="@array/attendance_calculator_sorting_mode_entries"
|
||||
app:entryValues="@array/attendance_calculator_sorting_mode_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_attendance_calculator_sorting_mode"
|
||||
app:title="@string/pref_view_attendance_calculator_sorting_mode"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="@bool/pref_default_attendance_calculator_show_empty_subjects"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_attendance_calculator_show_empty_subjects"
|
||||
app:title="@string/pref_attendance_calculator_show_empty_subjects" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory
|
||||
android:layout_height="wrap_content"
|
||||
app:iconSpaceReserved="false"
|
||||
@ -114,5 +142,13 @@
|
||||
app:key="@string/pref_key_timetable_show_gaps"
|
||||
app:title="@string/pref_view_timetable_show_gaps"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<ListPreference
|
||||
app:defaultValue="@string/pref_default_timetable_show_additional_lessons"
|
||||
app:entries="@array/timetable_show_additional_lessons_entries"
|
||||
app:entryValues="@array/timetable_show_additional_lessons_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:key="@string/pref_key_timetable_show_additional_lessons"
|
||||
app:title="@string/pref_view_timetable_show_additional_lessons"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
11
app/src/play/AndroidManifest.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<application tools:ignore="MissingApplicationIcon">
|
||||
<property
|
||||
android:name="android.adservices.AD_SERVICES_CONFIG"
|
||||
android:resource="@xml/gma_ad_services_config"
|
||||
tools:replace="android:resource" />
|
||||
</application>
|
||||
</manifest>
|
@ -1,42 +0,0 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "",
|
||||
"firebase_url": "",
|
||||
"project_id": "",
|
||||
"storage_bucket": ""
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:1091101852179:android:b558a25f65d088b1",
|
||||
"android_client_info": {
|
||||
"package_name": "io.github.wulkanowy"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": ""
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package io.github.wulkanowy.data
|
||||
|
||||
import io.mockk.*
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerifyOrder
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
@ -42,7 +46,6 @@ class ResourceTest {
|
||||
// first
|
||||
networkBoundResource(
|
||||
isResultEmpty = { false },
|
||||
showSavedOnLoading = false,
|
||||
query = { repo.query() },
|
||||
fetch = {
|
||||
val data = repo.fetch()
|
||||
@ -57,7 +60,6 @@ class ResourceTest {
|
||||
// second
|
||||
networkBoundResource(
|
||||
isResultEmpty = { false },
|
||||
showSavedOnLoading = false,
|
||||
query = { repo.query() },
|
||||
fetch = {
|
||||
val data = repo.fetch()
|
||||
@ -124,7 +126,6 @@ class ResourceTest {
|
||||
networkBoundResource(
|
||||
isResultEmpty = { false },
|
||||
mutex = saveResultMutex,
|
||||
showSavedOnLoading = false,
|
||||
query = { repo.query() },
|
||||
fetch = {
|
||||
val data = repo.fetch()
|
||||
@ -143,7 +144,6 @@ class ResourceTest {
|
||||
networkBoundResource(
|
||||
isResultEmpty = { false },
|
||||
mutex = saveResultMutex,
|
||||
showSavedOnLoading = false,
|
||||
query = { repo.query() },
|
||||
fetch = {
|
||||
val data = repo.fetch()
|
||||
|
@ -8,6 +8,7 @@ import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.toFirstResult
|
||||
import io.github.wulkanowy.getStudentEntity
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppWidgetUpdater
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
@ -32,6 +33,9 @@ class LuckyNumberRemoteTest {
|
||||
@MockK
|
||||
private lateinit var luckyNumberDb: LuckyNumberDao
|
||||
|
||||
@MockK(relaxed = true)
|
||||
private lateinit var appWidgetUpdater: AppWidgetUpdater
|
||||
|
||||
private val student = getStudentEntity()
|
||||
|
||||
private lateinit var luckyNumberRepository: LuckyNumberRepository
|
||||
@ -44,7 +48,11 @@ class LuckyNumberRemoteTest {
|
||||
fun setUp() {
|
||||
MockKAnnotations.init(this)
|
||||
|
||||
luckyNumberRepository = LuckyNumberRepository(luckyNumberDb, wulkanowySdkFactory)
|
||||
luckyNumberRepository = LuckyNumberRepository(
|
||||
luckyNumberDb = luckyNumberDb,
|
||||
wulkanowySdkFactory = wulkanowySdkFactory,
|
||||
appWidgetUpdater = appWidgetUpdater,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|