mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-02-23 07:34:44 +01:00
Merge branch 'release/1.7.0'
This commit is contained in:
commit
c293c76398
20
.github/workflows/deploy-store.yml
vendored
20
.github/workflows/deploy-store.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Deploy to app stores
|
||||
name: Deploy release
|
||||
|
||||
on:
|
||||
release:
|
||||
@ -7,16 +7,17 @@ on:
|
||||
jobs:
|
||||
|
||||
deploy-google-play:
|
||||
name: Deploy to google play
|
||||
name: Google Play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: google-play
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@ -41,16 +42,17 @@ jobs:
|
||||
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
||||
|
||||
deploy-app-gallery:
|
||||
name: Deploy to AppGallery
|
||||
name: AppGallery
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-gallery
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
20
.github/workflows/deploy-test.yml
vendored
20
.github/workflows/deploy-test.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Deploy to app tests
|
||||
name: Deploy DEV
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -18,11 +18,12 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
environment: app-center
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@ -66,7 +67,7 @@ jobs:
|
||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assembleFdroidDebug --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
|
||||
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
||||
@ -87,11 +88,12 @@ jobs:
|
||||
environment: app-distribution
|
||||
if: github.event_name != 'pull_request_target'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@ -131,7 +133,7 @@ jobs:
|
||||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
|
||||
path: app/build/outputs/apk/play/debug/app-play-debug.apk
|
||||
|
66
.github/workflows/test.yml
vendored
66
.github/workflows/test.yml
vendored
@ -8,18 +8,20 @@ on:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
|
||||
tests-fdroid:
|
||||
name: F-Droid
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v1
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
@ -29,6 +31,58 @@ jobs:
|
||||
run: |
|
||||
./gradlew testFdroidDebugUnitTest --stacktrace
|
||||
./gradlew jacocoTestReport --stacktrace
|
||||
- uses: codecov/codecov-action@v1
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: unit
|
||||
|
||||
tests-play:
|
||||
name: Play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- name: Unit tests
|
||||
run: |
|
||||
./gradlew testPlayDebugUnitTest --stacktrace
|
||||
./gradlew jacocoTestReport --stacktrace
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: unit
|
||||
|
||||
tests-hms:
|
||||
name: HMS
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*') }}
|
||||
- name: Unit tests
|
||||
run: |
|
||||
./gradlew testHmsDebugUnitTest --stacktrace
|
||||
./gradlew jacocoTestReport --stacktrace
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
flags: unit
|
||||
|
@ -51,7 +51,7 @@ Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGal
|
||||
alt="Explore it on AppGallery"
|
||||
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||
|
||||
Sie können auch ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
|
||||
Sie können auch eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
|
||||
|
||||
## Gebaut mit
|
||||
|
||||
|
@ -15,33 +15,35 @@ apply from: 'sonarqube.gradle'
|
||||
apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
namespace 'io.github.wulkanowy'
|
||||
compileSdkVersion 32
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 108
|
||||
versionName "1.6.4"
|
||||
targetSdkVersion 32
|
||||
versionCode 109
|
||||
versionName "1.7.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
|
||||
manifestPlaceholders = [
|
||||
firebase_enabled: project.hasProperty("enableFirebase"),
|
||||
admob_project_id: ""
|
||||
firebase_enabled: project.hasProperty("enableFirebase"),
|
||||
admob_project_id: ""
|
||||
]
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments += [
|
||||
"room.schemaLocation": "$projectDir/schemas".toString(),
|
||||
"room.incremental" : "true"
|
||||
"room.schemaLocation": "$projectDir/schemas".toString(),
|
||||
"room.incremental" : "true"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
|
||||
|
||||
if (System.env.SET_BUILD_TIMESTAMP) {
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||
@ -94,10 +96,12 @@ android {
|
||||
play {
|
||||
dimension "platform"
|
||||
manifestPlaceholders = [
|
||||
install_channel : "Google Play",
|
||||
admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
|
||||
install_channel : "Google Play",
|
||||
admob_project_id: System.getenv("ADMOB_PROJECT_ID") ?: "ca-app-pub-3940256099942544~3347511713"
|
||||
]
|
||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
|
||||
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "\"${System.getenv("DASHBOARD_TILE_AD_ID") ?: "ca-app-pub-3940256099942544/6300978111"}\""
|
||||
|
||||
}
|
||||
|
||||
fdroid {
|
||||
@ -122,6 +126,8 @@ android {
|
||||
|
||||
testOptions.unitTests {
|
||||
includeAndroidResources = true
|
||||
// workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750
|
||||
all { jvmArgs '-noverify' }
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
@ -136,8 +142,10 @@ android {
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'META-INF/library_release.kotlin_module'
|
||||
exclude 'META-INF/library-core_release.kotlin_module'
|
||||
resources {
|
||||
excludes += ['META-INF/library_release.kotlin_module',
|
||||
'META-INF/library-core_release.kotlin_module']
|
||||
}
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
@ -153,8 +161,8 @@ play {
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.50d
|
||||
updatePriority = 3
|
||||
userFraction = 0.05d
|
||||
updatePriority = 5
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
@ -171,34 +179,34 @@ huaweiPublish {
|
||||
ext {
|
||||
work_manager = "2.7.1"
|
||||
android_hilt = "1.0.0"
|
||||
room = "2.4.2"
|
||||
room = "2.4.3"
|
||||
chucker = "3.5.2"
|
||||
mockk = "1.12.4"
|
||||
coroutines = "1.6.1"
|
||||
mockk = "1.12.5"
|
||||
coroutines = "1.6.4"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.6.4"
|
||||
implementation "io.github.wulkanowy:sdk:1.7.0"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
|
||||
implementation "androidx.core:core-ktx:1.7.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-beta02'
|
||||
implementation "androidx.activity:activity-ktx:1.4.0"
|
||||
implementation "androidx.appcompat:appcompat:1.4.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.core:core-ktx:1.8.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation "androidx.activity:activity-ktx:1.5.1"
|
||||
implementation "androidx.appcompat:appcompat:1.5.0"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.2"
|
||||
implementation "androidx.annotation:annotation:1.4.0"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
implementation "com.google.android.material:material:1.5.0"
|
||||
implementation "com.google.android.material:material:1.6.1"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation 'com.github.lopspower:CircularImageView:4.2.0'
|
||||
@ -206,7 +214,7 @@ dependencies {
|
||||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
@ -222,27 +230,27 @@ dependencies {
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
|
||||
|
||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||
implementation "io.coil-kt:coil:2.0.0"
|
||||
implementation "io.coil-kt:coil:2.2.0"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.7.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:30.0.1')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:30.3.2')
|
||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||
playImplementation 'com.google.android.play:core:1.10.3'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:21.1.0'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.5.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.6.200'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
|
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json
Normal file
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/49.json
Normal file
File diff suppressed because it is too large
Load Diff
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json
Normal file
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/50.json
Normal file
File diff suppressed because it is too large
Load Diff
2409
app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json
Normal file
2409
app/schemas/io.github.wulkanowy.data.db.AppDatabase/51.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,33 +1,92 @@
|
||||
{
|
||||
"agcgw":{
|
||||
"backurl":"connect-dre.dbankcloud.cn",
|
||||
"url":"connect-dre.hispace.hicloud.com"
|
||||
},
|
||||
"client":{
|
||||
"cp_id":"890048000024105546",
|
||||
"product_id":"",
|
||||
"client_id":"",
|
||||
"client_secret":"",
|
||||
"app_id":"101440411",
|
||||
"package_name":"io.github.wulkanowy.dev",
|
||||
"api_key":""
|
||||
},
|
||||
"service":{
|
||||
"analytics":{
|
||||
"collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
|
||||
"resource_id":"p1",
|
||||
"channel_id":""
|
||||
},
|
||||
"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":"https://ops-dre.agcstorage.link"
|
||||
},
|
||||
"ml":{
|
||||
"mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
|
||||
}
|
||||
},
|
||||
"region":"DE",
|
||||
"configuration_version":"1.0"
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
28
app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt
Normal file
28
app/src/fdroid/java/io/github/wulkanowy/utils/AdsHelper.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("unused")
|
||||
class AdsHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) {
|
||||
|
||||
fun initialize() {
|
||||
preferencesRepository.isAdsEnabled = false
|
||||
preferencesRepository.isAgreeToProcessData = false
|
||||
preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
|
||||
}
|
||||
|
||||
@Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
|
||||
suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
|
||||
throw IllegalStateException("Can't get ad banner (F-droid)")
|
||||
}
|
||||
}
|
||||
|
||||
data class AdBanner(val view: View)
|
28
app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt
Normal file
28
app/src/hms/java/io/github/wulkanowy/utils/AdsHelper.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package io.github.wulkanowy.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("unused")
|
||||
class AdsHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val preferencesRepository: PreferencesRepository
|
||||
) {
|
||||
|
||||
fun initialize() {
|
||||
preferencesRepository.isAdsEnabled = false
|
||||
preferencesRepository.isAgreeToProcessData = false
|
||||
preferencesRepository.selectedDashboardTiles -= DashboardItem.Tile.ADS
|
||||
}
|
||||
|
||||
@Suppress("RedundantSuspendModifier", "UNUSED_PARAMETER")
|
||||
suspend fun getDashboardTileAdBanner(width: Int): AdBanner {
|
||||
throw IllegalStateException("Can't get ad banner (HMS)")
|
||||
}
|
||||
}
|
||||
|
||||
data class AdBanner(val view: View)
|
@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.github.wulkanowy"
|
||||
android:installLocation="internalOnly">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
@ -31,10 +31,14 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
||||
@Inject
|
||||
lateinit var analyticsHelper: AnalyticsHelper
|
||||
|
||||
@Inject
|
||||
lateinit var adsHelper: AdsHelper
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
initializeAppLanguage()
|
||||
themeManager.applyDefaultTheme()
|
||||
adsHelper.initialize()
|
||||
initLogging()
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
@ -110,7 +109,6 @@ internal class DataModule {
|
||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
|
||||
@ -197,7 +195,7 @@ internal class DataModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
|
||||
fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -30,7 +30,7 @@ import javax.inject.Singleton
|
||||
Subject::class,
|
||||
LuckyNumber::class,
|
||||
CompletedLesson::class,
|
||||
ReportingUnit::class,
|
||||
Mailbox::class,
|
||||
Recipient::class,
|
||||
MobileDevice::class,
|
||||
Teacher::class,
|
||||
@ -55,7 +55,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 48
|
||||
const val VERSION_SCHEMA = 51
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -102,6 +102,9 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration43(),
|
||||
Migration44(),
|
||||
Migration46(),
|
||||
Migration49(),
|
||||
Migration50(),
|
||||
Migration51(),
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
@ -152,7 +155,7 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
abstract val completedLessonsDao: CompletedLessonsDao
|
||||
|
||||
abstract val reportingUnitDao: ReportingUnitDao
|
||||
abstract val mailboxDao: MailboxDao
|
||||
|
||||
abstract val recipientDao: RecipientDao
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface MailboxDao : BaseDao<Mailbox> {
|
||||
|
||||
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
|
||||
suspend fun loadAll(userLoginId: Int): List<Mailbox>
|
||||
|
||||
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId AND studentName = :studentName ")
|
||||
suspend fun load(userLoginId: Int, studentName: String): Mailbox?
|
||||
}
|
@ -11,9 +11,9 @@ import kotlinx.coroutines.flow.Flow
|
||||
interface MessagesDao : BaseDao<Message> {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
|
||||
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment?>
|
||||
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
|
||||
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
|
||||
|
||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
|
||||
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
||||
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
||||
}
|
||||
|
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
||||
@Dao
|
||||
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
||||
|
||||
@Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC")
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -9,6 +10,6 @@ import javax.inject.Singleton
|
||||
@Dao
|
||||
interface RecipientDao : BaseDao<Recipient> {
|
||||
|
||||
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND unit_id = :unitId AND role = :role")
|
||||
suspend fun loadAll(studentId: Int, unitId: Int, role: Int): List<Recipient>
|
||||
@Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey")
|
||||
suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List<Recipient>
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface ReportingUnitDao : BaseDao<ReportingUnit> {
|
||||
|
||||
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
|
||||
suspend fun load(studentId: Int): List<ReportingUnit>
|
||||
|
||||
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
|
||||
suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit?
|
||||
}
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>>
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "Mailboxes")
|
||||
data class Mailbox(
|
||||
|
||||
@PrimaryKey
|
||||
val globalKey: String,
|
||||
val fullName: String,
|
||||
val userName: String,
|
||||
val userLoginId: Int,
|
||||
val studentName: String,
|
||||
val schoolNameShort: String,
|
||||
val type: MailboxType,
|
||||
)
|
||||
|
||||
enum class MailboxType {
|
||||
STUDENT,
|
||||
PARENT,
|
||||
GUARDIAN,
|
||||
EMPLOYEE,
|
||||
UNKNOWN,
|
||||
}
|
@ -9,23 +9,16 @@ import java.time.Instant
|
||||
@Entity(tableName = "Messages")
|
||||
data class Message(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Long,
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
@ColumnInfo(name = "mailbox_key")
|
||||
val mailboxKey: String,
|
||||
|
||||
@ColumnInfo(name = "message_id")
|
||||
val messageId: Int,
|
||||
|
||||
@ColumnInfo(name = "sender_name")
|
||||
val sender: String,
|
||||
|
||||
@ColumnInfo(name = "sender_id")
|
||||
val senderId: Int,
|
||||
|
||||
@ColumnInfo(name = "recipient_name")
|
||||
val recipient: String,
|
||||
val correspondents: String,
|
||||
|
||||
val subject: String,
|
||||
|
||||
@ -36,8 +29,6 @@ data class Message(
|
||||
|
||||
var unread: Boolean,
|
||||
|
||||
val removed: Boolean,
|
||||
|
||||
@ColumnInfo(name = "has_attachments")
|
||||
val hasAttachments: Boolean
|
||||
) : Serializable {
|
||||
@ -48,11 +39,7 @@ data class Message(
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
|
||||
@ColumnInfo(name = "unread_by")
|
||||
var unreadBy: Int = 0
|
||||
|
||||
@ColumnInfo(name = "read_by")
|
||||
var readBy: Int = 0
|
||||
|
||||
var content: String = ""
|
||||
var sender: String? = null
|
||||
var recipients: String? = null
|
||||
}
|
||||
|
@ -12,11 +12,8 @@ data class MessageAttachment(
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
||||
@ColumnInfo(name = "message_id")
|
||||
val messageId: Int,
|
||||
|
||||
@ColumnInfo(name = "one_drive_id")
|
||||
val oneDriveId: String,
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
|
||||
@ColumnInfo(name = "url")
|
||||
val url: String,
|
||||
|
@ -7,6 +7,6 @@ data class MessageWithAttachment(
|
||||
@Embedded
|
||||
val message: Message,
|
||||
|
||||
@Relation(parentColumn = "message_id", entityColumn = "message_id")
|
||||
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
|
||||
val attachments: List<MessageAttachment>
|
||||
)
|
||||
|
@ -9,7 +9,7 @@ import java.time.Instant
|
||||
@Entity(tableName = "MobileDevices")
|
||||
data class MobileDevice(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
@ColumnInfo(name = "device_id")
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
@ -8,32 +7,16 @@ import java.io.Serializable
|
||||
@kotlinx.serialization.Serializable
|
||||
@Entity(tableName = "Recipients")
|
||||
data class Recipient(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: String,
|
||||
|
||||
val name: String,
|
||||
|
||||
@ColumnInfo(name = "real_name")
|
||||
val realName: String,
|
||||
|
||||
@ColumnInfo(name = "login_id")
|
||||
val loginId: Int,
|
||||
|
||||
@ColumnInfo(name = "unit_id")
|
||||
val unitId: Int,
|
||||
|
||||
val role: Int,
|
||||
|
||||
val hash: String
|
||||
|
||||
val mailboxGlobalKey: String,
|
||||
val studentMailboxGlobalKey: String,
|
||||
val fullName: String,
|
||||
val userName: String,
|
||||
val schoolShortName: String,
|
||||
val type: MailboxType,
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
override fun toString() = name
|
||||
override fun toString() = userName
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
||||
@Entity(tableName = "ReportingUnits")
|
||||
data class ReportingUnit(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "real_id")
|
||||
val unitId: Int,
|
||||
|
||||
@ColumnInfo(name = "short")
|
||||
val shortName: String,
|
||||
|
||||
@ColumnInfo(name = "sender_id")
|
||||
val senderId: Int,
|
||||
|
||||
@ColumnInfo(name = "sender_name")
|
||||
val senderName: String,
|
||||
|
||||
val roles: List<Int>
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
}
|
@ -9,8 +9,8 @@ import java.time.LocalDate
|
||||
@Entity(tableName = "SchoolAnnouncements")
|
||||
data class SchoolAnnouncement(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration49 : Migration(48, 49) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements")
|
||||
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` (
|
||||
`user_login_id` INTEGER NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`subject` TEXT NOT NULL,
|
||||
`content` TEXT NOT NULL,
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`is_notified` INTEGER NOT NULL)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration50 : Migration(49, 50) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS MobileDevices")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `MobileDevices` (
|
||||
`user_login_id` INTEGER NOT NULL,
|
||||
`device_id` INTEGER NOT NULL,
|
||||
`name` TEXT NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration51 : Migration(50, 51) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
createMailboxTable(database)
|
||||
recreateMessagesTable(database)
|
||||
recreateMessageAttachmentsTable(database)
|
||||
recreateRecipientsTable(database)
|
||||
deleteReportingUnitTable(database)
|
||||
}
|
||||
|
||||
private fun createMailboxTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS Mailboxes")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `Mailboxes` (
|
||||
`globalKey` TEXT NOT NULL,
|
||||
`fullName` TEXT NOT NULL,
|
||||
`userName` TEXT NOT NULL,
|
||||
`userLoginId` INTEGER NOT NULL,
|
||||
`studentName` TEXT NOT NULL,
|
||||
`schoolNameShort` TEXT NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
PRIMARY KEY(`globalKey`)
|
||||
)""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS Messages")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `Messages` (
|
||||
`message_global_key` TEXT NOT NULL,
|
||||
`mailbox_key` TEXT NOT NULL,
|
||||
`message_id` INTEGER NOT NULL,
|
||||
`correspondents` TEXT NOT NULL,
|
||||
`subject` TEXT NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`folder_id` INTEGER NOT NULL,
|
||||
`unread` INTEGER NOT NULL,
|
||||
`has_attachments` INTEGER NOT NULL,
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`is_notified` INTEGER NOT NULL,
|
||||
`content` TEXT NOT NULL,
|
||||
`sender` TEXT, `recipients` TEXT
|
||||
)""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS MessageAttachments")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `MessageAttachments` (
|
||||
`real_id` INTEGER NOT NULL,
|
||||
`message_global_key` TEXT NOT NULL,
|
||||
`url` TEXT NOT NULL,
|
||||
`filename` TEXT NOT NULL,
|
||||
PRIMARY KEY(`real_id`)
|
||||
)""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun recreateRecipientsTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS Recipients")
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `Recipients` (
|
||||
`mailboxGlobalKey` TEXT NOT NULL,
|
||||
`studentMailboxGlobalKey` TEXT NOT NULL,
|
||||
`fullName` TEXT NOT NULL,
|
||||
`userName` TEXT NOT NULL,
|
||||
`schoolShortName` TEXT NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
|
||||
)""".trimIndent()
|
||||
)
|
||||
}
|
||||
|
||||
private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DROP TABLE IF EXISTS ReportingUnits")
|
||||
}
|
||||
}
|
@ -2,9 +2,10 @@ package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class GradeSortingMode(val value: String) {
|
||||
ALPHABETIC("alphabetic"),
|
||||
DATE("date");
|
||||
DATE("date"),
|
||||
AVERAGE("average");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
|
||||
|
||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
studentId = student.userLoginId,
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
|
@ -0,0 +1,18 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.sdk.pojo.Mailbox as SdkMailbox
|
||||
|
||||
fun List<SdkMailbox>.mapToEntities(student: Student) = map {
|
||||
Mailbox(
|
||||
globalKey = it.globalKey,
|
||||
fullName = it.fullName,
|
||||
userName = it.userName,
|
||||
userLoginId = student.userLoginId,
|
||||
studentName = it.studentName,
|
||||
schoolNameShort = it.schoolNameShort,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
)
|
||||
}
|
@ -1,40 +1,31 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import java.time.Instant
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.sdk.pojo.MailboxType
|
||||
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
|
||||
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
|
||||
fun List<SdkMessage>.mapToEntities(student: Student) = map {
|
||||
fun List<SdkMessage>.mapToEntities(mailbox: Mailbox) = map {
|
||||
Message(
|
||||
studentId = student.id,
|
||||
realId = it.id ?: 0,
|
||||
messageId = it.messageId ?: 0,
|
||||
sender = it.sender?.name.orEmpty(),
|
||||
senderId = it.sender?.loginId ?: 0,
|
||||
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
|
||||
messageGlobalKey = it.globalKey,
|
||||
mailboxKey = mailbox.globalKey,
|
||||
messageId = it.id,
|
||||
correspondents = it.correspondents,
|
||||
subject = it.subject.trim(),
|
||||
date = it.dateZoned?.toInstant() ?: Instant.now(),
|
||||
date = it.dateZoned.toInstant(),
|
||||
folderId = it.folderId,
|
||||
unread = it.unread ?: false,
|
||||
removed = it.removed,
|
||||
unread = it.unread,
|
||||
hasAttachments = it.hasAttachments
|
||||
).apply {
|
||||
content = it.content.orEmpty()
|
||||
unreadBy = it.unreadBy ?: 0
|
||||
readBy = it.readBy ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
fun List<SdkMessageAttachment>.mapToEntities() = map {
|
||||
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
||||
MessageAttachment(
|
||||
realId = it.id,
|
||||
messageId = it.messageId,
|
||||
oneDriveId = it.oneDriveId,
|
||||
messageGlobalKey = messageGlobalKey,
|
||||
realId = it.url.hashCode(),
|
||||
url = it.url,
|
||||
filename = it.filename
|
||||
)
|
||||
@ -42,12 +33,11 @@ fun List<SdkMessageAttachment>.mapToEntities() = map {
|
||||
|
||||
fun List<Recipient>.mapFromEntities() = map {
|
||||
SdkRecipient(
|
||||
id = it.realId,
|
||||
name = it.realName,
|
||||
loginId = it.loginId,
|
||||
reportingUnitId = it.unitId,
|
||||
role = it.role,
|
||||
hash = it.hash,
|
||||
shortName = it.name
|
||||
fullName = it.fullName,
|
||||
userName = it.userName,
|
||||
studentName = it.userName,
|
||||
mailboxGlobalKey = it.mailboxGlobalKey,
|
||||
schoolNameShort = it.schoolShortName,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
)
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
|
||||
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
||||
|
||||
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
|
||||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||
MobileDevice(
|
||||
userLoginId = semester.studentId,
|
||||
userLoginId = student.userLoginId,
|
||||
date = it.createDateZoned.toInstant(),
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
|
@ -1,17 +1,16 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
||||
|
||||
fun List<SdkRecipient>.mapToEntities(userLoginId: Int) = map {
|
||||
fun List<SdkRecipient>.mapToEntities(studentMailboxGlobalKey: String) = map {
|
||||
Recipient(
|
||||
studentId = userLoginId,
|
||||
realId = it.id,
|
||||
realName = it.name,
|
||||
name = it.shortName,
|
||||
hash = it.hash,
|
||||
loginId = it.loginId,
|
||||
role = it.role,
|
||||
unitId = it.reportingUnitId ?: 0
|
||||
mailboxGlobalKey = it.mailboxGlobalKey,
|
||||
fullName = it.fullName,
|
||||
userName = it.userName,
|
||||
studentMailboxGlobalKey = studentMailboxGlobalKey,
|
||||
schoolShortName = it.schoolNameShort,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
)
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.sdk.pojo.ReportingUnit as SdkReportingUnit
|
||||
|
||||
fun List<SdkReportingUnit>.mapToEntities(student: Student) = map {
|
||||
ReportingUnit(
|
||||
studentId = student.id.toInt(),
|
||||
unitId = it.id,
|
||||
roles = it.roles,
|
||||
senderId = it.senderId,
|
||||
senderName = it.senderName,
|
||||
shortName = it.short
|
||||
)
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MailboxRepository @Inject constructor(
|
||||
private val mailboxDao: MailboxDao,
|
||||
private val sdk: Sdk,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
private val cacheKey = "mailboxes"
|
||||
|
||||
suspend fun refreshMailboxes(student: Student) {
|
||||
val new = sdk.init(student).getMailboxes().mapToEntities(student)
|
||||
val old = mailboxDao.loadAll(student.userLoginId)
|
||||
|
||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||
mailboxDao.insertAll(new uniqueSubtract old)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
|
||||
suspend fun getMailbox(student: Student): Mailbox {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
val mailbox = mailboxDao.load(student.userLoginId, student.studentName)
|
||||
|
||||
return if (isExpired || mailbox == null) {
|
||||
refreshMailboxes(student)
|
||||
val newMailbox = mailboxDao.load(student.userLoginId, student.studentName)
|
||||
|
||||
requireNotNull(newMailbox) {
|
||||
"Mailbox for ${student.userName} - ${student.studentName} not found!"
|
||||
}
|
||||
|
||||
newMailbox
|
||||
} else mailbox
|
||||
}
|
||||
}
|
@ -10,24 +10,24 @@ import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
|
||||
import io.github.wulkanowy.data.mappers.mapFromEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.MessageDraft
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Folder
|
||||
import io.github.wulkanowy.sdk.pojo.SentMessage
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import timber.log.Timber
|
||||
import java.time.LocalDateTime.now
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@ -49,7 +49,7 @@ class MessageRepository @Inject constructor(
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getMessages(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
mailbox: Mailbox,
|
||||
folder: MessageFolder,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
@ -62,42 +62,20 @@ class MessageRepository @Inject constructor(
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
|
||||
query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now())
|
||||
.mapToEntities(student)
|
||||
sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
messagesDb.deleteAll(old uniqueSubtract new)
|
||||
messagesDb.insertAll((new uniqueSubtract old).onEach {
|
||||
it.isNotified = !notify
|
||||
})
|
||||
messagesDb.updateAll(getMessagesWithReadByChange(old, new, !notify))
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
|
||||
}
|
||||
)
|
||||
|
||||
private fun getMessagesWithReadByChange(
|
||||
old: List<Message>,
|
||||
new: List<Message>,
|
||||
setNotified: Boolean
|
||||
): List<Message> {
|
||||
val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) }
|
||||
val newMeta = new.map { Triple(it, it.readBy, it.unreadBy) }
|
||||
|
||||
val updatedItems = newMeta uniqueSubtract oldMeta
|
||||
|
||||
return updatedItems.map {
|
||||
val oldItem = old.find { item -> item.messageId == it.first.messageId }
|
||||
it.first.apply {
|
||||
id = oldItem?.id ?: 0
|
||||
isNotified = oldItem?.isNotified ?: setNotified
|
||||
content = oldItem?.content.orEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessage(
|
||||
student: Student,
|
||||
message: Message,
|
||||
@ -106,34 +84,34 @@ class MessageRepository @Inject constructor(
|
||||
isResultEmpty = { it?.message?.content.isNullOrBlank() },
|
||||
shouldFetch = {
|
||||
checkNotNull(it) { "This message no longer exist!" }
|
||||
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||
it.message.unread || it.message.content.isEmpty()
|
||||
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
|
||||
it.message.unread || it.message.content.isBlank()
|
||||
},
|
||||
query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) },
|
||||
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageId = it!!.message.messageId,
|
||||
folderId = message.folderId,
|
||||
read = markAsRead,
|
||||
id = message.realId
|
||||
).let { details ->
|
||||
details.content to details.attachments.mapToEntities()
|
||||
}
|
||||
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey)
|
||||
},
|
||||
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
||||
saveFetchResult = { old, new ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
messagesDb.updateAll(listOf(old.message.apply {
|
||||
id = old.message.id
|
||||
unread = !markAsRead
|
||||
content = content.ifBlank { downloadedMessage }
|
||||
}))
|
||||
messageAttachmentDao.insertAttachments(attachments)
|
||||
messagesDb.updateAll(
|
||||
listOf(old.message.apply {
|
||||
id = message.id
|
||||
unread = !markAsRead
|
||||
sender = new.sender
|
||||
recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów"
|
||||
content = content.ifBlank { new.content }
|
||||
})
|
||||
)
|
||||
messageAttachmentDao.insertAttachments(
|
||||
items = new.attachments.mapToEntities(message.messageGlobalKey),
|
||||
)
|
||||
|
||||
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
|
||||
}
|
||||
)
|
||||
|
||||
fun getMessagesFromDatabase(student: Student): Flow<List<Message>> {
|
||||
return messagesDb.loadAll(student.id.toInt(), RECEIVED.id)
|
||||
fun getMessagesFromDatabase(mailbox: Mailbox): Flow<List<Message>> {
|
||||
return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
|
||||
}
|
||||
|
||||
suspend fun updateMessages(messages: List<Message>) {
|
||||
@ -145,32 +123,48 @@ class MessageRepository @Inject constructor(
|
||||
subject: String,
|
||||
content: String,
|
||||
recipients: List<Recipient>,
|
||||
): SentMessage = sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities()
|
||||
)
|
||||
mailboxId: String,
|
||||
) {
|
||||
sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities(),
|
||||
mailboxId = mailboxId,
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||
val folderId = messages.first().folderId
|
||||
val isDeleted = sdk.init(student)
|
||||
.deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
|
||||
suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List<Message>) {
|
||||
val firstMessage = messages.first()
|
||||
sdk.init(student).deleteMessages(
|
||||
messages = messages.map { it.messageGlobalKey },
|
||||
removeForever = firstMessage.folderId == TRASHED.id,
|
||||
)
|
||||
|
||||
if (folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||
if (firstMessage.folderId != TRASHED.id) {
|
||||
val deletedMessages = messages.map {
|
||||
it.copy(folderId = MessageFolder.TRASHED.id)
|
||||
it.copy(folderId = TRASHED.id)
|
||||
.apply {
|
||||
id = it.id
|
||||
content = it.content
|
||||
sender = it.sender
|
||||
recipients = it.recipients
|
||||
}
|
||||
}
|
||||
|
||||
messagesDb.updateAll(deletedMessages)
|
||||
} else messagesDb.deleteAll(messages)
|
||||
|
||||
getMessages(
|
||||
student = student,
|
||||
mailbox = mailbox,
|
||||
folder = TRASHED,
|
||||
forceRefresh = true,
|
||||
).first()
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(student: Student, message: Message) =
|
||||
deleteMessages(student, listOf(message))
|
||||
suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) {
|
||||
deleteMessages(student, mailbox, listOf(message))
|
||||
}
|
||||
|
||||
var draftMessage: MessageDraft?
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
||||
|
@ -39,12 +39,12 @@ class MobileDeviceRepository @Inject constructor(
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
||||
query = { mobileDb.loadAll(student.userLoginId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getRegisteredDevices()
|
||||
.mapToEntities(semester)
|
||||
.mapToEntities(student)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
mobileDb.deleteAll(old uniqueSubtract new)
|
||||
|
@ -222,19 +222,31 @@ class PreferencesRepository @Inject constructor(
|
||||
get() = selectedDashboardTilesPreference.asFlow()
|
||||
.map { set ->
|
||||
set.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.plus(
|
||||
listOfNotNull(
|
||||
DashboardItem.Tile.ACCOUNT,
|
||||
DashboardItem.Tile.ADMIN_MESSAGE,
|
||||
DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
|
||||
)
|
||||
)
|
||||
.toSet()
|
||||
}
|
||||
|
||||
var selectedDashboardTiles: Set<DashboardItem.Tile>
|
||||
get() = selectedDashboardTilesPreference.get()
|
||||
.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.plus(
|
||||
listOfNotNull(
|
||||
DashboardItem.Tile.ACCOUNT,
|
||||
DashboardItem.Tile.ADMIN_MESSAGE,
|
||||
DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
|
||||
)
|
||||
)
|
||||
.toSet()
|
||||
set(value) {
|
||||
val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT }
|
||||
val filteredValue = value.filterNot {
|
||||
it == DashboardItem.Tile.ACCOUNT || it == DashboardItem.Tile.ADMIN_MESSAGE
|
||||
}
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
|
||||
@ -271,7 +283,38 @@ class PreferencesRepository @Inject constructor(
|
||||
|
||||
var isAppReviewDone: Boolean
|
||||
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
|
||||
set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply()
|
||||
set(value) = sharedPref.edit { putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value) }
|
||||
|
||||
var isAppSupportShown: Boolean
|
||||
get() = sharedPref.getBoolean(PREF_KEY_APP_SUPPORT_SHOWN, false)
|
||||
set(value) = sharedPref.edit { putBoolean(PREF_KEY_APP_SUPPORT_SHOWN, value) }
|
||||
|
||||
var isAgreeToProcessData: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_ads_consent_data_processing,
|
||||
R.bool.pref_default_ads_consent_data_processing
|
||||
)
|
||||
set(value) = sharedPref.edit {
|
||||
putBoolean(context.getString(R.string.pref_key_ads_consent_data_processing), value)
|
||||
}
|
||||
|
||||
var isPersonalizedAdsEnabled: Boolean
|
||||
get() = sharedPref.getBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, false)
|
||||
set(value) = sharedPref.edit { putBoolean(PREF_KEY_PERSONALIZED_ADS_ENABLED, value) }
|
||||
|
||||
val isAdsEnabledFlow = flowSharedPref.getBoolean(
|
||||
context.getString(R.string.pref_key_ads_enabled),
|
||||
context.resources.getBoolean(R.bool.pref_default_ads_enabled)
|
||||
).asFlow()
|
||||
|
||||
var isAdsEnabled: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_ads_enabled,
|
||||
R.bool.pref_default_ads_enabled
|
||||
)
|
||||
set(value) = sharedPref.edit {
|
||||
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
|
||||
}
|
||||
|
||||
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
|
||||
|
||||
@ -301,6 +344,10 @@ class PreferencesRepository @Inject constructor(
|
||||
|
||||
private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done"
|
||||
|
||||
private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown"
|
||||
|
||||
private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled"
|
||||
|
||||
private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
@ -23,9 +20,10 @@ class RecipientRepository @Inject constructor(
|
||||
|
||||
private val cacheKey = "recipient"
|
||||
|
||||
suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) {
|
||||
val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId)
|
||||
val old = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
suspend fun refreshRecipients(student: Student, mailbox: Mailbox, type: MailboxType) {
|
||||
val new = sdk.init(student).getRecipients(mailbox.globalKey)
|
||||
.mapToEntities(mailbox.globalKey)
|
||||
val old = recipientDb.loadAll(type, mailbox.globalKey)
|
||||
|
||||
recipientDb.deleteAll(old uniqueSubtract new)
|
||||
recipientDb.insertAll(new uniqueSubtract old)
|
||||
@ -33,18 +31,27 @@ class RecipientRepository @Inject constructor(
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
|
||||
suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List<Recipient> {
|
||||
val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
suspend fun getRecipients(
|
||||
student: Student,
|
||||
mailbox: Mailbox,
|
||||
type: MailboxType
|
||||
): List<Recipient> {
|
||||
val cached = recipientDb.loadAll(type, mailbox.globalKey)
|
||||
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
return if (cached.isEmpty() || isExpired) {
|
||||
refreshRecipients(student, unit, role)
|
||||
recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
refreshRecipients(student, mailbox, type)
|
||||
recipientDb.loadAll(type, mailbox.globalKey)
|
||||
} else cached
|
||||
}
|
||||
|
||||
suspend fun getMessageRecipients(student: Student, message: Message): List<Recipient> {
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
|
||||
.mapToEntities(student.studentId)
|
||||
}
|
||||
suspend fun getMessageSender(
|
||||
student: Student,
|
||||
mailbox: Mailbox,
|
||||
message: Message
|
||||
): List<Recipient> = sdk.init(student)
|
||||
.getMessageReplayDetails(message.messageGlobalKey)
|
||||
.sender
|
||||
.let(::listOf)
|
||||
.mapToEntities(mailbox.globalKey)
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class ReportingUnitRepository @Inject constructor(
|
||||
private val reportingUnitDb: ReportingUnitDao,
|
||||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
suspend fun refreshReportingUnits(student: Student) {
|
||||
val new = sdk.init(student).getReportingUnits().mapToEntities(student)
|
||||
val old = reportingUnitDb.load(student.id.toInt())
|
||||
|
||||
reportingUnitDb.deleteAll(old.uniqueSubtract(new))
|
||||
reportingUnitDb.insertAll(new.uniqueSubtract(old))
|
||||
}
|
||||
|
||||
suspend fun getReportingUnits(student: Student): List<ReportingUnit> {
|
||||
return reportingUnitDb.load(student.id.toInt()).ifEmpty {
|
||||
refreshReportingUnits(student)
|
||||
|
||||
reportingUnitDb.load(student.id.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReportingUnit(student: Student, unitId: Int): ReportingUnit? {
|
||||
return reportingUnitDb.loadOne(student.id.toInt(), unitId) ?: run {
|
||||
refreshReportingUnits(student)
|
||||
|
||||
return reportingUnitDb.loadOne(student.id.toInt(), unitId)
|
||||
}
|
||||
}
|
||||
}
|
@ -28,7 +28,8 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
|
||||
fun getSchoolAnnouncements(
|
||||
student: Student,
|
||||
forceRefresh: Boolean, notify: Boolean = false
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
@ -37,7 +38,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
schoolAnnouncementDb.loadAll(student.studentId)
|
||||
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
@ -56,7 +57,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
||||
)
|
||||
|
||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
||||
return schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
}
|
||||
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||
|
@ -21,7 +21,7 @@ class NewMessageNotification @Inject constructor(
|
||||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.message_new_items, 1),
|
||||
content = "${it.sender}: ${it.subject}",
|
||||
content = "${it.correspondents}: ${it.subject}",
|
||||
destination = Destination.Message,
|
||||
)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.services.sync.works
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||
import io.github.wulkanowy.data.repositories.MailboxRepository
|
||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||
import io.github.wulkanowy.data.waitForResult
|
||||
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
|
||||
@ -11,19 +12,21 @@ import javax.inject.Inject
|
||||
|
||||
class MessageWork @Inject constructor(
|
||||
private val messageRepository: MessageRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val newMessageNotification: NewMessageNotification,
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
messageRepository.getMessages(
|
||||
student = student,
|
||||
semester = semester,
|
||||
mailbox = mailbox,
|
||||
folder = RECEIVED,
|
||||
forceRefresh = true,
|
||||
notify = notify
|
||||
).waitForResult()
|
||||
|
||||
messageRepository.getMessagesFromDatabase(student).first()
|
||||
messageRepository.getMessagesFromDatabase(mailbox).first()
|
||||
.filter { !it.isNotified && it.unread }.let {
|
||||
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
|
||||
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
|
||||
|
@ -1,23 +1,22 @@
|
||||
package io.github.wulkanowy.services.sync.works
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.repositories.MailboxRepository
|
||||
import io.github.wulkanowy.data.repositories.RecipientRepository
|
||||
import io.github.wulkanowy.data.repositories.ReportingUnitRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class RecipientWork @Inject constructor(
|
||||
private val reportingUnitRepository: ReportingUnitRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val recipientRepository: RecipientRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||
reportingUnitRepository.refreshReportingUnits(student)
|
||||
mailboxRepository.refreshMailboxes(student)
|
||||
|
||||
reportingUnitRepository.getReportingUnits(student).let { units ->
|
||||
units.map {
|
||||
recipientRepository.refreshRecipients(student, it, 2)
|
||||
}
|
||||
}
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
|
||||
recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE)
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import io.github.wulkanowy.data.repositories.SchoolAnnouncementRepository
|
||||
import io.github.wulkanowy.data.waitForResult
|
||||
import io.github.wulkanowy.services.sync.notifications.NewSchoolAnnouncementNotification
|
||||
import kotlinx.coroutines.flow.first
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
||||
class SchoolAnnouncementWork @Inject constructor(
|
||||
@ -20,10 +21,13 @@ class SchoolAnnouncementWork @Inject constructor(
|
||||
notify = notify,
|
||||
).waitForResult()
|
||||
|
||||
|
||||
schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first()
|
||||
.filter { !it.isNotified }.let {
|
||||
if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student)
|
||||
schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student)
|
||||
.first()
|
||||
.filter { !it.isNotified && it.date >= LocalDate.now() }
|
||||
.let {
|
||||
if (it.isNotEmpty()) {
|
||||
newSchoolAnnouncementNotification.notify(it, student)
|
||||
}
|
||||
|
||||
schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement ->
|
||||
schoolAnnouncement.isNotified = true
|
||||
|
@ -91,15 +91,19 @@ class AttendancePresenter @Inject constructor(
|
||||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Attendance view is reselected")
|
||||
view?.also { view ->
|
||||
view?.let { view ->
|
||||
if (view.currentStackSize == 1) {
|
||||
baseDate.also {
|
||||
if (currentDate != it) {
|
||||
reloadView(it)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) view.resetView()
|
||||
baseDate = now().previousOrSameSchoolDay
|
||||
|
||||
if (currentDate != baseDate) {
|
||||
reloadView(baseDate)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) {
|
||||
view.resetView()
|
||||
}
|
||||
} else view.popView()
|
||||
} else {
|
||||
view.popView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.account.accountdetails.AccountDetailsFragment
|
||||
import io.github.wulkanowy.ui.modules.attendance.summary.AttendanceSummaryFragment
|
||||
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
|
||||
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
|
||||
import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||
@ -47,6 +48,14 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
||||
override var subtitleString =
|
||||
LocalDate.now().toFormattedString("EEEE, d MMMM yyyy").capitalise()
|
||||
|
||||
override val tileWidth: Int
|
||||
get() {
|
||||
val recyclerWidth = binding.dashboardRecycler.width
|
||||
val margin = requireContext().dpToPx(24f).toInt()
|
||||
|
||||
return ((recyclerWidth - margin) / resources.displayMetrics.density).toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = DashboardFragment()
|
||||
|
@ -1,13 +1,9 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.data.enums.GradeColorTheme
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.utils.AdBanner
|
||||
import io.github.wulkanowy.data.db.entities.Homework as EntitiesHomework
|
||||
|
||||
sealed class DashboardItem(val type: Type) {
|
||||
@ -106,17 +102,26 @@ sealed class DashboardItem(val type: Type) {
|
||||
override val isDataLoaded get() = conferences != null
|
||||
}
|
||||
|
||||
data class Ads(
|
||||
val adBanner: AdBanner? = null,
|
||||
override val error: Throwable? = null,
|
||||
override val isLoading: Boolean = false
|
||||
) : DashboardItem(Type.ADS) {
|
||||
|
||||
override val isDataLoaded get() = adBanner != null
|
||||
}
|
||||
|
||||
enum class Type {
|
||||
ADMIN_MESSAGE,
|
||||
ACCOUNT,
|
||||
HORIZONTAL_GROUP,
|
||||
LESSONS,
|
||||
ADS,
|
||||
GRADES,
|
||||
HOMEWORK,
|
||||
ANNOUNCEMENTS,
|
||||
EXAMS,
|
||||
CONFERENCES,
|
||||
ADS
|
||||
}
|
||||
|
||||
enum class Tile {
|
||||
@ -126,12 +131,12 @@ sealed class DashboardItem(val type: Type) {
|
||||
MESSAGES,
|
||||
ATTENDANCE,
|
||||
LESSONS,
|
||||
ADS,
|
||||
GRADES,
|
||||
HOMEWORK,
|
||||
ANNOUNCEMENTS,
|
||||
EXAMS,
|
||||
CONFERENCES,
|
||||
ADS
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,4 +153,4 @@ fun DashboardItem.Tile.toDashboardItemType() = when (this) {
|
||||
DashboardItem.Tile.EXAMS -> DashboardItem.Type.EXAMS
|
||||
DashboardItem.Tile.CONFERENCES -> DashboardItem.Type.CONFERENCES
|
||||
DashboardItem.Tile.ADS -> DashboardItem.Type.ADS
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.util.Collections
|
||||
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
|
||||
import java.util.*
|
||||
|
||||
class DashboardItemMoveCallback(
|
||||
private val dashboardAdapter: DashboardAdapter,
|
||||
|
@ -8,6 +8,7 @@ import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.repositories.*
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
import io.github.wulkanowy.utils.AdsHelper
|
||||
import io.github.wulkanowy.utils.calculatePercentage
|
||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
||||
import kotlinx.coroutines.flow.*
|
||||
@ -24,6 +25,7 @@ class DashboardPresenter @Inject constructor(
|
||||
private val gradeRepository: GradeRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val messageRepository: MessageRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val attendanceSummaryRepository: AttendanceSummaryRepository,
|
||||
private val timetableRepository: TimetableRepository,
|
||||
private val homeworkRepository: HomeworkRepository,
|
||||
@ -31,7 +33,8 @@ class DashboardPresenter @Inject constructor(
|
||||
private val conferenceRepository: ConferenceRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
|
||||
private val adminMessageRepository: AdminMessageRepository
|
||||
private val adminMessageRepository: AdminMessageRepository,
|
||||
private val adsHelper: AdsHelper
|
||||
) : BasePresenter<DashboardView>(errorHandler, studentRepository) {
|
||||
|
||||
private val dashboardItemLoadedList = mutableListOf<DashboardItem>()
|
||||
@ -55,7 +58,11 @@ class DashboardPresenter @Inject constructor(
|
||||
showContent(false)
|
||||
}
|
||||
|
||||
preferencesRepository.selectedDashboardTilesFlow
|
||||
merge(
|
||||
preferencesRepository.selectedDashboardTilesFlow,
|
||||
preferencesRepository.isAdsEnabledFlow
|
||||
.map { preferencesRepository.selectedDashboardTiles }
|
||||
)
|
||||
.onEach { loadData(tilesToLoad = it) }
|
||||
.launch("dashboard_pref")
|
||||
}
|
||||
@ -166,7 +173,7 @@ class DashboardPresenter @Inject constructor(
|
||||
DashboardItem.Type.CONFERENCES -> {
|
||||
loadConferences(student, forceRefresh)
|
||||
}
|
||||
DashboardItem.Type.ADS -> TODO()
|
||||
DashboardItem.Type.ADS -> loadAds(forceRefresh)
|
||||
DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh)
|
||||
}
|
||||
}
|
||||
@ -221,6 +228,7 @@ class DashboardPresenter @Inject constructor(
|
||||
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
|
||||
flow {
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
val selectedTiles = preferencesRepository.selectedDashboardTiles
|
||||
|
||||
val flowSuccess = flowOf(Resource.Success(null))
|
||||
@ -232,7 +240,7 @@ class DashboardPresenter @Inject constructor(
|
||||
|
||||
val messageFLow = messageRepository.getMessages(
|
||||
student = student,
|
||||
semester = semester,
|
||||
mailbox = mailbox,
|
||||
folder = MessageFolder.RECEIVED,
|
||||
forceRefresh = forceRefresh
|
||||
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
||||
@ -595,6 +603,23 @@ class DashboardPresenter @Inject constructor(
|
||||
.launchWithUniqueRefreshJob("dashboard_admin_messages", forceRefresh)
|
||||
}
|
||||
|
||||
private fun loadAds(forceRefresh: Boolean) {
|
||||
presenterScope.launch {
|
||||
if (!forceRefresh) {
|
||||
updateData(DashboardItem.Ads(), forceRefresh)
|
||||
}
|
||||
|
||||
val dashboardAdItem =
|
||||
runCatching {
|
||||
DashboardItem.Ads(adsHelper.getDashboardTileAdBanner(view!!.tileWidth))
|
||||
}
|
||||
.onFailure { Timber.e(it) }
|
||||
.getOrElse { DashboardItem.Ads(error = it) }
|
||||
|
||||
updateData(dashboardAdItem, forceRefresh)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateData(dashboardItem: DashboardItem, forceRefresh: Boolean) {
|
||||
val isForceRefreshError = forceRefresh && dashboardItem.error != null
|
||||
val isFirstRunDataLoadedError =
|
||||
@ -619,6 +644,18 @@ class DashboardPresenter @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (dashboardItem is DashboardItem.Ads) {
|
||||
if (!dashboardItem.isDataLoaded) {
|
||||
dashboardItemsToLoad = dashboardItemsToLoad - DashboardItem.Type.ADS
|
||||
dashboardTileLoadedList = dashboardTileLoadedList - DashboardItem.Tile.ADS
|
||||
|
||||
dashboardItemLoadedList.removeAll { it.type == DashboardItem.Type.ADS }
|
||||
} else {
|
||||
dashboardItemsToLoad = dashboardItemsToLoad + DashboardItem.Type.ADS
|
||||
dashboardTileLoadedList = dashboardTileLoadedList + DashboardItem.Tile.ADS
|
||||
}
|
||||
}
|
||||
|
||||
if (forceRefresh) {
|
||||
updateForceRefreshData(dashboardItem)
|
||||
} else {
|
||||
|
@ -4,6 +4,8 @@ import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface DashboardView : BaseView {
|
||||
|
||||
val tileWidth: Int
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<DashboardItem>)
|
||||
@ -27,4 +29,4 @@ interface DashboardView : BaseView {
|
||||
fun openNotificationsCenterView()
|
||||
|
||||
fun openInternetBrowser(url: String)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
@ -22,24 +22,15 @@ import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.enums.GradeColorTheme
|
||||
import io.github.wulkanowy.databinding.ItemDashboardAccountBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardAnnouncementsBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardConferencesBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardExamsBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardGradesBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardHomeworkBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardHorizontalGroupBinding
|
||||
import io.github.wulkanowy.databinding.ItemDashboardLessonsBinding
|
||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||
import io.github.wulkanowy.utils.left
|
||||
import io.github.wulkanowy.utils.nickOrName
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
import io.github.wulkanowy.databinding.*
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import io.github.wulkanowy.utils.*
|
||||
import timber.log.Timber
|
||||
import java.time.*
|
||||
import java.util.Timer
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.timer
|
||||
|
||||
@ -120,6 +111,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder(
|
||||
ItemDashboardAdminMessageBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
DashboardItem.Type.ADS.ordinal -> AdsViewHolder(
|
||||
ItemDashboardAdsBinding.inflate(inflater, parent, false)
|
||||
)
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
@ -135,6 +129,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
is ExamsViewHolder -> bindExamsViewHolder(holder, position)
|
||||
is ConferencesViewHolder -> bindConferencesViewHolder(holder, position)
|
||||
is AdminMessageViewHolder -> bindAdminMessage(holder, position)
|
||||
is AdsViewHolder -> bindAdsViewHolder(holder, position)
|
||||
}
|
||||
}
|
||||
|
||||
@ -746,6 +741,20 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindAdsViewHolder(adsViewHolder: AdsViewHolder, position: Int) {
|
||||
val item = (items[position] as DashboardItem.Ads).adBanner ?: return
|
||||
val binding = adsViewHolder.binding
|
||||
|
||||
binding.dashboardAdminMessageItemContent.removeAllViews()
|
||||
binding.dashboardAdminMessageItemContent.addView(
|
||||
item.view,
|
||||
ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class AccountViewHolder(val binding: ItemDashboardAccountBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
@ -788,6 +797,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
||||
class AdminMessageViewHolder(val binding: ItemDashboardAdminMessageBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class AdsViewHolder(val binding: ItemDashboardAdsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
private class DiffCallback(
|
||||
private val newList: List<DashboardItem>,
|
||||
private val oldList: List<DashboardItem>
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
@ -33,4 +33,4 @@ class DashboardAnnouncementsAdapter :
|
||||
|
||||
class ViewHolder(val binding: SubitemDashboardAnnouncementsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
@ -33,4 +33,4 @@ class DashboardConferencesAdapter :
|
||||
|
||||
class ViewHolder(val binding: SubitemDashboardConferencesBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
@ -56,4 +56,4 @@ class DashboardExamsAdapter :
|
||||
|
||||
class ViewHolder(val binding: SubitemDashboardExamsBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
@ -1,4 +1,4 @@
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
@ -53,4 +53,4 @@ class DashboardHomeworkAdapter : RecyclerView.Adapter<DashboardHomeworkAdapter.V
|
||||
|
||||
class ViewHolder(val binding: SubitemDashboardHomeworkBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
}
|
@ -17,16 +17,13 @@ val debugMessageItems = listOf(
|
||||
)
|
||||
|
||||
private fun generateMessage(sender: String, subject: String) = Message(
|
||||
sender = sender,
|
||||
subject = subject,
|
||||
studentId = 0,
|
||||
realId = 0,
|
||||
messageId = 0,
|
||||
senderId = 0,
|
||||
recipient = "",
|
||||
messageId = 123,
|
||||
date = Instant.now(),
|
||||
folderId = 0,
|
||||
unread = true,
|
||||
removed = false,
|
||||
hasAttachments = false
|
||||
hasAttachments = false,
|
||||
messageGlobalKey = "",
|
||||
correspondents = sender,
|
||||
mailboxKey = "",
|
||||
)
|
||||
|
@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
|
||||
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
|
||||
subject = subject,
|
||||
content = content,
|
||||
studentId = 0,
|
||||
userLoginId = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
|
@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.grade.details
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.enums.GradeExpandMode
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.*
|
||||
import io.github.wulkanowy.data.repositories.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
@ -204,6 +204,7 @@ class GradeDetailsPresenter @Inject constructor(
|
||||
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
|
||||
gradeDetailsWithAverage.subject.lowercase()
|
||||
}
|
||||
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
|
||||
}
|
||||
}
|
||||
.map { (subject, average, points, _, grades) ->
|
||||
|
@ -2,6 +2,9 @@ package io.github.wulkanowy.ui.modules.grade.summary
|
||||
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.*
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -14,6 +17,7 @@ import javax.inject.Inject
|
||||
class GradeSummaryPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val averageProvider: GradeAverageProvider,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository) {
|
||||
@ -127,7 +131,17 @@ class GradeSummaryPresenter @Inject constructor(
|
||||
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummary> {
|
||||
return items
|
||||
.filter { !checkEmpty(it) }
|
||||
.sortedBy { it.subject }
|
||||
.let { gradeSubjects ->
|
||||
when (preferencesRepository.gradeSortingMode) {
|
||||
DATE -> gradeSubjects.sortedByDescending { gradeDetailsWithAverage ->
|
||||
gradeDetailsWithAverage.grades.maxByOrNull { it.date }?.date
|
||||
}
|
||||
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
|
||||
gradeDetailsWithAverage.subject.lowercase()
|
||||
}
|
||||
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
|
||||
}
|
||||
}
|
||||
.map { it.summary.copy(average = it.average) }
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
|
||||
}
|
||||
|
||||
//https://developer.android.com/guide/playcore/in-app-updates#status_callback
|
||||
@Deprecated("Deprecated in Java")
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
@ -172,7 +172,7 @@ class LoginFormPresenter @Inject constructor(
|
||||
if ("@" in login && "||" !in login && "login" !in host && "email" !in host) {
|
||||
val emailHost = login.substringAfter("@")
|
||||
val emailDomain = URL(host).host
|
||||
if (emailHost != emailDomain) {
|
||||
if (!emailHost.equals(emailDomain, true)) {
|
||||
view?.setErrorEmailInvalid(domain = emailDomain)
|
||||
isCorrect = false
|
||||
}
|
||||
|
@ -6,9 +6,7 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import android.webkit.*
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
@ -206,10 +204,9 @@ class LoginRecoverFragment :
|
||||
}
|
||||
|
||||
override fun onReceivedError(
|
||||
view: WebView,
|
||||
errorCode: Int,
|
||||
description: String,
|
||||
failingUrl: String
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?,
|
||||
error: WebResourceError?
|
||||
) {
|
||||
recoverWebViewSuccess = false
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.elevation.ElevationOverlayProvider
|
||||
import com.ncapdevi.fragnav.FragNavController
|
||||
import com.ncapdevi.fragnav.FragNavController.Companion.HIDE
|
||||
@ -20,6 +21,7 @@ import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.databinding.ActivityMainBinding
|
||||
import io.github.wulkanowy.databinding.DialogAdsConsentBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.account.accountquick.AccountQuickDialog
|
||||
@ -100,6 +102,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
}
|
||||
|
||||
//https://developer.android.com/guide/playcore/in-app-updates#status_callback
|
||||
@Deprecated("Deprecated in Java")
|
||||
@Suppress("DEPRECATION")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
@ -287,6 +290,50 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
|
||||
inAppReviewHelper.showInAppReview(this)
|
||||
}
|
||||
|
||||
override fun showAppSupport() {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.main_support_title)
|
||||
.setMessage(R.string.main_support_description)
|
||||
.setPositiveButton(R.string.main_support_positive) { _, _ -> presenter.onEnableAdsSelected() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||
.setOnDismissListener { }
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun showPrivacyPolicyDialog() {
|
||||
val dialogAdsConsentBinding = DialogAdsConsentBinding.inflate(layoutInflater)
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.pref_ads_consent_title)
|
||||
.setMessage(R.string.pref_ads_consent_description)
|
||||
.setView(dialogAdsConsentBinding.root)
|
||||
.show()
|
||||
|
||||
dialogAdsConsentBinding.adsConsentOver.setOnCheckedChangeListener { _, isChecked ->
|
||||
dialogAdsConsentBinding.adsConsentPersonalised.isEnabled = isChecked
|
||||
}
|
||||
|
||||
dialogAdsConsentBinding.adsConsentPersonalised.setOnClickListener {
|
||||
presenter.onPrivacyAgree(true)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialogAdsConsentBinding.adsConsentNonPersonalised.setOnClickListener {
|
||||
presenter.onPrivacyAgree(false)
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialogAdsConsentBinding.adsConsentPrivacy.setOnClickListener { presenter.onPrivacySelected() }
|
||||
dialogAdsConsentBinding.adsConsentCancel.setOnClickListener { dialog.cancel() }
|
||||
}
|
||||
|
||||
override fun openPrivacyPolicy() {
|
||||
openInternetBrowser(
|
||||
"https://wulkanowy.github.io/polityka-prywatnosci.html",
|
||||
::showMessage
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
navController.onSaveInstanceState(outState)
|
||||
|
@ -18,7 +18,10 @@ import io.github.wulkanowy.ui.modules.grade.GradeView
|
||||
import io.github.wulkanowy.ui.modules.message.MessageView
|
||||
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersView
|
||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||
import io.github.wulkanowy.utils.AdsHelper
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import timber.log.Timber
|
||||
@ -29,10 +32,12 @@ import javax.inject.Inject
|
||||
class MainPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val prefRepository: PreferencesRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val syncManager: SyncManager,
|
||||
private val analytics: AnalyticsHelper,
|
||||
private val json: Json
|
||||
private val json: Json,
|
||||
private val adsHelper: AdsHelper,
|
||||
private val appInfo: AppInfo
|
||||
) : BasePresenter<MainView>(errorHandler, studentRepository) {
|
||||
|
||||
private var studentsWitSemesters: List<StudentWithSemesters>? = null
|
||||
@ -47,7 +52,7 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
private val Destination?.startMenuIndex
|
||||
get() = when {
|
||||
this == null -> prefRepository.startMenuIndex
|
||||
this == null -> preferencesRepository.startMenuIndex
|
||||
destinationType in rootDestinationTypeList -> {
|
||||
rootDestinationTypeList.indexOf(destinationType)
|
||||
}
|
||||
@ -71,6 +76,8 @@ class MainPresenter @Inject constructor(
|
||||
|
||||
syncManager.startPeriodicSyncWorker()
|
||||
|
||||
checkAppSupport()
|
||||
|
||||
analytics.logEvent("app_open", "destination" to initDestination.toString())
|
||||
Timber.i("Main view was initialized with $initDestination")
|
||||
}
|
||||
@ -155,18 +162,52 @@ class MainPresenter @Inject constructor(
|
||||
} == true
|
||||
}
|
||||
|
||||
private fun checkInAppReview() {
|
||||
prefRepository.inAppReviewCount++
|
||||
fun onEnableAdsSelected() {
|
||||
view?.showPrivacyPolicyDialog()
|
||||
}
|
||||
|
||||
if (prefRepository.inAppReviewDate == null) {
|
||||
prefRepository.inAppReviewDate = Instant.now()
|
||||
fun onPrivacyAgree(isPersonalizedAds: Boolean) {
|
||||
preferencesRepository.isAgreeToProcessData = true
|
||||
preferencesRepository.isPersonalizedAdsEnabled = isPersonalizedAds
|
||||
|
||||
adsHelper.initialize()
|
||||
|
||||
preferencesRepository.isAdsEnabled = true
|
||||
}
|
||||
|
||||
fun onPrivacySelected() {
|
||||
view?.openPrivacyPolicy()
|
||||
}
|
||||
|
||||
private fun checkInAppReview() {
|
||||
preferencesRepository.inAppReviewCount++
|
||||
|
||||
if (preferencesRepository.inAppReviewDate == null) {
|
||||
preferencesRepository.inAppReviewDate = Instant.now()
|
||||
}
|
||||
|
||||
if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 &&
|
||||
Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate)
|
||||
if (!preferencesRepository.isAppReviewDone && preferencesRepository.inAppReviewCount >= 50 &&
|
||||
Instant.now().minus(Duration.ofDays(14)).isAfter(preferencesRepository.inAppReviewDate)
|
||||
) {
|
||||
view?.showInAppReview()
|
||||
prefRepository.isAppReviewDone = true
|
||||
preferencesRepository.isAppReviewDone = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkAppSupport() {
|
||||
if (!preferencesRepository.isAppSupportShown && !preferencesRepository.isAdsEnabled
|
||||
&& appInfo.buildFlavor == "play"
|
||||
) {
|
||||
presenterScope.launch {
|
||||
val student = runCatching { studentRepository.getCurrentStudent(false) }
|
||||
.onFailure { Timber.e(it) }
|
||||
.getOrElse { return@launch }
|
||||
|
||||
if (Instant.now().minus(Duration.ofDays(28)).isAfter(student.registrationDate)) {
|
||||
view?.showAppSupport()
|
||||
preferencesRepository.isAppSupportShown = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,12 @@ interface MainView : BaseView {
|
||||
|
||||
fun showInAppReview()
|
||||
|
||||
fun showAppSupport()
|
||||
|
||||
fun showPrivacyPolicyDialog()
|
||||
|
||||
fun openPrivacyPolicy()
|
||||
|
||||
fun openMoreDestination(destination: Destination)
|
||||
|
||||
interface MainChildView {
|
||||
|
@ -4,6 +4,8 @@ import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
@ -75,29 +77,25 @@ class MessagePreviewAdapter @Inject constructor() :
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun bindMessage(holder: MessageViewHolder, message: Message) {
|
||||
val context = holder.binding.root.context
|
||||
val recipientCount = message.unreadBy + message.readBy
|
||||
|
||||
val readText = when {
|
||||
recipientCount > 1 -> {
|
||||
context.getString(R.string.message_read_by, message.readBy, recipientCount)
|
||||
}
|
||||
message.readBy == 1 -> {
|
||||
context.getString(R.string.message_read, context.getString(R.string.all_yes))
|
||||
}
|
||||
else -> context.getString(R.string.message_read, context.getString(R.string.all_no))
|
||||
val readTextValue = when {
|
||||
!message.unread -> R.string.all_yes
|
||||
else -> R.string.all_no
|
||||
}
|
||||
val readText = context.getString(R.string.message_read, context.getString(readTextValue))
|
||||
|
||||
with(holder.binding) {
|
||||
messagePreviewSubject.text =
|
||||
message.subject.ifBlank { root.context.getString(R.string.message_no_subject) }
|
||||
messagePreviewDate.text = root.context.getString(
|
||||
messagePreviewSubject.text = message.subject.ifBlank {
|
||||
context.getString(R.string.message_no_subject)
|
||||
}
|
||||
messagePreviewDate.text = context.getString(
|
||||
R.string.message_date,
|
||||
message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
|
||||
)
|
||||
messagePreviewRead.text = readText
|
||||
messagePreviewContent.text = message.content
|
||||
messagePreviewContent.text = message.content.parseAsHtml(FROM_HTML_MODE_COMPACT)
|
||||
messagePreviewFromSender.text = message.sender
|
||||
messagePreviewToRecipient.text = message.recipient
|
||||
messagePreviewToRecipient.text = message.recipients
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,8 @@ class MessagePreviewFragment :
|
||||
get() = getString(R.string.message_no_subject)
|
||||
|
||||
override val printHTML: String
|
||||
get() = requireContext().assets.open("message-print-page.html").bufferedReader().use { it.readText() }
|
||||
get() = requireContext().assets.open("message-print-page.html").bufferedReader()
|
||||
.use { it.readText() }
|
||||
|
||||
override val messageNotExists: String
|
||||
get() = getString(R.string.message_not_exists)
|
||||
@ -81,7 +82,10 @@ class MessagePreviewFragment :
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = FragmentMessagePreviewBinding.bind(view)
|
||||
messageContainer = binding.messagePreviewContainer
|
||||
presenter.onAttachView(this, (savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message)
|
||||
presenter.onAttachView(
|
||||
this,
|
||||
(savedInstanceState ?: arguments)?.getSerializable(MESSAGE_ID_KEY) as? Message
|
||||
)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
@ -101,6 +105,8 @@ class MessagePreviewFragment :
|
||||
menuShareButton = menu.findItem(R.id.messagePreviewMenuShare)
|
||||
menuPrintButton = menu.findItem(R.id.messagePreviewMenuPrint)
|
||||
presenter.onCreateOptionsMenu()
|
||||
|
||||
menu.findItem(R.id.mainMenuAccount).isVisible = false
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@ -129,8 +135,8 @@ class MessagePreviewFragment :
|
||||
binding.messagePreviewRecycler.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun showOptions(show: Boolean) {
|
||||
menuReplyButton?.isVisible = show
|
||||
override fun showOptions(show: Boolean, isReplayable: Boolean) {
|
||||
menuReplyButton?.isVisible = isReplayable
|
||||
menuForwardButton?.isVisible = show
|
||||
menuDeleteButton?.isVisible = show
|
||||
menuShareButton?.isVisible = show
|
||||
@ -173,7 +179,8 @@ class MessagePreviewFragment :
|
||||
val webView = WebView(requireContext())
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) =
|
||||
false
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
createWebPrintJob(view, jobName)
|
||||
|
@ -1,10 +1,12 @@
|
||||
package io.github.wulkanowy.ui.modules.message.preview
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.core.text.parseAsHtml
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.repositories.MailboxRepository
|
||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
@ -19,6 +21,7 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val messageRepository: MessageRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {
|
||||
|
||||
@ -52,7 +55,7 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
|
||||
private fun loadData(messageToLoad: Message) {
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getStudentById(messageToLoad.studentId)
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
messageRepository.getMessage(student, messageToLoad, true)
|
||||
}
|
||||
.logResourceStatus("message ${messageToLoad.messageId} preview")
|
||||
@ -104,62 +107,69 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onShare(): Boolean {
|
||||
message?.let {
|
||||
var text =
|
||||
"Temat: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}\n" + when (it.sender.isNotEmpty()) {
|
||||
true -> "Od: ${it.sender}\n"
|
||||
false -> "Do: ${it.recipient}\n"
|
||||
} + "Data: ${it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${it.content}"
|
||||
val message = message ?: return false
|
||||
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
||||
|
||||
attachments?.let { attachments ->
|
||||
if (attachments.isNotEmpty()) {
|
||||
text += "\n\nZałączniki:"
|
||||
val text = buildString {
|
||||
appendLine("Temat: $subject")
|
||||
appendLine("Od: ${message.sender}")
|
||||
appendLine("Do: ${message.recipients}")
|
||||
appendLine("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}")
|
||||
|
||||
attachments.forEach { attachment ->
|
||||
text += "\n${attachment.filename}: ${attachment.url}"
|
||||
}
|
||||
}
|
||||
appendLine()
|
||||
|
||||
appendLine(message.content.parseAsHtml())
|
||||
|
||||
if (!attachments.isNullOrEmpty()) {
|
||||
appendLine()
|
||||
appendLine("Załączniki:")
|
||||
|
||||
append(attachments.orEmpty().joinToString(separator = "\n") { attachment ->
|
||||
"${attachment.filename}: ${attachment.url}"
|
||||
})
|
||||
}
|
||||
|
||||
view?.shareText(
|
||||
text,
|
||||
"FW: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }}"
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
view?.shareText(
|
||||
subject = "FW: $subject",
|
||||
text = text,
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
fun onPrint(): Boolean {
|
||||
message?.let {
|
||||
val dateString = it.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
|
||||
val infoContent = "<div><h4>Data wysłania</h4>$dateString</div>" + when {
|
||||
it.sender.isNotEmpty() -> "<div><h4>Od</h4>${it.sender}</div>"
|
||||
else -> "<div><h4>Do</h4>${it.recipient}</div>"
|
||||
}
|
||||
val message = message ?: return false
|
||||
val subject = message.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }
|
||||
|
||||
val messageContent = "<p>${it.content}</p>"
|
||||
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
|
||||
.replace(Regex("[\\n\\r]"), "<br>")
|
||||
val dateString = message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
val jobName = "Wiadomość " + when {
|
||||
it.sender.isNotEmpty() -> "od ${it.sender}"
|
||||
else -> "do ${it.recipient}"
|
||||
} + " $dateString: ${it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() }} | Wulkanowy"
|
||||
val infoContent = buildString {
|
||||
append("<div><h4>Data wysłania</h4>$dateString</div>")
|
||||
|
||||
view?.apply {
|
||||
val html = printHTML
|
||||
.replace(
|
||||
"%SUBJECT%",
|
||||
it.subject.ifBlank { view?.messageNoSubjectString.orEmpty() })
|
||||
.replace("%CONTENT%", messageContent)
|
||||
.replace("%INFO%", infoContent)
|
||||
printDocument(html, jobName)
|
||||
}
|
||||
return true
|
||||
append("<div><h4>Od</h4>${message.sender}</div>")
|
||||
append("<div><h4>DO</h4>${message.recipients}</div>")
|
||||
}
|
||||
return false
|
||||
val messageContent = "<p>${message.content}</p>"
|
||||
.replace(Regex("[\\n\\r]{2,}"), "</p><p>")
|
||||
.replace(Regex("[\\n\\r]"), "<br>")
|
||||
|
||||
val jobName = buildString {
|
||||
append("Wiadomość ")
|
||||
append("od ${message.correspondents}")
|
||||
append("do ${message.correspondents}")
|
||||
append(" $dateString: $subject | Wulkanowy")
|
||||
}
|
||||
|
||||
view?.apply {
|
||||
val html = printHTML
|
||||
.replace("%SUBJECT%", subject)
|
||||
.replace("%CONTENT%", messageContent)
|
||||
.replace("%INFO%", infoContent)
|
||||
printDocument(html, jobName)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun deleteMessage() {
|
||||
@ -168,16 +178,17 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
view?.run {
|
||||
showContent(false)
|
||||
showProgress(true)
|
||||
showOptions(false)
|
||||
showOptions(show = false, isReplayable = false)
|
||||
showErrorView(false)
|
||||
}
|
||||
|
||||
Timber.i("Delete message ${message?.id}")
|
||||
Timber.i("Delete message ${message?.messageGlobalKey}")
|
||||
|
||||
presenterScope.launch {
|
||||
runCatching {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
messageRepository.deleteMessage(student, message!!)
|
||||
val student = studentRepository.getCurrentStudent(decryptPass = true)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
messageRepository.deleteMessage(student, mailbox, message!!)
|
||||
}
|
||||
.onFailure {
|
||||
retryCallback = { onMessageDelete() }
|
||||
@ -211,7 +222,10 @@ class MessagePreviewPresenter @Inject constructor(
|
||||
|
||||
private fun initOptions() {
|
||||
view?.apply {
|
||||
showOptions(message != null)
|
||||
showOptions(
|
||||
show = message != null,
|
||||
isReplayable = message?.folderId != MessageFolder.SENT.id,
|
||||
)
|
||||
message?.let {
|
||||
when (it.folderId == MessageFolder.TRASHED.id) {
|
||||
true -> setDeletedOptionsLabels()
|
||||
|
@ -28,7 +28,7 @@ interface MessagePreviewView : BaseView {
|
||||
|
||||
fun setErrorRetryCallback(callback: () -> Unit)
|
||||
|
||||
fun showOptions(show: Boolean)
|
||||
fun showOptions(show: Boolean, isReplayable: Boolean)
|
||||
|
||||
fun setDeletedOptionsLabels()
|
||||
|
||||
|
@ -6,6 +6,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.TouchDelegate
|
||||
@ -13,11 +14,12 @@ import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.text.toHtml
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.databinding.ActivitySendMessageBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.utils.dpToPx
|
||||
@ -72,17 +74,32 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
|
||||
override val messageSuccess: String
|
||||
get() = getString(R.string.message_send_successful)
|
||||
|
||||
override val mailboxStudent: String
|
||||
get() = getString(R.string.message_mailbox_type_student)
|
||||
|
||||
override val mailboxParent: String
|
||||
get() = getString(R.string.message_mailbox_type_parent)
|
||||
|
||||
override val mailboxGuardian: String
|
||||
get() = getString(R.string.message_mailbox_type_guardian)
|
||||
|
||||
override val mailboxEmployee: String
|
||||
get() = getString(R.string.message_mailbox_type_employee)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivitySendMessageBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||
setContentView(
|
||||
ActivitySendMessageBinding.inflate(layoutInflater).apply { binding = this }.root
|
||||
)
|
||||
setSupportActionBar(binding.sendMessageToolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
messageContainer = binding.sendMessageContainer
|
||||
|
||||
formRecipientsData = binding.sendMessageTo.addedChipItems as List<RecipientChipItem>
|
||||
formSubjectValue = binding.sendMessageSubject.text.toString()
|
||||
formContentValue = binding.sendMessageMessageContent.text.toString()
|
||||
formContentValue =
|
||||
binding.sendMessageMessageContent.text.toString().parseAsHtml().toString()
|
||||
|
||||
presenter.onAttachView(
|
||||
view = this,
|
||||
@ -110,7 +127,7 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
|
||||
}
|
||||
|
||||
private fun onMessageContentChange(text: CharSequence?) {
|
||||
formContentValue = text.toString()
|
||||
formContentValue = (text as Spanned).toHtml()
|
||||
presenter.onMessageContentChange()
|
||||
}
|
||||
|
||||
@ -132,8 +149,8 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
|
||||
return presenter.onUpNavigate()
|
||||
}
|
||||
|
||||
override fun setReportingUnit(unit: ReportingUnit) {
|
||||
binding.sendMessageFrom.text = unit.senderName
|
||||
override fun setMailbox(mailbox: String) {
|
||||
binding.sendMessageFrom.text = mailbox
|
||||
}
|
||||
|
||||
override fun setRecipients(recipients: List<RecipientChipItem>) {
|
||||
@ -165,7 +182,7 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
|
||||
}
|
||||
|
||||
override fun setContent(content: String) {
|
||||
binding.sendMessageMessageContent.setText(content)
|
||||
binding.sendMessageMessageContent.setText(content.parseAsHtml())
|
||||
}
|
||||
|
||||
override fun showMessage(text: String) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
package io.github.wulkanowy.ui.modules.message.send
|
||||
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.logResourceStatus
|
||||
@ -25,9 +27,8 @@ import javax.inject.Inject
|
||||
class SendMessagePresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val messageRepository: MessageRepository,
|
||||
private val reportingUnitRepository: ReportingUnitRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val recipientRepository: RecipientRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val analytics: AnalyticsHelper
|
||||
@ -52,20 +53,21 @@ class SendMessagePresenter @Inject constructor(
|
||||
message?.let {
|
||||
setSubject(
|
||||
when (reply) {
|
||||
true -> "Re: "
|
||||
true -> "RE: "
|
||||
else -> "FW: "
|
||||
} + message.subject
|
||||
)
|
||||
if (preferencesRepository.fillMessageContent || reply != true) {
|
||||
setContent(
|
||||
when (reply) {
|
||||
true -> "\n\n"
|
||||
else -> ""
|
||||
} + when (message.sender.isNotEmpty()) {
|
||||
true -> "Od: ${message.sender}\n"
|
||||
false -> "Do: ${message.recipient}\n"
|
||||
} + "Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}\n\n${message.content}"
|
||||
)
|
||||
setContent(buildString {
|
||||
if (reply == true) {
|
||||
append("<br><br>")
|
||||
}
|
||||
|
||||
append("Od: ${message.sender}<br>")
|
||||
append("Do: ${message.recipients}<br>")
|
||||
append("Data: ${message.date.toFormattedString("yyyy-MM-dd HH:mm:ss")}<br><br>")
|
||||
append(message.content)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,21 +113,24 @@ class SendMessagePresenter @Inject constructor(
|
||||
private fun loadData(message: Message?, reply: Boolean?) {
|
||||
resourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
val unit = reportingUnitRepository.getReportingUnit(student, semester.unitId)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
|
||||
Timber.i("Loading recipients started")
|
||||
val recipients = when {
|
||||
unit != null -> recipientRepository.getRecipients(student, unit, 2)
|
||||
else -> listOf()
|
||||
}.let { createChips(it) }
|
||||
val recipients = createChips(
|
||||
recipients = recipientRepository.getRecipients(
|
||||
student = student,
|
||||
mailbox = mailbox,
|
||||
type = MailboxType.EMPLOYEE,
|
||||
)
|
||||
)
|
||||
Timber.i("Loading recipients result: Success, fetched %d recipients", recipients.size)
|
||||
|
||||
Timber.i("Loading message recipients started")
|
||||
val messageRecipients = when {
|
||||
message != null && reply == true -> recipientRepository.getMessageRecipients(
|
||||
student,
|
||||
message
|
||||
message != null && reply == true -> recipientRepository.getMessageSender(
|
||||
student = student,
|
||||
message = message,
|
||||
mailbox = mailbox,
|
||||
)
|
||||
else -> emptyList()
|
||||
}.let { createChips(it) }
|
||||
@ -134,7 +139,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
messageRecipients.size
|
||||
)
|
||||
|
||||
Triple(unit, recipients, messageRecipients)
|
||||
Triple(mailbox, recipients, messageRecipients)
|
||||
}
|
||||
.logResourceStatus("load recipients")
|
||||
.onEach {
|
||||
@ -143,19 +148,14 @@ class SendMessagePresenter @Inject constructor(
|
||||
showProgress(true)
|
||||
showContent(false)
|
||||
}
|
||||
is Resource.Success -> it.data.let { (reportingUnit, recipientChips, selectedRecipientChips) ->
|
||||
is Resource.Success -> it.data.let { (mailbox, recipientChips, selectedRecipientChips) ->
|
||||
view?.run {
|
||||
if (reportingUnit != null) {
|
||||
setReportingUnit(reportingUnit)
|
||||
setRecipients(recipientChips)
|
||||
if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
|
||||
selectedRecipientChips
|
||||
)
|
||||
showContent(true)
|
||||
} else {
|
||||
Timber.i("Loading recipients result: Can't find the reporting unit")
|
||||
view?.showEmpty(true)
|
||||
}
|
||||
setMailbox(getMailboxName(mailbox))
|
||||
setRecipients(recipientChips)
|
||||
if (selectedRecipientChips.isNotEmpty()) setSelectedRecipients(
|
||||
selectedRecipientChips
|
||||
)
|
||||
showContent(true)
|
||||
}
|
||||
}
|
||||
is Resource.Error -> {
|
||||
@ -171,7 +171,14 @@ class SendMessagePresenter @Inject constructor(
|
||||
private fun sendMessage(subject: String, content: String, recipients: List<Recipient>) {
|
||||
resourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
messageRepository.sendMessage(student, subject, content, recipients)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
messageRepository.sendMessage(
|
||||
student = student,
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients,
|
||||
mailboxId = mailbox.globalKey,
|
||||
)
|
||||
}.logResourceStatus("sending message").onEach {
|
||||
when (it) {
|
||||
is Resource.Loading -> view?.run {
|
||||
@ -201,31 +208,44 @@ class SendMessagePresenter @Inject constructor(
|
||||
}
|
||||
|
||||
private fun createChips(recipients: List<Recipient>): List<RecipientChipItem> {
|
||||
fun generateCorrectSummary(recipientRealName: String): String {
|
||||
val substring = recipientRealName.substringBeforeLast("-")
|
||||
return when {
|
||||
substring == recipientRealName -> recipientRealName
|
||||
substring.indexOf("(") != -1 -> {
|
||||
recipientRealName.indexOf("(")
|
||||
.let { recipientRealName.substring(if (it != -1) it else 0) }
|
||||
}
|
||||
substring.indexOf("[") != -1 -> {
|
||||
recipientRealName.indexOf("[")
|
||||
.let { recipientRealName.substring(if (it != -1) it else 0) }
|
||||
}
|
||||
else -> recipientRealName.substringAfter("-")
|
||||
}.trim()
|
||||
}
|
||||
|
||||
return recipients.map {
|
||||
RecipientChipItem(
|
||||
title = it.name,
|
||||
summary = generateCorrectSummary(it.realName),
|
||||
title = it.userName,
|
||||
summary = buildString {
|
||||
getMailboxType(it.type)?.let(::append)
|
||||
if (isNotBlank()) append(" ")
|
||||
|
||||
append("(${it.schoolShortName})")
|
||||
},
|
||||
recipient = it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMailboxName(mailbox: Mailbox): String {
|
||||
return buildString {
|
||||
append(mailbox.userName)
|
||||
append(" - ")
|
||||
append(getMailboxType(mailbox.type))
|
||||
|
||||
if (mailbox.type == MailboxType.PARENT) {
|
||||
append(" - ")
|
||||
append(mailbox.studentName)
|
||||
}
|
||||
|
||||
append(" - ")
|
||||
append("(${mailbox.schoolNameShort})")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMailboxType(type: MailboxType): String? = when (type) {
|
||||
MailboxType.STUDENT -> view?.mailboxStudent
|
||||
MailboxType.PARENT -> view?.mailboxParent
|
||||
MailboxType.GUARDIAN -> view?.mailboxGuardian
|
||||
MailboxType.EMPLOYEE -> view?.mailboxEmployee
|
||||
MailboxType.UNKNOWN -> null
|
||||
}
|
||||
|
||||
fun onMessageContentChange() {
|
||||
presenterScope.launch {
|
||||
messageUpdateChannel.send(Unit)
|
||||
@ -263,7 +283,7 @@ class SendMessagePresenter @Inject constructor(
|
||||
|
||||
fun getRecipientsNames(): String {
|
||||
return messageRepository.draftMessage?.recipients.orEmpty()
|
||||
.joinToString { it.recipient.name }
|
||||
.joinToString { it.recipient.userName }
|
||||
}
|
||||
|
||||
fun clearDraft() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.message.send
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import io.github.wulkanowy.ui.base.BaseView
|
||||
|
||||
interface SendMessageView : BaseView {
|
||||
@ -18,9 +18,17 @@ interface SendMessageView : BaseView {
|
||||
|
||||
val messageSuccess: String
|
||||
|
||||
val mailboxStudent: String
|
||||
|
||||
val mailboxParent: String
|
||||
|
||||
val mailboxGuardian: String
|
||||
|
||||
val mailboxEmployee: String
|
||||
|
||||
fun initView()
|
||||
|
||||
fun setReportingUnit(unit: ReportingUnit)
|
||||
fun setMailbox(mailbox: String)
|
||||
|
||||
fun setRecipients(recipients: List<RecipientChipItem>)
|
||||
|
||||
|
@ -8,7 +8,6 @@ import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.databinding.ItemMessageBinding
|
||||
import io.github.wulkanowy.databinding.ItemMessageChipsBinding
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
@ -88,12 +87,8 @@ class MessageTabAdapter @Inject constructor() :
|
||||
with(holder.binding) {
|
||||
val style = if (message.unread) Typeface.BOLD else Typeface.NORMAL
|
||||
|
||||
messageItemAuthor.run {
|
||||
text = if (message.folderId == MessageFolder.SENT.id) {
|
||||
message.recipient
|
||||
} else {
|
||||
message.sender
|
||||
}
|
||||
with(messageItemAuthor) {
|
||||
text = message.correspondents
|
||||
setTypeface(null, style)
|
||||
}
|
||||
messageItemSubject.run {
|
||||
@ -145,7 +140,7 @@ class MessageTabAdapter @Inject constructor() :
|
||||
val newItem = new[newItemPosition]
|
||||
|
||||
return if (oldItem is MessageTabDataItem.MessageItem && newItem is MessageTabDataItem.MessageItem) {
|
||||
oldItem.message.id == newItem.message.id
|
||||
oldItem.message.messageGlobalKey == newItem.message.messageGlobalKey
|
||||
} else {
|
||||
oldItem.viewType == newItem.viewType
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.message.tab
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.repositories.MailboxRepository
|
||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||
import io.github.wulkanowy.ui.base.BasePresenter
|
||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||
@ -26,7 +26,7 @@ class MessageTabPresenter @Inject constructor(
|
||||
errorHandler: ErrorHandler,
|
||||
studentRepository: StudentRepository,
|
||||
private val messageRepository: MessageRepository,
|
||||
private val semesterRepository: SemesterRepository,
|
||||
private val mailboxRepository: MailboxRepository,
|
||||
private val analytics: AnalyticsHelper
|
||||
) : BasePresenter<MessageTabView>(errorHandler, studentRepository) {
|
||||
|
||||
@ -122,7 +122,8 @@ class MessageTabPresenter @Inject constructor(
|
||||
|
||||
runCatching {
|
||||
val student = studentRepository.getCurrentStudent(true)
|
||||
messageRepository.deleteMessages(student, messageList)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
messageRepository.deleteMessages(student, mailbox, messageList)
|
||||
}
|
||||
.onFailure(errorHandler::dispatch)
|
||||
.onSuccess { view?.showMessagesDeleted() }
|
||||
@ -159,7 +160,7 @@ class MessageTabPresenter @Inject constructor(
|
||||
}
|
||||
|
||||
fun onMessageItemSelected(messageItem: MessageTabDataItem.MessageItem, position: Int) {
|
||||
Timber.i("Select message ${messageItem.message.id} item (position: $position)")
|
||||
Timber.i("Select message ${messageItem.message.messageGlobalKey} item (position: $position)")
|
||||
|
||||
if (!isActionMode) {
|
||||
view?.run {
|
||||
@ -206,8 +207,8 @@ class MessageTabPresenter @Inject constructor(
|
||||
|
||||
flatResourceFlow {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
messageRepository.getMessages(student, semester, folder, forceRefresh)
|
||||
val mailbox = mailboxRepository.getMailbox(student)
|
||||
messageRepository.getMessages(student, mailbox, folder, forceRefresh)
|
||||
}
|
||||
.logResourceStatus("load $folder message")
|
||||
.onResourceData {
|
||||
@ -333,7 +334,7 @@ class MessageTabPresenter @Inject constructor(
|
||||
addAll(data.map { message ->
|
||||
MessageTabDataItem.MessageItem(
|
||||
message = message,
|
||||
isSelected = messagesToDelete.any { it.id == message.id },
|
||||
isSelected = messagesToDelete.any { it.messageGlobalKey == message.messageGlobalKey },
|
||||
isActionMode = isActionMode
|
||||
)
|
||||
})
|
||||
@ -345,10 +346,9 @@ class MessageTabPresenter @Inject constructor(
|
||||
private fun calculateMatchRatio(message: Message, query: String): Int {
|
||||
val subjectRatio = FuzzySearch.tokenSortPartialRatio(query.lowercase(), message.subject)
|
||||
|
||||
val senderOrRecipientRatio = FuzzySearch.tokenSortPartialRatio(
|
||||
val correspondentsRatio = FuzzySearch.tokenSortPartialRatio(
|
||||
query.lowercase(),
|
||||
if (message.sender.isNotEmpty()) message.sender.lowercase()
|
||||
else message.recipient.lowercase()
|
||||
message.correspondents
|
||||
)
|
||||
|
||||
val dateRatio = listOf(
|
||||
@ -364,7 +364,7 @@ class MessageTabPresenter @Inject constructor(
|
||||
|
||||
|
||||
return (subjectRatio.toDouble().pow(2)
|
||||
+ senderOrRecipientRatio.toDouble().pow(2)
|
||||
+ correspondentsRatio.toDouble().pow(2)
|
||||
+ dateRatio.toDouble().pow(2) * 2
|
||||
).toInt()
|
||||
}
|
||||
|
@ -87,15 +87,19 @@ class TimetablePresenter @Inject constructor(
|
||||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Timetable view is reselected")
|
||||
view?.also { view ->
|
||||
view?.let { view ->
|
||||
if (view.currentStackSize == 1) {
|
||||
baseDate.also {
|
||||
if (currentDate != it) {
|
||||
reloadView(it)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) view.resetView()
|
||||
baseDate = now().nextOrSameSchoolDay
|
||||
|
||||
if (currentDate != baseDate) {
|
||||
reloadView(baseDate)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) {
|
||||
view.resetView()
|
||||
}
|
||||
} else view.popView()
|
||||
} else {
|
||||
view.popView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.appwidget.AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID
|
||||
import android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_IDS
|
||||
import android.appwidget.AppWidgetManager.*
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@ -17,6 +15,7 @@ import io.github.wulkanowy.databinding.ActivityWidgetConfigureBinding
|
||||
import io.github.wulkanowy.ui.base.BaseActivity
|
||||
import io.github.wulkanowy.ui.base.WidgetConfigureAdapter
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_CONFIGURE
|
||||
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetProvider.Companion.EXTRA_FROM_PROVIDER
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import javax.inject.Inject
|
||||
@ -92,6 +91,7 @@ class TimetableWidgetConfigureActivity :
|
||||
.apply {
|
||||
action = ACTION_APPWIDGET_UPDATE
|
||||
putExtra(EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
|
||||
putExtra(EXTRA_FROM_CONFIGURE, true)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.github.wulkanowy.ui.modules.timetablewidget
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetManager.*
|
||||
@ -61,6 +60,8 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
|
||||
private const val BUTTON_RESET = "buttonReset"
|
||||
|
||||
const val EXTRA_FROM_CONFIGURE = "extraFromConfigure"
|
||||
|
||||
const val EXTRA_FROM_PROVIDER = "extraFromProvider"
|
||||
|
||||
fun getDateWidgetKey(appWidgetId: Int) = "timetable_widget_date_$appWidgetId"
|
||||
@ -87,12 +88,22 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
}
|
||||
|
||||
private suspend fun onUpdate(context: Context, intent: Intent) {
|
||||
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) === null) {
|
||||
intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS)?.forEach { appWidgetId ->
|
||||
if (intent.getStringExtra(EXTRA_BUTTON_TYPE) == null) {
|
||||
val isFromConfigure = intent.getBooleanExtra(EXTRA_FROM_CONFIGURE, false)
|
||||
val appWidgetIds = intent.getIntArrayExtra(EXTRA_APPWIDGET_IDS) ?: return
|
||||
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
val student =
|
||||
getStudent(sharedPref.getLong(getStudentWidgetKey(appWidgetId), 0), appWidgetId)
|
||||
val savedDataEpochDay = sharedPref.getLong(getDateWidgetKey(appWidgetId), 0)
|
||||
|
||||
updateWidget(context, appWidgetId, getWidgetDateToLoad(appWidgetId), student)
|
||||
val dateToLoad = if (isFromConfigure && savedDataEpochDay != 0L) {
|
||||
LocalDate.ofEpochDay(savedDataEpochDay)
|
||||
} else {
|
||||
getWidgetDefaultDateToLoad(appWidgetId)
|
||||
}
|
||||
|
||||
updateWidget(context, appWidgetId, dateToLoad, student)
|
||||
}
|
||||
} else {
|
||||
val buttonType = intent.getStringExtra(EXTRA_BUTTON_TYPE)
|
||||
@ -104,10 +115,10 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
val savedDate =
|
||||
LocalDate.ofEpochDay(sharedPref.getLong(getDateWidgetKey(toggledWidgetId), 0))
|
||||
val date = when (buttonType) {
|
||||
BUTTON_RESET -> getWidgetDateToLoad(toggledWidgetId)
|
||||
BUTTON_RESET -> getWidgetDefaultDateToLoad(toggledWidgetId)
|
||||
BUTTON_NEXT -> savedDate.nextSchoolDay
|
||||
BUTTON_PREV -> savedDate.previousSchoolDay
|
||||
else -> getWidgetDateToLoad(toggledWidgetId)
|
||||
else -> getWidgetDefaultDateToLoad(toggledWidgetId)
|
||||
}
|
||||
if (!buttonType.isNullOrBlank()) {
|
||||
analytics.logEvent(
|
||||
@ -132,7 +143,6 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
private fun updateWidget(
|
||||
context: Context,
|
||||
appWidgetId: Int,
|
||||
@ -273,7 +283,7 @@ class TimetableWidgetProvider : BroadcastReceiver() {
|
||||
return avatarBitmap
|
||||
}
|
||||
|
||||
private fun getWidgetDateToLoad(appWidgetId: Int): LocalDate {
|
||||
private fun getWidgetDefaultDateToLoad(appWidgetId: Int): LocalDate {
|
||||
val lastLessonEndTimestamp =
|
||||
sharedPref.getLong(getTodayLastLessonEndDateTimeWidgetKey(appWidgetId), 0)
|
||||
val lastLessonEndDateTime =
|
||||
|
@ -13,6 +13,7 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
|
||||
|
||||
@ColorInt
|
||||
fun Context.getThemeAttrColor(@AttrRes colorAttr: Int): Int {
|
||||
val array = obtainStyledAttributes(null, intArrayOf(colorAttr))
|
||||
|
@ -1,5 +1,9 @@
|
||||
Wersja 1.6.4
|
||||
Wersja 1.7.0
|
||||
|
||||
- naprawiliśmy błąd ładowania frekwencji na GPE i Lubelskim Portalu Oświatowym
|
||||
- naprawiliśmy logowanie do aplikacji
|
||||
- dodaliśmy wsparcie nowego modułu Wiadomości Plus
|
||||
- dodaliśmy nową możliwość wsparcia naszego projektu przez opcjonalne reklamy
|
||||
- dodaliśmy sortowanie po średniej
|
||||
- naprawiliśmy też kilka usterek wpływających na komfort używania aplikacji
|
||||
|
||||
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases
|
||||
|
@ -16,8 +16,7 @@
|
||||
app:layout_constraintBottom_toTopOf="@id/sendMessageScroll"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:targetApi="lollipop" />
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<io.github.wulkanowy.materialchipsinput.ConsumedNestedScrollView
|
||||
android:id="@+id/sendMessageScroll"
|
||||
|
79
app/src/main/res/layout/dialog_ads_consent.xml
Normal file
79
app/src/main/res/layout/dialog_ads_consent.xml
Normal file
@ -0,0 +1,79 @@
|
||||
<?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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ads_consent_privacy"
|
||||
style="@style/Widget.MaterialComponents.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:padding="0dp"
|
||||
android:text="Privacy Policy"
|
||||
android:textAllCaps="false"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/ads_consent_over"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="17dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="I am over 18 years old"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="14dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/ads_consent_privacy" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ads_consent_personalised"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:enabled="false"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:text="Yes, personalized ads"
|
||||
app:layout_constraintTop_toBottomOf="@id/ads_consent_over" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ads_consent_non_personalised"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:text="Yes, non-personalized ads"
|
||||
app:layout_constraintBottom_toTopOf="@id/ads_consent_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@id/ads_consent_personalised"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/ads_consent_cancel"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:text="Cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintVertical_bias="0" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -19,7 +19,9 @@
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/noteRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent"
|
||||
tools:itemCount="4"
|
||||
tools:listitem="@layout/item_note" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -37,7 +37,7 @@
|
||||
android:padding="10dp"
|
||||
android:visibility="gone"
|
||||
tools:ignore="UseCompoundDrawables"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
@ -63,7 +63,7 @@
|
||||
android:orientation="vertical"
|
||||
android:visibility="invisible"
|
||||
tools:ignore="UseCompoundDrawables"
|
||||
tools:visibility="visible">
|
||||
tools:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="100dp"
|
||||
|
15
app/src/main/res/layout/item_dashboard_ads.xml
Normal file
15
app/src/main/res/layout/item_dashboard_ads.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginVertical="6dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/dashboard_admin_message_item_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
@ -54,7 +54,7 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noteItemPoints"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textSize="16sp"
|
||||
|
@ -3,10 +3,11 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="64dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
android:focusable="true"
|
||||
android:minHeight="64dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/student_info_item_title"
|
||||
@ -16,12 +17,12 @@
|
||||
android:layout_marginTop="13dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/student_info_item_arrow"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:maxLines="1"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<TextView
|
||||
@ -31,12 +32,12 @@
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="bottom"
|
||||
android:maxLines="1"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toStartOf="@id/student_info_item_arrow"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/student_info_item_title"
|
||||
tools:maxLines="1"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<ImageView
|
||||
|
@ -34,6 +34,7 @@
|
||||
<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>
|
||||
|
@ -77,6 +77,9 @@
|
||||
<string name="main_log_in">Přihlásit se</string>
|
||||
<string name="main_session_expired">Relace vypršela</string>
|
||||
<string name="main_session_relogin">Relace vypršela. Přihlaste se prosím znovu</string>
|
||||
<string name="main_support_title">Podpora aplikace</string>
|
||||
<string name="main_support_description">Líbí se Vám tato aplikace? Podpořte její vývoj tím, že povolíte neinvazivní reklamy, které můžete kdykoliv vypnout</string>
|
||||
<string name="main_support_positive">Zapnout reklamy</string>
|
||||
<!--Grade-->
|
||||
<string name="grade_header">Známka</string>
|
||||
<string name="grade_semester">Semestr %d</string>
|
||||
@ -94,7 +97,7 @@
|
||||
<string name="grade_summary_predicted_grade">Předpokládaná známka</string>
|
||||
<string name="grade_summary_calculated_average">Vypočítaný 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ý.\n3. Sčítání vypočítaných průměrů\n4. Výpočet aritmetického průměru součtených 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>
|
||||
<string name="grade_summary_final_average_help_dialog_message">Konečný průměr je aritmetický průměr vypočítaný ze všech aktuálně dostupných konečných známek v daném semestru.\n\nSchéma výpočtu se skládá z následujících kroků:\n1. Sčítání konečných známek zadaných učiteli\n2. Děleno počtem předmětů, pro které už byly uděleny známky</string>
|
||||
<string name="grade_summary_final_average">Konečný průměr</string>
|
||||
@ -294,6 +297,10 @@
|
||||
<string name="message_move_to_trash">Přesunout do koše</string>
|
||||
<string name="message_delete_forever">Odstranit natrvalo</string>
|
||||
<string name="message_delete_success">Zpráva byla úspěšně odstraněna</string>
|
||||
<string name="message_mailbox_type_student">žák</string>
|
||||
<string name="message_mailbox_type_parent">rodič</string>
|
||||
<string name="message_mailbox_type_guardian">opatrovník</string>
|
||||
<string name="message_mailbox_type_employee">pracovník</string>
|
||||
<string name="message_share">Sdílet</string>
|
||||
<string name="message_print">Vytisknout</string>
|
||||
<string name="message_subject">Předmět</string>
|
||||
@ -305,7 +312,6 @@
|
||||
<string name="message_chip_only_unread">Pouze nepřečtené</string>
|
||||
<string name="message_chip_only_with_attachments">Pouze s přílohami</string>
|
||||
<string name="message_read">Přečtena: %s</string>
|
||||
<string name="message_read_by">Přečtena přes: %1$d z %2$d osob</string>
|
||||
<plurals name="message_number_item">
|
||||
<item quantity="one">%1$d zpráva</item>
|
||||
<item quantity="few">%1$d zprávy</item>
|
||||
@ -721,6 +727,10 @@
|
||||
<string name="pref_ads_privacy_link">Ochrana osobních údajů</string>
|
||||
<string name="pref_ads_loading">Reklama se načítá</string>
|
||||
<string name="pref_ads_once_per_visit">Děkujeme za vaši podporu, vraťte se později pro více reklam</string>
|
||||
<string name="pref_ads_consent_title">Můžeme použít Vaše data k zobrazení reklam?</string>
|
||||
<string name="pref_ads_consent_description">Volbu můžete kdykoliv změnit v nastavení aplikace. Můžeme použít Vaše data k zobrazení reklam šitých pro vás nebo pomocí méně vašich dat zobrazovat nepřizpůsobené reklamy. Podrobnosti naleznete v našich Zásadách ochrany osobních údajů</string>
|
||||
<string name="pref_ads_summary_personalized">Přizpůsobené reklamy</string>
|
||||
<string name="pref_ads_summary_non_personalized">Nepřizpůsobené reklamy</string>
|
||||
<string name="pref_settings_advanced_title">Pokročilé</string>
|
||||
<string name="pref_settings_appearance_title">Vzhled a chování</string>
|
||||
<string name="pref_settings_notifications_title">Oznámení</string>
|
||||
|
@ -34,6 +34,7 @@
|
||||
<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>
|
||||
|
@ -77,6 +77,9 @@
|
||||
<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_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>
|
||||
<!--Grade-->
|
||||
<string name="grade_header">Note</string>
|
||||
<string name="grade_semester">Semester %d</string>
|
||||
@ -94,7 +97,7 @@
|
||||
<string name="grade_summary_predicted_grade">Vorhergesagte Note</string>
|
||||
<string name="grade_summary_calculated_average">Berechnender Durchschnitt</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. \n3. Addition der berechneten Durchschnittswerte\n4. Berechnung des arithmetischen Mittels der summierten Mittelwerte</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>
|
||||
<string name="grade_summary_final_average_help_dialog_message">Der Final Average ist das arithmetische Mittel, das aus allen derzeit verfügbaren Abschlussnoten des jeweiligen Semesters berechnet wird. \n\nDas Berechnungsschema besteht aus folgenden Schritten:\n1. Zusammenfassung der von den Lehrern gegebenen Abschlussnoten\n2. Division durch die Anzahl der Fächer, die bereits bewertet wurden</string>
|
||||
<string name="grade_summary_final_average">Finaler Durchschnitt</string>
|
||||
@ -260,6 +263,10 @@
|
||||
<string name="message_move_to_trash">In Papierkorb verschieben</string>
|
||||
<string name="message_delete_forever">Dauerhaft löschen</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>
|
||||
<string name="message_mailbox_type_guardian">Betreuer</string>
|
||||
<string name="message_mailbox_type_employee">Mitarbeiter</string>
|
||||
<string name="message_share">Teilen</string>
|
||||
<string name="message_print">Drucken</string>
|
||||
<string name="message_subject">Thema</string>
|
||||
@ -271,7 +278,6 @@
|
||||
<string name="message_chip_only_unread">Nur ungelesen</string>
|
||||
<string name="message_chip_only_with_attachments">Nur mit Anhängen</string>
|
||||
<string name="message_read">Lesen: %s</string>
|
||||
<string name="message_read_by">Lesen von: %1$d von %2$d Personen</string>
|
||||
<plurals name="message_number_item">
|
||||
<item quantity="one">%1$d Nachricht</item>
|
||||
<item quantity="other">%1$d Nachrichten</item>
|
||||
@ -633,6 +639,10 @@
|
||||
<string name="pref_ads_privacy_link">Datenschutzerklärung</string>
|
||||
<string name="pref_ads_loading">Anzeige wird geladen</string>
|
||||
<string name="pref_ads_once_per_visit">Vielen Dank für Ihre Unterstützung, kommen Sie später wieder für weitere Anzeigen</string>
|
||||
<string name="pref_ads_consent_title">Können wir Ihre Daten zur Anzeige von Werbung verwenden?</string>
|
||||
<string name="pref_ads_consent_description">Sie können Ihre Wahl jederzeit in den App-Einstellungen ändern. Wir verwenden Ihre Daten, um auf Sie zugeschnittene Anzeigen anzuzeigen oder unter Verwendung weniger Ihrer Daten nicht personalisierte Werbung anzuzeigen. Bitte lesen Sie unsere Datenschutzerklärung für Details</string>
|
||||
<string name="pref_ads_summary_personalized">Personalisierte Werbung</string>
|
||||
<string name="pref_ads_summary_non_personalized">keine personalisierte Werbung</string>
|
||||
<string name="pref_settings_advanced_title">Erweitert</string>
|
||||
<string name="pref_settings_appearance_title">Aussehen & Verhalten</string>
|
||||
<string name="pref_settings_notifications_title">Benachrichtigungen</string>
|
||||
|
@ -34,6 +34,7 @@
|
||||
<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>
|
||||
|
@ -77,6 +77,9 @@
|
||||
<string name="main_log_in">Zaloguj się</string>
|
||||
<string name="main_session_expired">Sesja wygasła</string>
|
||||
<string name="main_session_relogin">Sesja wygasła, zaloguj się ponownie</string>
|
||||
<string name="main_support_title">Wparcie aplikacji</string>
|
||||
<string name="main_support_description">Podoba Ci się ta aplikacja? Wspieraj jej rozwój poprzez włączenie nieinwazyjnych reklam, które możesz wyłączyć w dowolnym momencie</string>
|
||||
<string name="main_support_positive">Włącz reklamy</string>
|
||||
<!--Grade-->
|
||||
<string name="grade_header">Ocena</string>
|
||||
<string name="grade_semester">Semestr %d</string>
|
||||
@ -94,7 +97,7 @@
|
||||
<string name="grade_summary_predicted_grade">Przewidywana ocena</string>
|
||||
<string name="grade_summary_calculated_average">Obliczona średnia</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_title">Jak działa obliczona średnia?</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen tylko z wybranego semestru</b>:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia ze średnich z obu semestrów</b>:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semetrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej z zsumowanych średnich</string>
|
||||
<string name="grade_summary_calculated_average_help_dialog_message">Obliczona średnia jest średnią arytmetyczną obliczoną ze średnich przedmiotów. Pozwala ona na poznanie przybliżonej średniej końcowej. Jest obliczana w sposób wybrany przez użytkownika w ustawieniach aplikacji. Zaleca się wybranie odpowiedniej opcji. Dzieje się tak dlatego, że obliczanie średnich w szkołach różni się. Dodatkowo, jeśli twoja szkoła ma włączone średnie przedmiotów na stronie dziennika Vulcan, aplikacja pobiera je i ich nie oblicza. Można to zmienić, wymuszając obliczanie średniej w ustawieniach aplikacji.\n\n<b>Średnia ocen tylko z wybranego semestru</b>:\n1. Obliczanie średniej arytmetycznej każdego przedmiotu w danym semestrze\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia ze średnich z obu semestrów</b>:\n1.Obliczanie średniej arytmetycznej każdego przedmiotu w semestrze 1 i 2\n2. Obliczanie średniej arytmetycznej obliczonych średnich w semestrze 1 i 2 każdego przedmiotu.\n3. Zsumowanie obliczonych średnich\n4. Obliczanie średniej arytmetycznej zsumowanych średnich\n\n<b>Średnia wszystkich ocen z całego roku:</b>\n1. Obliczanie średniej arytmetycznej z każdego przedmiotu w ciągu całego roku. Końcowa ocena w 1 semestrze jest bez znaczenia.\n2. Zsumowanie obliczonych średnich\n3. Obliczanie średniej arytmetycznej z zsumowanych średnich</string>
|
||||
<string name="grade_summary_final_average_help_dialog_title">Jak działa końcowa średnia?</string>
|
||||
<string name="grade_summary_final_average_help_dialog_message">Średnią końcową jest średnia arytmetyczna obliczona na podstawie wszystkich obecnie dostępnych ocen końcowych w danym semestrze.\n\nSchemat obliczeń składa się z następujących kroków:\n1. Sumowanie końcowych ocen wpisanych przez nauczycieli\n2. Dzielenie przez liczbę przedmiotów, z których oceny zostały już wystawione</string>
|
||||
<string name="grade_summary_final_average">Końcowa średnia</string>
|
||||
@ -294,6 +297,10 @@
|
||||
<string name="message_move_to_trash">Przenieś do kosza</string>
|
||||
<string name="message_delete_forever">Usuń trwale</string>
|
||||
<string name="message_delete_success">Wiadomość usunięta pomyślnie</string>
|
||||
<string name="message_mailbox_type_student">uczeń</string>
|
||||
<string name="message_mailbox_type_parent">rodzic</string>
|
||||
<string name="message_mailbox_type_guardian">opiekun</string>
|
||||
<string name="message_mailbox_type_employee">pracownik</string>
|
||||
<string name="message_share">Udostępnij</string>
|
||||
<string name="message_print">Drukuj</string>
|
||||
<string name="message_subject">Temat</string>
|
||||
@ -305,7 +312,6 @@
|
||||
<string name="message_chip_only_unread">Tylko nieprzeczytane</string>
|
||||
<string name="message_chip_only_with_attachments">Tylko z załącznikami</string>
|
||||
<string name="message_read">Przeczytana: %s</string>
|
||||
<string name="message_read_by">Przeczytana przez: %1$d z %2$d osób</string>
|
||||
<plurals name="message_number_item">
|
||||
<item quantity="one">%1$d wiadomość</item>
|
||||
<item quantity="few">%1$d wiadomości</item>
|
||||
@ -701,7 +707,7 @@
|
||||
<string name="pref_notification_go_to_settings">Przejdź do ustawień</string>
|
||||
<string name="pref_services_header">Synchronizacja</string>
|
||||
<string name="pref_services_switch">Automatyczna aktualizacja</string>
|
||||
<string name="pref_services_suspended">Zawieszona na wakacjach</string>
|
||||
<string name="pref_services_suspended">Wstrzymana podczas wakacji</string>
|
||||
<string name="pref_services_interval">Interwał aktualizacji</string>
|
||||
<string name="pref_services_wifi">Tylko WiFi</string>
|
||||
<string name="pref_services_force_sync">Synchronizuj teraz</string>
|
||||
@ -721,6 +727,10 @@
|
||||
<string name="pref_ads_privacy_link">Polityka prywatności</string>
|
||||
<string name="pref_ads_loading">Ładowanie reklamy</string>
|
||||
<string name="pref_ads_once_per_visit">Dziękujemy za wsparcie, wróć później po więcej reklam</string>
|
||||
<string name="pref_ads_consent_title">Czy możemy używać Twoich danych do wyświetlania reklam?</string>
|
||||
<string name="pref_ads_consent_description">Możesz zmienić swój wybór w dowolnym momencie w ustawieniach aplikacji. Możemy wykorzystać Twoje dane do wyświetlania reklam dostosowanych do Ciebie lub, przy użyciu mniejszej ilości danych, wyświetlić niepersonalizowane reklamy. Zobacz naszą Politykę Prywatności, aby uzyskać więcej informacji</string>
|
||||
<string name="pref_ads_summary_personalized">Spersonalizowane reklamy</string>
|
||||
<string name="pref_ads_summary_non_personalized">Niespersonalizowane reklamy</string>
|
||||
<string name="pref_settings_advanced_title">Zaawansowane</string>
|
||||
<string name="pref_settings_appearance_title">Wygląd i zachowanie</string>
|
||||
<string name="pref_settings_notifications_title">Powiadomienia</string>
|
||||
|
@ -34,6 +34,7 @@
|
||||
<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>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user