Compare commits
No commits in common. "1.8.3" and "1.6.0" have entirely different histories.
196 changed files with 2135 additions and 20252 deletions
21
.github/workflows/deploy-store.yml
vendored
21
.github/workflows/deploy-store.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Deploy release
|
||||
name: Deploy to app stores
|
||||
|
||||
on:
|
||||
release:
|
||||
|
@ -7,17 +7,16 @@ on:
|
|||
jobs:
|
||||
|
||||
deploy-google-play:
|
||||
name: Google Play
|
||||
name: Deploy to google play
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: google-play
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
@ -38,22 +37,20 @@ jobs:
|
|||
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
||||
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
||||
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
||||
DASHBOARD_TILE_AD_ID: ${{ secrets.DASHBOARD_TILE_AD_ID }}
|
||||
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
||||
|
||||
deploy-app-gallery:
|
||||
name: AppGallery
|
||||
name: Deploy to AppGallery
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
environment: app-gallery
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
|
20
.github/workflows/deploy-test.yml
vendored
20
.github/workflows/deploy-test.yml
vendored
|
@ -1,4 +1,4 @@
|
|||
name: Deploy DEV
|
||||
name: Deploy to app tests
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -18,12 +18,11 @@ jobs:
|
|||
timeout-minutes: 10
|
||||
environment: app-center
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
@ -67,7 +66,7 @@ jobs:
|
|||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assembleFdroidDebug --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}.apk
|
||||
path: app/build/outputs/apk/fdroid/debug/app-fdroid-debug.apk
|
||||
|
@ -88,12 +87,11 @@ jobs:
|
|||
environment: app-distribution
|
||||
if: github.event_name != 'pull_request_target'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
@ -133,7 +131,7 @@ jobs:
|
|||
BITRISE_KEY_PASSWORD: ${{ secrets.BITRISE_KEY_PASSWORD }}
|
||||
run: ./gradlew assemblePlayDebug -PenableFirebase --stacktrace
|
||||
- name: Upload apk to github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: wulkanowyDEV-${{ env.RUN_NUMBER }}-dev.apk
|
||||
path: app/build/outputs/apk/play/debug/app-play-debug.apk
|
||||
|
|
72
.github/workflows/test.yml
vendored
72
.github/workflows/test.yml
vendored
|
@ -2,28 +2,24 @@ name: Tests
|
|||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- 'hotfix/**'
|
||||
branches: [ master, develop ]
|
||||
tags: [ '*' ]
|
||||
pull_request:
|
||||
branches: [ master, develop ]
|
||||
|
||||
jobs:
|
||||
|
||||
tests-fdroid:
|
||||
name: F-Droid
|
||||
unit-tests:
|
||||
name: Unit tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: fkirc/skip-duplicate-actions@master
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- uses: actions/setup-java@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: 11
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
|
@ -33,58 +29,6 @@ jobs:
|
|||
run: |
|
||||
./gradlew testFdroidDebugUnitTest --stacktrace
|
||||
./gradlew jacocoTestReport --stacktrace
|
||||
- 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
|
||||
- uses: codecov/codecov-action@v1
|
||||
with:
|
||||
flags: unit
|
||||
|
|
15
README.cs.md
15
README.cs.md
|
@ -1,13 +1,18 @@
|
|||
Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||
[English version of README](README.en.md)
|
||||
|
||||
[Deutsche Version von README](README.de.md)
|
||||
|
||||
[Polska wersja README](README.md)
|
||||
|
||||
[Slovenská verzia README](README.sk.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
[](https://translate.wulkanowy.net.pl)
|
||||
|
||||
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
||||
|
||||
|
@ -34,7 +39,7 @@ Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
|||
* podpora více účtů s možností přejmenování žáků
|
||||
* tmavý a černý (AMOLED) motiv
|
||||
* offline režim
|
||||
* volitelné reklamy na podporu projektu
|
||||
* žádné reklamy
|
||||
|
||||
## Stáhnout
|
||||
|
||||
|
@ -52,7 +57,7 @@ Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGal
|
|||
|
||||
Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání
|
||||
|
||||
## Postaveno s pomocí
|
||||
## Postaveno s
|
||||
|
||||
|
||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||
|
|
13
README.de.md
13
README.de.md
|
@ -1,13 +1,14 @@
|
|||
[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||
[Polska wersja README](README.md)
|
||||
|
||||
[English version of README](README.en.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
[](https://translate.wulkanowy.net.pl)
|
||||
|
||||
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
||||
|
||||
|
@ -21,7 +22,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
|
|||
* Prozentsatz der Anwesenheit
|
||||
* Prüfungen
|
||||
* Stundenplan
|
||||
* abgeschlossene Unterrichtsstunden
|
||||
* Unterricht abgeschlossen
|
||||
* Nachrichten
|
||||
* Hausaufgaben
|
||||
* Anmerkungen
|
||||
|
@ -34,7 +35,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
|
|||
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
|
||||
* dunkles und schwarzes (AMOLED) Thema
|
||||
* Offline-Modus
|
||||
* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen
|
||||
* keine Werbung
|
||||
|
||||
## Herunterladen
|
||||
|
||||
|
@ -50,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 eine [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) die beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
|
||||
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
|
||||
|
||||
## Gebaut mit
|
||||
|
||||
|
|
13
README.en.md
13
README.en.md
|
@ -1,13 +1,18 @@
|
|||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||
[Polska wersja README](README.md)
|
||||
|
||||
[Deutsche Version von README](README.de.md)
|
||||
|
||||
[Česká verze README](README.cs.md)
|
||||
|
||||
[Slovenská verzia README](README.sk.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
[](https://translate.wulkanowy.net.pl)
|
||||
|
||||
Unofficial android VULCAN UONET+ register client for both students and their parents
|
||||
|
||||
|
@ -34,7 +39,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
|
|||
* support for multiple accounts with the ability to rename students
|
||||
* dark and black (AMOLED) theme
|
||||
* offline mode
|
||||
* optional ads which allow to support the project
|
||||
* no ads
|
||||
|
||||
## Download
|
||||
|
||||
|
|
13
README.md
13
README.md
|
@ -1,13 +1,18 @@
|
|||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.md)
|
||||
[English version of README](README.en.md)
|
||||
|
||||
[Deutsche Version von README](README.de.md)
|
||||
|
||||
[Česká verze README](README.cs.md)
|
||||
|
||||
[Slovenská verzia README](README.sk.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
[](https://translate.wulkanowy.net.pl)
|
||||
|
||||
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||
|
||||
|
@ -34,7 +39,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
|||
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||
* ciemny i czarny (AMOLED) motyw
|
||||
* tryb offline
|
||||
* opcjonalne reklamy umożliwiające wsparcie projektu
|
||||
* brak reklam
|
||||
|
||||
## Pobierz
|
||||
|
||||
|
|
15
README.sk.md
15
README.sk.md
|
@ -1,13 +1,18 @@
|
|||
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
|
||||
[English version of README](README.en.md)
|
||||
|
||||
[Deutsche Version von README](README.de.md)
|
||||
|
||||
[Polska wersja README](README.md)
|
||||
|
||||
[Česká verze README](README.cs.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
[](https://translate.wulkanowy.net.pl)
|
||||
|
||||
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
||||
|
||||
|
@ -34,7 +39,7 @@ Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
|||
* podpora viacerých účtov s možnosťou premenovania žiakov
|
||||
* tmavý a čierny (AMOLED) motív
|
||||
* offline režim
|
||||
* voliteľné reklamy na podporu projektu
|
||||
* žiadne reklamy
|
||||
|
||||
## Stiahnuť
|
||||
|
||||
|
@ -52,7 +57,7 @@ Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGa
|
|||
|
||||
Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie
|
||||
|
||||
## Postavené s pomocou
|
||||
## Postavené s
|
||||
|
||||
|
||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||
|
|
|
@ -15,35 +15,33 @@ apply from: 'sonarqube.gradle'
|
|||
apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
namespace 'io.github.wulkanowy'
|
||||
compileSdkVersion 32
|
||||
compileSdkVersion 31
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 32
|
||||
versionCode 118
|
||||
versionName "1.8.3"
|
||||
targetSdkVersion 31
|
||||
versionCode 104
|
||||
versionName "1.6.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())
|
||||
|
@ -96,12 +94,10 @@ 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 {
|
||||
|
@ -126,8 +122,6 @@ android {
|
|||
|
||||
testOptions.unitTests {
|
||||
includeAndroidResources = true
|
||||
// workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750
|
||||
all { jvmArgs '-noverify' }
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -138,14 +132,12 @@ android {
|
|||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
||||
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += ['META-INF/library_release.kotlin_module',
|
||||
'META-INF/library-core_release.kotlin_module']
|
||||
}
|
||||
exclude 'META-INF/library_release.kotlin_module'
|
||||
exclude 'META-INF/library-core_release.kotlin_module'
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
|
@ -161,8 +153,8 @@ play {
|
|||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.10d
|
||||
updatePriority = 5
|
||||
userFraction = 0.25d
|
||||
updatePriority = 1
|
||||
enabled.set(false)
|
||||
}
|
||||
|
||||
|
@ -179,42 +171,42 @@ huaweiPublish {
|
|||
ext {
|
||||
work_manager = "2.7.1"
|
||||
android_hilt = "1.0.0"
|
||||
room = "2.4.3"
|
||||
room = "2.4.2"
|
||||
chucker = "3.5.2"
|
||||
mockk = "1.13.2"
|
||||
coroutines = "1.6.4"
|
||||
mockk = "1.12.2"
|
||||
coroutines = "1.6.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.8.3"
|
||||
implementation "io.github.wulkanowy:sdk:1.6.0"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
|
||||
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.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.4"
|
||||
implementation "androidx.annotation:annotation:1.5.0"
|
||||
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.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.4"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
implementation "com.google.android.material:material:1.7.0"
|
||||
implementation "com.google.android.material:material:1.5.0"
|
||||
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||
implementation 'com.github.lopspower:CircularImageView:4.3.0'
|
||||
implementation 'com.github.lopspower:CircularImageView:4.2.0'
|
||||
|
||||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
|
@ -230,27 +222,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.10.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
|
||||
|
||||
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.2.2"
|
||||
implementation "io.coil-kt:coil:1.4.0"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:31.0.3')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:29.3.0')
|
||||
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:21.3.0'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.8.0.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.3.300'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.300'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
|
@ -263,10 +255,10 @@ dependencies {
|
|||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.9'
|
||||
testImplementation "androidx.test:runner:1.5.1"
|
||||
testImplementation "androidx.test.ext:junit:1.1.4"
|
||||
testImplementation "androidx.test:core:1.5.0"
|
||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||
testImplementation "androidx.test:runner:1.4.0"
|
||||
testImplementation "androidx.test.ext:junit:1.1.3"
|
||||
testImplementation "androidx.test:core:1.4.0"
|
||||
testImplementation "androidx.room:room-testing:$room"
|
||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,92 +1,33 @@
|
|||
{
|
||||
"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": ""
|
||||
},
|
||||
"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":""
|
||||
},
|
||||
"search":{
|
||||
"url":"https://search-dre.cloud.huawei.com"
|
||||
},
|
||||
"cloudstorage": {
|
||||
"storage_url_sg_back": "https://agc-storage-dra.cloud.huawei.asia",
|
||||
"storage_url_ru_back": "https://agc-storage-drru.cloud.huawei.ru",
|
||||
"storage_url_ru": "https://agc-storage-drru.cloud.huawei.ru",
|
||||
"storage_url_de_back": "https://agc-storage-dre.cloud.huawei.eu",
|
||||
"storage_url_de": "https://ops-dre.agcstorage.link",
|
||||
"storage_url": "https://agc-storage-drcn.platform.dbankcloud.cn",
|
||||
"storage_url_sg": "https://ops-dra.agcstorage.link",
|
||||
"storage_url_cn_back": "https://agc-storage-drcn.cloud.huawei.com.cn",
|
||||
"storage_url_cn": "https://agc-storage-drcn.platform.dbankcloud.cn"
|
||||
},
|
||||
"ml": {
|
||||
"mlservice_url": "ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
|
||||
}
|
||||
},
|
||||
"region": "DE",
|
||||
"configuration_version": "3.0",
|
||||
"appInfos": [
|
||||
{
|
||||
"package_name": "io.github.wulkanowy.dev",
|
||||
"client": {
|
||||
"app_id": "106552551"
|
||||
},
|
||||
"app_info": {
|
||||
"package_name": "io.github.wulkanowy.dev",
|
||||
"app_id": "106552551"
|
||||
},
|
||||
"oauth_client": {
|
||||
"client_type": 1,
|
||||
"client_id": "106552551"
|
||||
}
|
||||
}
|
||||
]
|
||||
"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"
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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)
|
|
@ -8,7 +8,15 @@ import javax.inject.Singleton
|
|||
@Suppress("UNUSED_PARAMETER")
|
||||
class AnalyticsHelper @Inject constructor() {
|
||||
|
||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) = Unit
|
||||
fun setCurrentScreen(activity: Activity, name: String?) = Unit
|
||||
fun popCurrentScreen(name: String?) = Unit
|
||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fun setCurrentScreen(activity: Activity, name: String?) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fun popCurrentScreen(name: String?) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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)
|
|
@ -3,38 +3,26 @@ package io.github.wulkanowy.utils
|
|||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import com.huawei.agconnect.crash.AGConnectCrash
|
||||
import com.huawei.hms.analytics.HiAnalytics
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AnalyticsHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
preferencesRepository: PreferencesRepository,
|
||||
appInfo: AppInfo,
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
||||
private val analytics by lazy { HiAnalytics.getInstance(context) }
|
||||
|
||||
private val connectCrash by lazy { AGConnectCrash.getInstance() }
|
||||
|
||||
init {
|
||||
if (!appInfo.isDebug) {
|
||||
connectCrash.setUserId(preferencesRepository.installationId)
|
||||
}
|
||||
}
|
||||
|
||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||
Bundle().apply {
|
||||
params.forEach { (key, value) ->
|
||||
if (value == null) return@forEach
|
||||
when (value) {
|
||||
is String -> putString(key, value)
|
||||
is Int -> putInt(key, value)
|
||||
is Boolean -> putBoolean(key, value)
|
||||
params.forEach {
|
||||
if (it.second == null) return@forEach
|
||||
when (it.second) {
|
||||
is String, is String? -> putString(it.first, it.second as String)
|
||||
is Int, is Int? -> putInt(it.first, it.second as Int)
|
||||
is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
|
||||
}
|
||||
}
|
||||
analytics.onEvent(name, this)
|
||||
|
|
|
@ -3,7 +3,6 @@ package io.github.wulkanowy.utils
|
|||
import android.util.Log
|
||||
import com.huawei.agconnect.crash.AGConnectCrash
|
||||
import fr.bipi.tressence.base.FormatterPriorityTree
|
||||
import fr.bipi.tressence.common.StackTraceRecorder
|
||||
|
||||
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
|
||||
|
||||
|
@ -23,10 +22,16 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter)
|
|||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||
if (skipLog(priority, tag, message, t)) return
|
||||
|
||||
// Disabled due to a bug in the Huawei library
|
||||
|
||||
/*connectCrash.setCustomKey("priority", priority)
|
||||
connectCrash.setCustomKey("tag", tag.orEmpty())
|
||||
connectCrash.setCustomKey("message", message)
|
||||
|
||||
if (t != null) {
|
||||
connectCrash.recordException(t)
|
||||
} else {
|
||||
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import javax.inject.Inject
|
|||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Suppress("UNUSED_PARAMETER", "unused")
|
||||
class InAppReviewHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?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,14 +31,10 @@ 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,6 +19,7 @@ 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
|
||||
|
@ -109,6 +110,7 @@ internal class DataModule {
|
|||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideFlowSharedPref(sharedPreferences: SharedPreferences) =
|
||||
|
@ -195,7 +197,7 @@ internal class DataModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideMailboxesDao(database: AppDatabase) = database.mailboxDao
|
||||
fun provideReportingUnitDao(database: AppDatabase) = database.reportingUnitDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
|
|
@ -49,8 +49,8 @@ fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
|||
|
||||
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||
val description = when (it) {
|
||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||
is Resource.Loading -> "started"
|
||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
||||
is Resource.Error -> "exception occurred: ${it.error}"
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import javax.inject.Singleton
|
|||
Subject::class,
|
||||
LuckyNumber::class,
|
||||
CompletedLesson::class,
|
||||
Mailbox::class,
|
||||
ReportingUnit::class,
|
||||
Recipient::class,
|
||||
MobileDevice::class,
|
||||
Teacher::class,
|
||||
|
@ -47,7 +47,6 @@ import javax.inject.Singleton
|
|||
AutoMigration(from = 44, to = 45),
|
||||
AutoMigration(from = 46, to = 47),
|
||||
AutoMigration(from = 47, to = 48),
|
||||
AutoMigration(from = 51, to = 52),
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
|
@ -56,7 +55,7 @@ import javax.inject.Singleton
|
|||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 54
|
||||
const val VERSION_SCHEMA = 48
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
|
@ -103,11 +102,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
Migration43(),
|
||||
Migration44(),
|
||||
Migration46(),
|
||||
Migration49(),
|
||||
Migration50(),
|
||||
Migration51(),
|
||||
Migration53(),
|
||||
Migration54(),
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
|
@ -158,7 +152,7 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
|
||||
abstract val completedLessonsDao: CompletedLessonsDao
|
||||
|
||||
abstract val mailboxDao: MailboxDao
|
||||
abstract val reportingUnitDao: ReportingUnitDao
|
||||
|
||||
abstract val recipientDao: RecipientDao
|
||||
|
||||
|
|
|
@ -2,12 +2,11 @@ package io.github.wulkanowy.data.db.dao
|
|||
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Update
|
||||
|
||||
interface BaseDao<T> {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
@Insert
|
||||
suspend fun insertAll(items: List<T>): List<Long>
|
||||
|
||||
@Update
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface MailboxDao : BaseDao<Mailbox> {
|
||||
|
||||
@Query("SELECT * FROM Mailboxes WHERE email = :email")
|
||||
suspend fun loadAll(email: String): List<Mailbox>
|
||||
|
||||
@Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId")
|
||||
fun loadAll(email: String, symbol: String, schoolId: String): Flow<List<Mailbox>>
|
||||
}
|
|
@ -11,12 +11,9 @@ import kotlinx.coroutines.flow.Flow
|
|||
interface MessagesDao : BaseDao<Message> {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Messages WHERE message_global_key = :messageGlobalKey")
|
||||
fun loadMessageWithAttachment(messageGlobalKey: String): Flow<MessageWithAttachment?>
|
||||
@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 mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
||||
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
||||
|
||||
@Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
|
||||
fun loadAll(folder: Int, email: String): Flow<List<Message>>
|
||||
@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>>
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@ import kotlinx.coroutines.flow.Flow
|
|||
@Dao
|
||||
interface MobileDeviceDao : BaseDao<MobileDevice> {
|
||||
|
||||
@Query("SELECT * FROM MobileDevices WHERE user_login_id = :userLoginId ORDER BY date DESC")
|
||||
@Query("SELECT * FROM MobileDevices WHERE student_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<MobileDevice>>
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ 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
|
||||
|
||||
|
@ -10,6 +9,6 @@ import javax.inject.Singleton
|
|||
@Dao
|
||||
interface RecipientDao : BaseDao<Recipient> {
|
||||
|
||||
@Query("SELECT * FROM Recipients WHERE type = :type AND studentMailboxGlobalKey = :studentMailboxGlobalKey")
|
||||
suspend fun loadAll(type: MailboxType, studentMailboxGlobalKey: String): List<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>
|
||||
}
|
||||
|
|
|
@ -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.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 user_login_id = :userLoginId ORDER BY date DESC")
|
||||
fun loadAll(userLoginId: Int): Flow<List<SchoolAnnouncement>>
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC")
|
||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||
}
|
||||
|
|
|
@ -13,7 +13,4 @@ interface TimetableDao : BaseDao<Timetable> {
|
|||
|
||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
||||
|
||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "Mailboxes")
|
||||
data class Mailbox(
|
||||
|
||||
@PrimaryKey
|
||||
val globalKey: String,
|
||||
|
||||
val email: String,
|
||||
val symbol: String,
|
||||
val schoolId: String,
|
||||
|
||||
val fullName: String,
|
||||
val userName: String,
|
||||
val studentName: String,
|
||||
val schoolNameShort: String,
|
||||
val type: MailboxType,
|
||||
) : java.io.Serializable, Parcelable
|
||||
|
||||
enum class MailboxType {
|
||||
STUDENT,
|
||||
PARENT,
|
||||
GUARDIAN,
|
||||
EMPLOYEE,
|
||||
UNKNOWN,
|
||||
}
|
|
@ -9,19 +9,23 @@ import java.time.Instant
|
|||
@Entity(tableName = "Messages")
|
||||
data class Message(
|
||||
|
||||
@ColumnInfo(name = "email")
|
||||
val email: String,
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Long,
|
||||
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
|
||||
@ColumnInfo(name = "mailbox_key")
|
||||
val mailboxKey: String,
|
||||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
||||
@ColumnInfo(name = "message_id")
|
||||
val messageId: Int,
|
||||
|
||||
val correspondents: String,
|
||||
@ColumnInfo(name = "sender_name")
|
||||
val sender: String,
|
||||
|
||||
@ColumnInfo(name = "sender_id")
|
||||
val senderId: Int,
|
||||
|
||||
@ColumnInfo(name = "recipient_name")
|
||||
val recipient: String,
|
||||
|
||||
val subject: String,
|
||||
|
||||
|
@ -32,11 +36,7 @@ data class Message(
|
|||
|
||||
var unread: Boolean,
|
||||
|
||||
@ColumnInfo(name = "read_by")
|
||||
val readBy: Int?,
|
||||
|
||||
@ColumnInfo(name = "unread_by")
|
||||
val unreadBy: Int?,
|
||||
val removed: Boolean,
|
||||
|
||||
@ColumnInfo(name = "has_attachments")
|
||||
val hasAttachments: Boolean
|
||||
|
@ -48,7 +48,11 @@ 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,8 +12,11 @@ data class MessageAttachment(
|
|||
@ColumnInfo(name = "real_id")
|
||||
val realId: Int,
|
||||
|
||||
@ColumnInfo(name = "message_global_key")
|
||||
val messageGlobalKey: String,
|
||||
@ColumnInfo(name = "message_id")
|
||||
val messageId: Int,
|
||||
|
||||
@ColumnInfo(name = "one_drive_id")
|
||||
val oneDriveId: String,
|
||||
|
||||
@ColumnInfo(name = "url")
|
||||
val url: String,
|
||||
|
|
|
@ -7,6 +7,6 @@ data class MessageWithAttachment(
|
|||
@Embedded
|
||||
val message: Message,
|
||||
|
||||
@Relation(parentColumn = "message_global_key", entityColumn = "message_global_key")
|
||||
@Relation(parentColumn = "message_id", entityColumn = "message_id")
|
||||
val attachments: List<MessageAttachment>
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.time.Instant
|
|||
@Entity(tableName = "MobileDevices")
|
||||
data class MobileDevice(
|
||||
|
||||
@ColumnInfo(name = "user_login_id")
|
||||
@ColumnInfo(name = "student_id")
|
||||
val userLoginId: Int,
|
||||
|
||||
@ColumnInfo(name = "device_id")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
|
@ -7,16 +8,32 @@ import java.io.Serializable
|
|||
@kotlinx.serialization.Serializable
|
||||
@Entity(tableName = "Recipients")
|
||||
data class Recipient(
|
||||
val mailboxGlobalKey: String,
|
||||
val studentMailboxGlobalKey: String,
|
||||
val fullName: String,
|
||||
val userName: String,
|
||||
val schoolShortName: String,
|
||||
val type: MailboxType,
|
||||
|
||||
@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
|
||||
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
override fun toString() = userName
|
||||
override fun toString() = name
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
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 = "user_login_id")
|
||||
val userLoginId: Int,
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
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()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
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")
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration53 : Migration(52, 53) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
createMailboxTable(database)
|
||||
recreateMessagesTable(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,
|
||||
`email` TEXT NOT NULL,
|
||||
`symbol` TEXT NOT NULL,
|
||||
`schoolId` TEXT NOT NULL,
|
||||
`fullName` TEXT NOT NULL,
|
||||
`userName` TEXT 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` (
|
||||
`email` TEXT NOT NULL,
|
||||
`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,
|
||||
`read_by` INTEGER,
|
||||
`unread_by` INTEGER,
|
||||
`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()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration54 : Migration(53, 54) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
migrateResman(database)
|
||||
removeTomaszowMazowieckiStudents(database)
|
||||
}
|
||||
|
||||
private fun migrateResman(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("""
|
||||
UPDATE Students SET
|
||||
scrapper_base_url = 'https://vulcan.net.pl',
|
||||
login_type = 'ADFSLightScoped',
|
||||
symbol = 'rzeszowprojekt'
|
||||
WHERE scrapper_base_url = 'https://resman.pl'
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
private fun removeTomaszowMazowieckiStudents(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'")
|
||||
}
|
||||
}
|
|
@ -2,8 +2,7 @@ package io.github.wulkanowy.data.enums
|
|||
|
||||
enum class GradeSortingMode(val value: String) {
|
||||
ALPHABETIC("alphabetic"),
|
||||
DATE("date"),
|
||||
AVERAGE("average");
|
||||
DATE("date");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC
|
||||
|
|
|
@ -3,22 +3,17 @@ package io.github.wulkanowy.data.mappers
|
|||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
||||
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
|
||||
|
||||
fun List<SdkAttendance>.mapToEntities(semester: Semester, lessons: List<Timetable>) = map {
|
||||
fun List<SdkAttendance>.mapToEntities(semester: Semester) = map {
|
||||
Attendance(
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
date = it.date,
|
||||
timeId = it.timeId,
|
||||
number = it.number,
|
||||
subject = it.subject.ifBlank {
|
||||
lessons.find { lesson ->
|
||||
lesson.date == it.date && lesson.number == it.number
|
||||
}?.subject.orEmpty()
|
||||
},
|
||||
subject = it.subject,
|
||||
name = it.name,
|
||||
presence = it.presence,
|
||||
absence = it.absence,
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
|
|||
|
||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
userLoginId = student.userLoginId,
|
||||
studentId = student.userLoginId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
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,
|
||||
studentName = it.studentName,
|
||||
schoolNameShort = it.schoolNameShort,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
email = student.email,
|
||||
symbol = student.symbol,
|
||||
schoolId = student.schoolSymbol,
|
||||
)
|
||||
}
|
|
@ -1,46 +1,40 @@
|
|||
package io.github.wulkanowy.data.mappers
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.sdk.pojo.MailboxType
|
||||
import timber.log.Timber
|
||||
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.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,
|
||||
mailbox: Mailbox?,
|
||||
allMailboxes: List<Mailbox>
|
||||
): List<Message> = map {
|
||||
fun List<SdkMessage>.mapToEntities(student: Student) = map {
|
||||
Message(
|
||||
messageGlobalKey = it.globalKey,
|
||||
mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
|
||||
box.fullName == it.mailbox
|
||||
}?.globalKey.let { mailboxKey ->
|
||||
if (mailboxKey == null) {
|
||||
Timber.e("Can't find ${it.mailbox} in $allMailboxes")
|
||||
"unknown"
|
||||
} else mailboxKey
|
||||
},
|
||||
email = student.email,
|
||||
messageId = it.id,
|
||||
correspondents = it.correspondents,
|
||||
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",
|
||||
subject = it.subject.trim(),
|
||||
date = it.dateZoned.toInstant(),
|
||||
date = it.dateZoned?.toInstant() ?: Instant.now(),
|
||||
folderId = it.folderId,
|
||||
unread = it.unread,
|
||||
unreadBy = it.unreadBy,
|
||||
readBy = it.readBy,
|
||||
hasAttachments = it.hasAttachments,
|
||||
unread = it.unread ?: false,
|
||||
removed = it.removed,
|
||||
hasAttachments = it.hasAttachments
|
||||
).apply {
|
||||
content = it.content.orEmpty()
|
||||
unreadBy = it.unreadBy ?: 0
|
||||
readBy = it.readBy ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
||||
fun List<SdkMessageAttachment>.mapToEntities() = map {
|
||||
MessageAttachment(
|
||||
messageGlobalKey = messageGlobalKey,
|
||||
realId = it.url.hashCode(),
|
||||
realId = it.id,
|
||||
messageId = it.messageId,
|
||||
oneDriveId = it.oneDriveId,
|
||||
url = it.url,
|
||||
filename = it.filename
|
||||
)
|
||||
|
@ -48,11 +42,12 @@ fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
|||
|
||||
fun List<Recipient>.mapFromEntities() = map {
|
||||
SdkRecipient(
|
||||
fullName = it.fullName,
|
||||
userName = it.userName,
|
||||
studentName = it.userName,
|
||||
mailboxGlobalKey = it.mailboxGlobalKey,
|
||||
schoolNameShort = it.schoolShortName,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
id = it.realId,
|
||||
name = it.realName,
|
||||
loginId = it.loginId,
|
||||
reportingUnitId = it.unitId,
|
||||
role = it.role,
|
||||
hash = it.hash,
|
||||
shortName = it.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.Student
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
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(student: Student) = map {
|
||||
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
|
||||
MobileDevice(
|
||||
userLoginId = student.userLoginId,
|
||||
userLoginId = semester.studentId,
|
||||
date = it.createDateZoned.toInstant(),
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
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(studentMailboxGlobalKey: String) = map {
|
||||
fun List<SdkRecipient>.mapToEntities(userLoginId: Int) = map {
|
||||
Recipient(
|
||||
mailboxGlobalKey = it.mailboxGlobalKey,
|
||||
fullName = it.fullName,
|
||||
userName = it.userName,
|
||||
studentMailboxGlobalKey = studentMailboxGlobalKey,
|
||||
schoolShortName = it.schoolNameShort,
|
||||
type = MailboxType.valueOf(it.type.name),
|
||||
studentId = userLoginId,
|
||||
realId = it.id,
|
||||
realName = it.name,
|
||||
name = it.shortName,
|
||||
hash = it.hash,
|
||||
loginId = it.loginId,
|
||||
role = it.role,
|
||||
unitId = it.reportingUnitId ?: 0
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
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
|
||||
)
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
|
@ -10,10 +9,8 @@ import io.github.wulkanowy.data.networkBoundResource
|
|||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Absent
|
||||
import io.github.wulkanowy.utils.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
|
@ -23,7 +20,6 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class AttendanceRepository @Inject constructor(
|
||||
private val attendanceDb: AttendanceDao,
|
||||
private val timetableDb: TimetableDao,
|
||||
private val sdk: Sdk,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
) {
|
||||
|
@ -52,15 +48,10 @@ class AttendanceRepository @Inject constructor(
|
|||
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||
},
|
||||
fetch = {
|
||||
val lessons = withContext(Dispatchers.IO) {
|
||||
timetableDb.load(
|
||||
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||
)
|
||||
}
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
||||
.mapToEntities(semester, lessons)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||
|
|
|
@ -3,32 +3,31 @@ package io.github.wulkanowy.data.repositories
|
|||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.*
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||
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.domain.messages.GetMailboxByStudentUseCase
|
||||
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
|
||||
|
||||
|
@ -41,18 +40,16 @@ class MessageRepository @Inject constructor(
|
|||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val sharedPrefProvider: SharedPrefProvider,
|
||||
private val json: Json,
|
||||
private val mailboxDao: MailboxDao,
|
||||
private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val messagesCacheKey = "message"
|
||||
private val mailboxCacheKey = "mailboxes"
|
||||
private val cacheKey = "message"
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getMessages(
|
||||
student: Student,
|
||||
mailbox: Mailbox?,
|
||||
semester: Semester,
|
||||
folder: MessageFolder,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
|
@ -61,33 +58,46 @@ class MessageRepository @Inject constructor(
|
|||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(messagesCacheKey, mailbox, folder)
|
||||
key = getRefreshKey(cacheKey, student, folder)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
if (mailbox == null) {
|
||||
messagesDb.loadAll(folder.id, student.email)
|
||||
} else messagesDb.loadAll(mailbox.globalKey, folder.id)
|
||||
},
|
||||
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessages(
|
||||
folder = Folder.valueOf(folder.name),
|
||||
mailboxKey = mailbox?.globalKey,
|
||||
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
|
||||
sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now())
|
||||
.mapToEntities(student)
|
||||
},
|
||||
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(messagesCacheKey, mailbox, folder)
|
||||
)
|
||||
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,
|
||||
|
@ -96,38 +106,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.isBlank()}")
|
||||
it.message.unread || it.message.content.isBlank()
|
||||
},
|
||||
query = {
|
||||
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
|
||||
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||
it.message.unread || it.message.content.isEmpty()
|
||||
},
|
||||
query = { messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId) },
|
||||
fetch = {
|
||||
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey, markAsRead)
|
||||
sdk.init(student).getMessageDetails(
|
||||
messageId = it!!.message.messageId,
|
||||
folderId = message.folderId,
|
||||
read = markAsRead,
|
||||
id = message.realId
|
||||
).let { details ->
|
||||
details.content to details.attachments.mapToEntities()
|
||||
}
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
messagesDb.updateAll(
|
||||
listOf(old.message.apply {
|
||||
id = message.id
|
||||
unread = !markAsRead
|
||||
sender = new.sender
|
||||
recipients = new.recipients.singleOrNull() ?: "Wielu adresatów"
|
||||
content = content.ifBlank { new.content }
|
||||
})
|
||||
)
|
||||
messageAttachmentDao.insertAttachments(
|
||||
items = new.attachments.mapToEntities(message.messageGlobalKey),
|
||||
)
|
||||
|
||||
messagesDb.updateAll(listOf(old.message.apply {
|
||||
id = old.message.id
|
||||
unread = !markAsRead
|
||||
content = content.ifBlank { downloadedMessage }
|
||||
}))
|
||||
messageAttachmentDao.insertAttachments(attachments)
|
||||
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
|
||||
}
|
||||
)
|
||||
|
||||
fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow<List<Message>> {
|
||||
return if (mailbox == null) {
|
||||
messagesDb.loadAll(RECEIVED.id, student.email)
|
||||
} else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
|
||||
fun getMessagesFromDatabase(student: Student): Flow<List<Message>> {
|
||||
return messagesDb.loadAll(student.id.toInt(), RECEIVED.id)
|
||||
}
|
||||
|
||||
suspend fun updateMessages(messages: List<Message>) {
|
||||
|
@ -139,88 +145,38 @@ class MessageRepository @Inject constructor(
|
|||
subject: String,
|
||||
content: String,
|
||||
recipients: List<Recipient>,
|
||||
mailboxId: String,
|
||||
) {
|
||||
sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities(),
|
||||
mailboxId = mailboxId,
|
||||
)
|
||||
}
|
||||
): SentMessage = sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities()
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
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)
|
||||
|
||||
if (firstMessage.folderId != TRASHED.id) {
|
||||
if (folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||
val deletedMessages = messages.map {
|
||||
it.copy(folderId = TRASHED.id)
|
||||
it.copy(folderId = MessageFolder.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, mailbox: Mailbox, message: Message) {
|
||||
deleteMessages(student, mailbox, listOf(message))
|
||||
}
|
||||
|
||||
suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(mailboxCacheKey, student),
|
||||
)
|
||||
it.isEmpty() || isExpired || forceRefresh
|
||||
},
|
||||
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
||||
fetch = {
|
||||
sdk.init(student).getMailboxes().mapToEntities(student)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||
mailboxDao.insertAll(new uniqueSubtract old)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student))
|
||||
}
|
||||
)
|
||||
|
||||
suspend fun getMailboxByStudent(student: Student): Mailbox? {
|
||||
val mailbox = getMailboxByStudentUseCase(student)
|
||||
|
||||
return if (mailbox == null) {
|
||||
getMailboxes(student, forceRefresh = true)
|
||||
.onResourceError { throw it }
|
||||
.onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") }
|
||||
.waitForResult()
|
||||
|
||||
getMailboxByStudentUseCase(student)
|
||||
} else mailbox
|
||||
}
|
||||
suspend fun deleteMessage(student: Student, message: Message) =
|
||||
deleteMessages(student, listOf(message))
|
||||
|
||||
var draftMessage: MessageDraft?
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft))
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
||||
?.let { json.decodeFromString(it) }
|
||||
set(value) = sharedPrefProvider.putString(
|
||||
context.getString(R.string.pref_key_message_draft),
|
||||
context.getString(R.string.pref_key_message_send_draft),
|
||||
value?.let { json.encodeToString(it) }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,12 +39,12 @@ class MobileDeviceRepository @Inject constructor(
|
|||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { mobileDb.loadAll(student.userLoginId) },
|
||||
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getRegisteredDevices()
|
||||
.mapToEntities(student)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
mobileDb.deleteAll(old uniqueSubtract new)
|
||||
|
|
|
@ -10,16 +10,17 @@ import io.github.wulkanowy.R
|
|||
import io.github.wulkanowy.data.enums.*
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
class PreferencesRepository @Inject constructor(
|
||||
@ApplicationContext val context: Context,
|
||||
|
@ -221,31 +222,19 @@ class PreferencesRepository @Inject constructor(
|
|||
get() = selectedDashboardTilesPreference.asFlow()
|
||||
.map { set ->
|
||||
set.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(
|
||||
listOfNotNull(
|
||||
DashboardItem.Tile.ACCOUNT,
|
||||
DashboardItem.Tile.ADMIN_MESSAGE,
|
||||
DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
|
||||
)
|
||||
)
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.toSet()
|
||||
}
|
||||
|
||||
var selectedDashboardTiles: Set<DashboardItem.Tile>
|
||||
get() = selectedDashboardTilesPreference.get()
|
||||
.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(
|
||||
listOfNotNull(
|
||||
DashboardItem.Tile.ACCOUNT,
|
||||
DashboardItem.Tile.ADMIN_MESSAGE,
|
||||
DashboardItem.Tile.ADS.takeIf { isAdsEnabled }
|
||||
)
|
||||
)
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.toSet()
|
||||
set(value) {
|
||||
val filteredValue = value.filterNot {
|
||||
it == DashboardItem.Tile.ACCOUNT || it == DashboardItem.Tile.ADMIN_MESSAGE
|
||||
}
|
||||
val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT }
|
||||
.map { it.name }
|
||||
.toSet()
|
||||
|
||||
|
@ -282,48 +271,7 @@ 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) }
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var installationId: String
|
||||
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
||||
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
||||
|
||||
init {
|
||||
if (installationId.isEmpty()) {
|
||||
installationId = UUID.randomUUID().toString()
|
||||
}
|
||||
}
|
||||
set(value) = sharedPref.edit().putBoolean(PREF_KEY_IN_APP_REVIEW_DONE, value).apply()
|
||||
|
||||
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
|
||||
|
||||
|
@ -340,14 +288,19 @@ class PreferencesRepository @Inject constructor(
|
|||
private fun getBoolean(id: String, default: Int) =
|
||||
sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
||||
|
||||
private fun getBoolean(id: Int, default: Boolean) =
|
||||
sharedPref.getBoolean(context.getString(id), default)
|
||||
|
||||
private companion object {
|
||||
private const val PREF_KEY_INSTALLATION_ID = "installation_id"
|
||||
|
||||
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
|
||||
|
||||
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
|
||||
|
||||
private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date"
|
||||
|
||||
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,7 +1,10 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
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.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
|
@ -20,10 +23,9 @@ class RecipientRepository @Inject constructor(
|
|||
|
||||
private val cacheKey = "recipient"
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
recipientDb.deleteAll(old uniqueSubtract new)
|
||||
recipientDb.insertAll(new uniqueSubtract old)
|
||||
|
@ -31,33 +33,18 @@ class RecipientRepository @Inject constructor(
|
|||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
|
||||
suspend fun getRecipients(
|
||||
student: Student,
|
||||
mailbox: Mailbox?,
|
||||
type: MailboxType,
|
||||
): List<Recipient> {
|
||||
mailbox ?: return emptyList()
|
||||
|
||||
val cached = recipientDb.loadAll(type, mailbox.globalKey)
|
||||
suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List<Recipient> {
|
||||
val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
return if (cached.isEmpty() || isExpired) {
|
||||
refreshRecipients(student, mailbox, type)
|
||||
recipientDb.loadAll(type, mailbox.globalKey)
|
||||
refreshRecipients(student, unit, role)
|
||||
recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
} else cached
|
||||
}
|
||||
|
||||
suspend fun getMessageSender(
|
||||
student: Student,
|
||||
mailbox: Mailbox?,
|
||||
message: Message,
|
||||
): List<Recipient> {
|
||||
mailbox ?: return emptyList()
|
||||
|
||||
return sdk.init(student)
|
||||
.getMessageReplayDetails(message.messageGlobalKey)
|
||||
.sender
|
||||
.let(::listOf)
|
||||
.mapToEntities(mailbox.globalKey)
|
||||
suspend fun getMessageRecipients(student: Student, message: Message): List<Recipient> {
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
|
||||
.mapToEntities(student.studentId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
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,8 +28,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||
|
||||
fun getSchoolAnnouncements(
|
||||
student: Student,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false
|
||||
forceRefresh: Boolean, notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
|
@ -38,7 +37,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
schoolAnnouncementDb.loadAll(student.studentId)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
|
@ -57,7 +56,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||
)
|
||||
|
||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||
return schoolAnnouncementDb.loadAll(student.userLoginId)
|
||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
||||
}
|
||||
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
package io.github.wulkanowy.domain.messages
|
||||
|
||||
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 javax.inject.Inject
|
||||
|
||||
class GetMailboxByStudentUseCase @Inject constructor(
|
||||
private val mailboxDao: MailboxDao,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(student: Student): Mailbox? {
|
||||
return mailboxDao.loadAll(student.email)
|
||||
.filterByStudent(student)
|
||||
}
|
||||
|
||||
private fun List<Mailbox>.filterByStudent(student: Student): Mailbox? {
|
||||
val normalizedStudentName = student.studentName.normalizeStudentName()
|
||||
|
||||
return singleOrNull {
|
||||
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||
} ?: singleOrNull {
|
||||
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||
&& it.schoolNameShort == student.schoolShortName
|
||||
} ?: singleOrNull {
|
||||
it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
|
||||
} ?: singleOrNull {
|
||||
it.studentName.getReversedName() == normalizedStudentName
|
||||
} ?: singleOrNull {
|
||||
it.studentName.getUnauthorizedVersion() == normalizedStudentName
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.normalizeStudentName(): String {
|
||||
return trim().split(" ")
|
||||
.filter { it.isNotBlank() }
|
||||
.joinToString(" ") { part ->
|
||||
part.lowercase().replaceFirstChar { it.uppercase() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.getFirstAndLastPart(): String {
|
||||
val parts = normalizeStudentName().split(" ")
|
||||
|
||||
val endParts = parts.filterIndexed { i, _ ->
|
||||
i == 0 || parts.size - 1 == i
|
||||
}
|
||||
return endParts.joinToString(" ")
|
||||
}
|
||||
|
||||
private fun String.getReversedName(): String {
|
||||
val parts = normalizeStudentName().split(" ")
|
||||
|
||||
return parts
|
||||
.asReversed()
|
||||
.joinToString(" ")
|
||||
}
|
||||
|
||||
private fun String.getUnauthorizedVersion(): String {
|
||||
return normalizeStudentName().split(" ")
|
||||
.joinToString(" ") {
|
||||
it.first() + "*".repeat(it.length - 1)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,41 +15,79 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
|
||||
|
||||
fun initializeShortcuts() {
|
||||
// Destination cannot be used here as shortcuts
|
||||
// require their intents to only use primitive types (see PersistableBundle.isValidType).
|
||||
|
||||
private val destinations = mapOf(
|
||||
"grade" to Destination.Grade,
|
||||
"attendance" to Destination.Attendance,
|
||||
"exam" to Destination.Exam,
|
||||
"timetable" to Destination.Timetable()
|
||||
)
|
||||
|
||||
init {
|
||||
initializeShortcuts()
|
||||
}
|
||||
|
||||
fun getDestination(intent: Intent) =
|
||||
destinations[intent.getStringExtra(EXTRA_SHORTCUT_DESTINATION_ID)]
|
||||
|
||||
private fun initializeShortcuts() {
|
||||
val shortcutsInfo = listOf(
|
||||
ShortcutInfoCompat.Builder(context, "grade_shortcut")
|
||||
.setShortLabel(context.getString(R.string.grade_title))
|
||||
.setLongLabel(context.getString(R.string.grade_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_grade))
|
||||
.setIntent(SplashActivity.getStartIntent(context, Destination.Grade)
|
||||
.apply { action = Intent.ACTION_VIEW })
|
||||
.setIntent(SplashActivity.getStartIntent(context)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "grade")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "attendance_shortcut")
|
||||
.setShortLabel(context.getString(R.string.attendance_title))
|
||||
.setLongLabel(context.getString(R.string.attendance_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_attendance))
|
||||
.setIntent(SplashActivity.getStartIntent(context, Destination.Attendance)
|
||||
.apply { action = Intent.ACTION_VIEW })
|
||||
.setIntent(SplashActivity.getStartIntent(context)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "attendance")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "exam_shortcut")
|
||||
.setShortLabel(context.getString(R.string.exam_title))
|
||||
.setLongLabel(context.getString(R.string.exam_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_exam))
|
||||
.setIntent(SplashActivity.getStartIntent(context, Destination.Exam)
|
||||
.apply { action = Intent.ACTION_VIEW })
|
||||
.setIntent(SplashActivity.getStartIntent(context)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "exam")
|
||||
}
|
||||
)
|
||||
.build(),
|
||||
|
||||
ShortcutInfoCompat.Builder(context, "timetable_shortcut")
|
||||
.setShortLabel(context.getString(R.string.timetable_title))
|
||||
.setLongLabel(context.getString(R.string.timetable_title))
|
||||
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut_timetable))
|
||||
.setIntent(SplashActivity.getStartIntent(context, Destination.Timetable())
|
||||
.apply { action = Intent.ACTION_VIEW })
|
||||
.setIntent(SplashActivity.getStartIntent(context)
|
||||
.apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
putExtra(EXTRA_SHORTCUT_DESTINATION_ID, "timetable")
|
||||
}
|
||||
)
|
||||
.build()
|
||||
)
|
||||
|
||||
shortcutsInfo.forEach { ShortcutManagerCompat.pushDynamicShortcut(context, it) }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
private const val EXTRA_SHORTCUT_DESTINATION_ID = "shortcut_destination_id"
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||
import io.github.wulkanowy.utils.descriptionRes
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import io.github.wulkanowy.utils.toFormattedString
|
||||
|
@ -21,9 +22,8 @@ class NewAttendanceNotification @Inject constructor(
|
|||
suspend fun notify(items: List<Attendance>, student: Student) {
|
||||
val lines = items.filterNot { it.presence || it.name == "UNKNOWN" }
|
||||
.map {
|
||||
val lesson = it.subject.ifBlank { "Lekcja ${it.number}" }
|
||||
val description = context.getString(it.descriptionRes)
|
||||
"${it.date.toFormattedString("dd.MM")} - $lesson: $description"
|
||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description"
|
||||
}
|
||||
.ifEmpty { return }
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||
import io.github.wulkanowy.data.pojos.NotificationData
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||
import io.github.wulkanowy.utils.getPlural
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -20,7 +21,7 @@ class NewMessageNotification @Inject constructor(
|
|||
val notificationDataList = items.map {
|
||||
NotificationData(
|
||||
title = context.getPlural(R.plurals.message_new_items, 1),
|
||||
content = "${it.correspondents}: ${it.subject}",
|
||||
content = "${it.sender}: ${it.subject}",
|
||||
destination = Destination.Message,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -15,16 +15,15 @@ class MessageWork @Inject constructor(
|
|||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||
messageRepository.getMessages(
|
||||
student = student,
|
||||
mailbox = mailbox,
|
||||
semester = semester,
|
||||
folder = RECEIVED,
|
||||
forceRefresh = true,
|
||||
notify = notify
|
||||
).waitForResult()
|
||||
|
||||
messageRepository.getMessagesFromDatabase(student, mailbox).first()
|
||||
messageRepository.getMessagesFromDatabase(student).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,23 @@
|
|||
package io.github.wulkanowy.services.sync.works
|
||||
|
||||
import io.github.wulkanowy.data.dataOrNull
|
||||
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.MessageRepository
|
||||
import io.github.wulkanowy.data.repositories.RecipientRepository
|
||||
import io.github.wulkanowy.data.toFirstResult
|
||||
import io.github.wulkanowy.data.repositories.ReportingUnitRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class RecipientWork @Inject constructor(
|
||||
private val messageRepository: MessageRepository,
|
||||
private val reportingUnitRepository: ReportingUnitRepository,
|
||||
private val recipientRepository: RecipientRepository
|
||||
) : Work {
|
||||
|
||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||
val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult()
|
||||
mailboxes.dataOrNull?.forEach {
|
||||
recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE)
|
||||
reportingUnitRepository.refreshReportingUnits(student)
|
||||
|
||||
reportingUnitRepository.getReportingUnits(student).let { units ->
|
||||
units.map {
|
||||
recipientRepository.refreshRecipients(student, it, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ 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(
|
||||
|
@ -21,13 +20,10 @@ class SchoolAnnouncementWork @Inject constructor(
|
|||
notify = notify,
|
||||
).waitForResult()
|
||||
|
||||
schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student)
|
||||
.first()
|
||||
.filter { !it.isNotified && it.date >= LocalDate.now() }
|
||||
.let {
|
||||
if (it.isNotEmpty()) {
|
||||
newSchoolAnnouncementNotification.notify(it, student)
|
||||
}
|
||||
|
||||
schoolAnnouncementRepository.getSchoolAnnouncementFromDatabase(student).first()
|
||||
.filter { !it.isNotified }.let {
|
||||
if (it.isNotEmpty()) newSchoolAnnouncementNotification.notify(it, student)
|
||||
|
||||
schoolAnnouncementRepository.updateSchoolAnnouncement(it.onEach { schoolAnnouncement ->
|
||||
schoolAnnouncement.isNotified = true
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Dialog
|
|||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import android.widget.Toast.LENGTH_LONG
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
|
@ -14,7 +15,6 @@ import androidx.fragment.app.DialogFragment
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.DialogErrorBinding
|
||||
import io.github.wulkanowy.utils.*
|
||||
import javax.inject.Inject
|
||||
|
@ -25,9 +25,6 @@ class ErrorDialog : DialogFragment() {
|
|||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
private const val ARGUMENT_KEY = "error"
|
||||
|
||||
|
@ -39,7 +36,7 @@ class ErrorDialog : DialogFragment() {
|
|||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
|
||||
|
||||
val binding = DialogErrorBinding.inflate(layoutInflater)
|
||||
val binding = DialogErrorBinding.inflate(LayoutInflater.from(context))
|
||||
binding.bindErrorDetails(error)
|
||||
|
||||
return getAlertDialog(binding, error).apply {
|
||||
|
@ -102,8 +99,7 @@ class ErrorDialog : DialogFragment() {
|
|||
R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||
preferencesRepository.installationId,
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||
) + "\n" + content,
|
||||
onActivityNotFound = {
|
||||
requireContext().openInternetBrowser(
|
||||
|
|
|
@ -19,15 +19,15 @@ import kotlinx.serialization.Serializable
|
|||
import java.time.LocalDate
|
||||
|
||||
@Serializable
|
||||
sealed class Destination {
|
||||
sealed class Destination private constructor() : java.io.Serializable {
|
||||
|
||||
/*
|
||||
Type in children classes have to be as getter to avoid null in enums
|
||||
https://stackoverflow.com/questions/68866453/kotlin-enum-val-is-returning-null-despite-being-set-at-compile-time
|
||||
*/
|
||||
abstract val destinationType: Type
|
||||
abstract val type: Type
|
||||
|
||||
abstract val destinationFragment: Fragment
|
||||
abstract val fragment: Fragment
|
||||
|
||||
enum class Type(val defaultDestination: Destination) {
|
||||
DASHBOARD(Dashboard),
|
||||
|
@ -47,26 +47,26 @@ sealed class Destination {
|
|||
|
||||
@Serializable
|
||||
object Dashboard : Destination() {
|
||||
override val destinationType get() = Type.DASHBOARD
|
||||
override val destinationFragment get() = DashboardFragment.newInstance()
|
||||
override val type get() = Type.DASHBOARD
|
||||
override val fragment get() = DashboardFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Grade : Destination() {
|
||||
override val destinationType get() = Type.GRADE
|
||||
override val destinationFragment get() = GradeFragment.newInstance()
|
||||
override val type get() = Type.GRADE
|
||||
override val fragment get() = GradeFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Attendance : Destination() {
|
||||
override val destinationType get() = Type.ATTENDANCE
|
||||
override val destinationFragment get() = AttendanceFragment.newInstance()
|
||||
override val type get() = Type.ATTENDANCE
|
||||
override val fragment get() = AttendanceFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Exam : Destination() {
|
||||
override val destinationType get() = Type.EXAM
|
||||
override val destinationFragment get() = ExamFragment.newInstance()
|
||||
override val type get() = Type.EXAM
|
||||
override val fragment get() = ExamFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
|
@ -74,55 +74,55 @@ sealed class Destination {
|
|||
@Serializable(with = LocalDateSerializer::class)
|
||||
private val date: LocalDate? = null
|
||||
) : Destination() {
|
||||
override val destinationType get() = Type.TIMETABLE
|
||||
override val destinationFragment get() = TimetableFragment.newInstance(date)
|
||||
override val type get() = Type.TIMETABLE
|
||||
override val fragment get() = TimetableFragment.newInstance(date)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Homework : Destination() {
|
||||
override val destinationType get() = Type.HOMEWORK
|
||||
override val destinationFragment get() = HomeworkFragment.newInstance()
|
||||
override val type get() = Type.HOMEWORK
|
||||
override val fragment get() = HomeworkFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Note : Destination() {
|
||||
override val destinationType get() = Type.NOTE
|
||||
override val destinationFragment get() = NoteFragment.newInstance()
|
||||
override val type get() = Type.NOTE
|
||||
override val fragment get() = NoteFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Conference : Destination() {
|
||||
override val destinationType get() = Type.CONFERENCE
|
||||
override val destinationFragment get() = ConferenceFragment.newInstance()
|
||||
override val type get() = Type.CONFERENCE
|
||||
override val fragment get() = ConferenceFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object SchoolAnnouncement : Destination() {
|
||||
override val destinationType get() = Type.SCHOOL_ANNOUNCEMENT
|
||||
override val destinationFragment get() = SchoolAnnouncementFragment.newInstance()
|
||||
override val type get() = Type.SCHOOL_ANNOUNCEMENT
|
||||
override val fragment get() = SchoolAnnouncementFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object School : Destination() {
|
||||
override val destinationType get() = Type.SCHOOL
|
||||
override val destinationFragment get() = SchoolFragment.newInstance()
|
||||
override val type get() = Type.SCHOOL
|
||||
override val fragment get() = SchoolFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object LuckyNumber : Destination() {
|
||||
override val destinationType get() = Type.LUCKY_NUMBER
|
||||
override val destinationFragment get() = LuckyNumberFragment.newInstance()
|
||||
override val type get() = Type.LUCKY_NUMBER
|
||||
override val fragment get() = LuckyNumberFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object More : Destination() {
|
||||
override val destinationType get() = Type.MORE
|
||||
override val destinationFragment get() = MoreFragment.newInstance()
|
||||
override val type get() = Type.MORE
|
||||
override val fragment get() = MoreFragment.newInstance()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
object Message : Destination() {
|
||||
override val destinationType get() = Type.MESSAGE
|
||||
override val destinationFragment get() = MessageFragment.newInstance()
|
||||
override val type get() = Type.MESSAGE
|
||||
override val fragment get() = MessageFragment.newInstance()
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@ import android.view.View
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentAboutBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
||||
|
@ -31,9 +30,6 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
override val versionRes: Triple<String, String, Drawable?>?
|
||||
get() = context?.run {
|
||||
val buildTimestamp =
|
||||
|
@ -189,8 +185,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||
R.string.about_feedback_template,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||
preferencesRepository.installationId,
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
||||
),
|
||||
onActivityNotFound = {
|
||||
requireContext().openInternetBrowser(
|
||||
|
|
|
@ -23,9 +23,8 @@ class LicenseAdapter @Inject constructor() : RecyclerView.Adapter<LicenseAdapter
|
|||
val item = items[position]
|
||||
|
||||
with(holder.binding) {
|
||||
licenseItemName.text = item.name
|
||||
licenseItemSummary.text = item.licenses.firstOrNull()?.name?.takeIf { it.isNotBlank() }
|
||||
?: item.artifactVersion
|
||||
licenseItemName.text = item.libraryName
|
||||
licenseItemSummary.text = item.licenses?.firstOrNull()?.licenseName?.takeIf { it.isNotBlank() } ?: item.libraryVersion
|
||||
|
||||
root.setOnClickListener { onClickListener(item) }
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.entity.Library
|
||||
import com.mikepenz.aboutlibraries.util.withContext
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.databinding.FragmentLicenseBinding
|
||||
|
@ -29,9 +28,7 @@ class LicenseFragment : BaseFragment<FragmentLicenseBinding>(R.layout.fragment_l
|
|||
|
||||
override val titleStringId get() = R.string.license_title
|
||||
|
||||
override val appLibraries by lazy {
|
||||
Libs.Builder().withContext(requireContext()).build().libraries
|
||||
}
|
||||
override val appLibraries by lazy { Libs(requireContext()).libraries }
|
||||
|
||||
companion object {
|
||||
fun newInstance() = LicenseFragment()
|
||||
|
|
|
@ -22,7 +22,7 @@ class LicensePresenter @Inject constructor(
|
|||
}
|
||||
|
||||
fun onItemSelected(library: Library) {
|
||||
view?.run { library.licenses.firstOrNull()?.licenseContent?.let { openLicense(it) } }
|
||||
view?.run { library.licenses?.firstOrNull()?.licenseDescription?.let { openLicense(it) } }
|
||||
}
|
||||
|
||||
private fun loadData() {
|
||||
|
|
|
@ -35,11 +35,9 @@ class AttendanceAdapter @Inject constructor() :
|
|||
|
||||
with(holder.binding) {
|
||||
attendanceItemNumber.text = item.number.toString()
|
||||
attendanceItemSubject.text = item.subject.ifBlank {
|
||||
root.context.getString(R.string.all_no_data)
|
||||
}
|
||||
attendanceItemSubject.text = item.subject
|
||||
attendanceItemDescription.setText(item.descriptionRes)
|
||||
attendanceItemAlert.isVisible = item.let { it.absence && !it.excused }
|
||||
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
|
||||
attendanceItemNumber.visibility = View.GONE
|
||||
attendanceItemExcuseInfo.visibility = View.GONE
|
||||
attendanceItemExcuseCheckbox.visibility = View.GONE
|
||||
|
@ -48,7 +46,7 @@ class AttendanceAdapter @Inject constructor() :
|
|||
onExcuseCheckboxSelect(item, checked)
|
||||
}
|
||||
|
||||
when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it) }) {
|
||||
when (item.excuseStatus?.let { SentExcuseStatus.valueOf(it)}) {
|
||||
SentExcuseStatus.WAITING -> {
|
||||
attendanceItemExcuseInfo.setImageResource(R.drawable.ic_excuse_waiting)
|
||||
attendanceItemExcuseInfo.visibility = View.VISIBLE
|
||||
|
|
|
@ -91,19 +91,15 @@ class AttendancePresenter @Inject constructor(
|
|||
|
||||
fun onViewReselected() {
|
||||
Timber.i("Attendance view is reselected")
|
||||
view?.let { view ->
|
||||
view?.also { view ->
|
||||
if (view.currentStackSize == 1) {
|
||||
baseDate = now().previousOrSameSchoolDay
|
||||
|
||||
if (currentDate != baseDate) {
|
||||
reloadView(baseDate)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) {
|
||||
view.resetView()
|
||||
baseDate.also {
|
||||
if (currentDate != it) {
|
||||
reloadView(it)
|
||||
loadData()
|
||||
} else if (!view.isViewEmpty) view.resetView()
|
||||
}
|
||||
} else {
|
||||
view.popView()
|
||||
}
|
||||
} else view.popView()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
|
@ -9,7 +9,6 @@ import android.os.Looper
|
|||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updateMarginsRelative
|
||||
|
@ -22,15 +21,24 @@ 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.*
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import io.github.wulkanowy.utils.*
|
||||
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 timber.log.Timber
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.util.*
|
||||
import java.time.*
|
||||
import java.util.Timer
|
||||
import javax.inject.Inject
|
||||
import kotlin.concurrent.timer
|
||||
|
||||
|
@ -111,9 +119,6 @@ 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()
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +134,6 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,105 +175,81 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||
position: Int
|
||||
) {
|
||||
val item = items[position] as DashboardItem.HorizontalGroup
|
||||
val unreadMessagesCount = item.unreadMessagesCount
|
||||
val attendancePercentage = item.attendancePercentage
|
||||
val luckyNumber = item.luckyNumber
|
||||
val error = item.error
|
||||
val isLoading = item.isLoading
|
||||
val binding = horizontalGroupViewHolder.binding
|
||||
val context = binding.root.context
|
||||
val isLoadingVisible =
|
||||
(item.isLoading && !item.isDataLoaded) || (item.isLoading && !item.isFullDataLoaded)
|
||||
val isWideErrorShow = isLoadingVisible || item.error != null
|
||||
|
||||
with(horizontalGroupViewHolder.binding) {
|
||||
dashboardHorizontalGroupItemInfoContainer.isVisible = isWideErrorShow
|
||||
dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible
|
||||
dashboardHorizontalGroupItemInfoErrorText.isVisible = item.error != null
|
||||
|
||||
bindLuckyNumber(item, isWideErrorShow)
|
||||
bindMessages(item, isWideErrorShow)
|
||||
bindAttendance(item, isWideErrorShow)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemDashboardHorizontalGroupBinding.bindLuckyNumber(
|
||||
item: DashboardItem.HorizontalGroup,
|
||||
isWideErrorShow: Boolean
|
||||
) {
|
||||
with(dashboardHorizontalGroupItemLuckyValue) {
|
||||
isVisible = item.luckyNumber?.error != true
|
||||
text = if (item.luckyNumber?.data == 0) {
|
||||
context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||
} else item.luckyNumber?.data?.toString()
|
||||
}
|
||||
dashboardHorizontalGroupItemLuckyError.isVisible = item.luckyNumber?.error == true
|
||||
with(dashboardHorizontalGroupItemLuckyContainer) {
|
||||
isVisible = item.luckyNumber?.isHidden == false && !isWideErrorShow
|
||||
setOnClickListener { onLuckyNumberTileClickListener() }
|
||||
|
||||
val isAttendanceHidden = item.attendancePercentage?.isHidden == true
|
||||
val isMessagesHidden = item.unreadMessagesCount?.isHidden == true
|
||||
val isLuckyNumberHidden = item.luckyNumber?.isHidden == true
|
||||
|
||||
updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
updateMarginsRelative(
|
||||
end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) {
|
||||
0
|
||||
} else context.dpToPx(8f).toInt()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemDashboardHorizontalGroupBinding.bindMessages(
|
||||
item: DashboardItem.HorizontalGroup,
|
||||
isWideErrorShow: Boolean
|
||||
) {
|
||||
dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true
|
||||
with(dashboardHorizontalGroupItemMessageValue) {
|
||||
isVisible = item.unreadMessagesCount?.error != true
|
||||
text = item.unreadMessagesCount?.data.toString()
|
||||
}
|
||||
with(dashboardHorizontalGroupItemMessageContainer) {
|
||||
isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow
|
||||
setOnClickListener { onMessageTileClickListener() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ItemDashboardHorizontalGroupBinding.bindAttendance(
|
||||
item: DashboardItem.HorizontalGroup,
|
||||
isWideErrorShow: Boolean
|
||||
) {
|
||||
val attendancePercentage = item.attendancePercentage?.data
|
||||
(isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded)
|
||||
val attendanceColor = when {
|
||||
attendancePercentage == null || attendancePercentage == .0 -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
}
|
||||
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
context.getThemeAttrColor(R.attr.colorPrimary)
|
||||
}
|
||||
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
|
||||
root.context.getThemeAttrColor(R.attr.colorTimetableChange)
|
||||
context.getThemeAttrColor(R.attr.colorTimetableChange)
|
||||
}
|
||||
else -> root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
else -> context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||
}
|
||||
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
|
||||
root.context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||
context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||
} else {
|
||||
"%.2f%%".format(attendancePercentage)
|
||||
}
|
||||
|
||||
dashboardHorizontalGroupItemAttendanceError.isVisible =
|
||||
item.attendancePercentage?.error == true
|
||||
with(dashboardHorizontalGroupItemAttendanceValue) {
|
||||
isVisible = item.attendancePercentage?.error != true
|
||||
with(binding.dashboardHorizontalGroupItemAttendanceValue) {
|
||||
text = attendanceString
|
||||
setTextColor(attendanceColor)
|
||||
}
|
||||
with(dashboardHorizontalGroupItemAttendanceContainer) {
|
||||
isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow
|
||||
setOnClickListener { onAttendanceTileClickListener() }
|
||||
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
matchConstraintPercentWidth = when {
|
||||
item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f
|
||||
item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f
|
||||
else -> 0.4f
|
||||
|
||||
with(binding) {
|
||||
dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString()
|
||||
dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) {
|
||||
context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||
} else luckyNumber?.toString()
|
||||
|
||||
dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible
|
||||
dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible
|
||||
dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null
|
||||
|
||||
with(dashboardHorizontalGroupItemLuckyContainer) {
|
||||
isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible
|
||||
setOnClickListener { onLuckyNumberTileClickListener() }
|
||||
|
||||
updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
updateMarginsRelative(
|
||||
end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) {
|
||||
0
|
||||
} else {
|
||||
context.dpToPx(8f).toInt()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
with(dashboardHorizontalGroupItemAttendanceContainer) {
|
||||
isVisible =
|
||||
attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible
|
||||
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
matchConstraintPercentWidth = when {
|
||||
luckyNumber == null && unreadMessagesCount == null -> 1.0f
|
||||
luckyNumber == null || unreadMessagesCount == null -> 0.5f
|
||||
else -> 0.4f
|
||||
}
|
||||
}
|
||||
setOnClickListener { onAttendanceTileClickListener() }
|
||||
}
|
||||
|
||||
with(dashboardHorizontalGroupItemMessageContainer) {
|
||||
isVisible =
|
||||
unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible
|
||||
setOnClickListener { onMessageTileClickListener() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -583,7 +563,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||
) {
|
||||
with(binding.dashboardLessonsItemDayHeader) {
|
||||
isVisible = header != null
|
||||
text = header?.content?.parseAsHtml()
|
||||
text = header?.content
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,20 +745,6 @@ 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)
|
||||
|
||||
|
@ -821,9 +787,6 @@ 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.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
|
@ -18,7 +18,6 @@ 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
|
||||
|
@ -48,14 +47,6 @@ 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,4 +1,4 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
|
@ -1,4 +1,4 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
|
@ -1,9 +1,13 @@
|
|||
package io.github.wulkanowy.ui.modules.dashboard
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
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.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) {
|
||||
|
@ -33,27 +37,18 @@ sealed class DashboardItem(val type: Type) {
|
|||
}
|
||||
|
||||
data class HorizontalGroup(
|
||||
val unreadMessagesCount: Cell<Int?>? = null,
|
||||
val attendancePercentage: Cell<Double>? = null,
|
||||
val luckyNumber: Cell<Int>? = null,
|
||||
val unreadMessagesCount: Int? = null,
|
||||
val attendancePercentage: Double? = null,
|
||||
val luckyNumber: Int? = null,
|
||||
override val error: Throwable? = null,
|
||||
override val isLoading: Boolean = false
|
||||
) : DashboardItem(Type.HORIZONTAL_GROUP) {
|
||||
|
||||
data class Cell<T>(
|
||||
val data: T?,
|
||||
val error: Boolean,
|
||||
val isLoading: Boolean,
|
||||
) {
|
||||
val isHidden: Boolean
|
||||
get() = data == null && !error && !isLoading
|
||||
}
|
||||
|
||||
override val isDataLoaded
|
||||
get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false
|
||||
get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null
|
||||
|
||||
val isFullDataLoaded
|
||||
get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true
|
||||
get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1
|
||||
}
|
||||
|
||||
data class Grades(
|
||||
|
@ -111,26 +106,17 @@ 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 {
|
||||
|
@ -140,12 +126,12 @@ sealed class DashboardItem(val type: Type) {
|
|||
MESSAGES,
|
||||
ATTENDANCE,
|
||||
LESSONS,
|
||||
ADS,
|
||||
GRADES,
|
||||
HOMEWORK,
|
||||
ANNOUNCEMENTS,
|
||||
EXAMS,
|
||||
CONFERENCES,
|
||||
ADS
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ package io.github.wulkanowy.ui.modules.dashboard
|
|||
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
|
||||
import java.util.*
|
||||
import java.util.Collections
|
||||
|
||||
class DashboardItemMoveCallback(
|
||||
private val dashboardAdapter: DashboardAdapter,
|
||||
|
|
|
@ -8,7 +8,6 @@ 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.*
|
||||
|
@ -32,8 +31,7 @@ class DashboardPresenter @Inject constructor(
|
|||
private val conferenceRepository: ConferenceRepository,
|
||||
private val preferencesRepository: PreferencesRepository,
|
||||
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
|
||||
private val adminMessageRepository: AdminMessageRepository,
|
||||
private val adsHelper: AdsHelper
|
||||
private val adminMessageRepository: AdminMessageRepository
|
||||
) : BasePresenter<DashboardView>(errorHandler, studentRepository) {
|
||||
|
||||
private val dashboardItemLoadedList = mutableListOf<DashboardItem>()
|
||||
|
@ -57,11 +55,7 @@ class DashboardPresenter @Inject constructor(
|
|||
showContent(false)
|
||||
}
|
||||
|
||||
merge(
|
||||
preferencesRepository.selectedDashboardTilesFlow,
|
||||
preferencesRepository.isAdsEnabledFlow
|
||||
.map { preferencesRepository.selectedDashboardTiles }
|
||||
)
|
||||
preferencesRepository.selectedDashboardTilesFlow
|
||||
.onEach { loadData(tilesToLoad = it) }
|
||||
.launch("dashboard_pref")
|
||||
}
|
||||
|
@ -172,7 +166,7 @@ class DashboardPresenter @Inject constructor(
|
|||
DashboardItem.Type.CONFERENCES -> {
|
||||
loadConferences(student, forceRefresh)
|
||||
}
|
||||
DashboardItem.Type.ADS -> loadAds(forceRefresh)
|
||||
DashboardItem.Type.ADS -> TODO()
|
||||
DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh)
|
||||
}
|
||||
}
|
||||
|
@ -226,71 +220,49 @@ class DashboardPresenter @Inject constructor(
|
|||
|
||||
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
|
||||
flow {
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
val selectedTiles = preferencesRepository.selectedDashboardTiles
|
||||
val flowSuccess = flowOf(Resource.Success(null))
|
||||
|
||||
val flowSuccess = flowOf(Resource.Success(null))
|
||||
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
|
||||
.mapResourceData {
|
||||
it ?: LuckyNumber(0, LocalDate.now(), 0)
|
||||
}
|
||||
.onResourceError { errorHandler.dispatch(it) }
|
||||
.takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess
|
||||
|
||||
val messageFLow = flatResourceFlow {
|
||||
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||
val messageFLow = messageRepository.getMessages(
|
||||
student = student,
|
||||
semester = semester,
|
||||
folder = MessageFolder.RECEIVED,
|
||||
forceRefresh = forceRefresh
|
||||
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
||||
|
||||
messageRepository.getMessages(
|
||||
student = student,
|
||||
mailbox = mailbox,
|
||||
folder = MessageFolder.RECEIVED,
|
||||
forceRefresh = forceRefresh
|
||||
)
|
||||
}
|
||||
.onResourceError { errorHandler.dispatch(it) }
|
||||
.takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
||||
|
||||
val attendanceFlow = flatResourceFlow {
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
attendanceSummaryRepository.getAttendanceSummary(
|
||||
student = student,
|
||||
semester = semester,
|
||||
subjectId = -1,
|
||||
forceRefresh = forceRefresh
|
||||
)
|
||||
}
|
||||
.onResourceError { errorHandler.dispatch(it) }
|
||||
.takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
|
||||
val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary(
|
||||
student = student,
|
||||
semester = semester,
|
||||
subjectId = -1,
|
||||
forceRefresh = forceRefresh
|
||||
).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
|
||||
|
||||
emitAll(
|
||||
combine(
|
||||
flow = luckyNumberFlow,
|
||||
flow2 = messageFLow,
|
||||
flow3 = attendanceFlow,
|
||||
luckyNumberFlow,
|
||||
messageFLow,
|
||||
attendanceFlow
|
||||
) { luckyNumberResource, messageResource, attendanceResource ->
|
||||
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
|
||||
resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it }
|
||||
val isLoading = resList.any { it is Resource.Loading }
|
||||
|
||||
val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber
|
||||
val messageCount = messageResource.dataOrNull?.count { it.unread }
|
||||
val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage()
|
||||
|
||||
DashboardItem.HorizontalGroup(
|
||||
isLoading = resList.any { it is Resource.Loading },
|
||||
error = resList.map { it.errorOrNull }.let { errors ->
|
||||
if (errors.all { it != null }) {
|
||||
errors.firstOrNull()
|
||||
} else null
|
||||
},
|
||||
attendancePercentage = DashboardItem.HorizontalGroup.Cell(
|
||||
data = attendanceResource.dataOrNull?.calculatePercentage(),
|
||||
error = attendanceResource.errorOrNull != null,
|
||||
isLoading = attendanceResource is Resource.Loading,
|
||||
),
|
||||
unreadMessagesCount = DashboardItem.HorizontalGroup.Cell(
|
||||
data = messageResource.dataOrNull?.count { it.unread },
|
||||
error = messageResource.errorOrNull != null,
|
||||
isLoading = messageResource is Resource.Loading,
|
||||
),
|
||||
luckyNumber = DashboardItem.HorizontalGroup.Cell(
|
||||
data = luckyNumberResource.dataOrNull?.luckyNumber,
|
||||
error = luckyNumberResource.errorOrNull != null,
|
||||
isLoading = luckyNumberResource is Resource.Loading,
|
||||
)
|
||||
isLoading = isLoading,
|
||||
attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage,
|
||||
unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount,
|
||||
luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -301,8 +273,11 @@ class DashboardPresenter @Inject constructor(
|
|||
|
||||
if (it.isLoading) {
|
||||
Timber.i("Loading horizontal group data started")
|
||||
|
||||
if (it.isFullDataLoaded) {
|
||||
firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
|
||||
}
|
||||
} else {
|
||||
firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
|
||||
Timber.i("Loading horizontal group result: Success")
|
||||
}
|
||||
}
|
||||
|
@ -620,23 +595,6 @@ 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 =
|
||||
|
@ -661,18 +619,6 @@ 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,8 +4,6 @@ import io.github.wulkanowy.ui.base.BaseView
|
|||
|
||||
interface DashboardView : BaseView {
|
||||
|
||||
val tileWidth: Int
|
||||
|
||||
fun initView()
|
||||
|
||||
fun updateData(data: List<DashboardItem>)
|
||||
|
|
|
@ -17,16 +17,16 @@ val debugMessageItems = listOf(
|
|||
)
|
||||
|
||||
private fun generateMessage(sender: String, subject: String) = Message(
|
||||
sender = sender,
|
||||
subject = subject,
|
||||
messageId = 123,
|
||||
email = "",
|
||||
studentId = 0,
|
||||
realId = 0,
|
||||
messageId = 0,
|
||||
senderId = 0,
|
||||
recipient = "",
|
||||
date = Instant.now(),
|
||||
folderId = 0,
|
||||
unread = true,
|
||||
readBy = 2,
|
||||
unreadBy = 2,
|
||||
hasAttachments = false,
|
||||
messageGlobalKey = "",
|
||||
correspondents = sender,
|
||||
mailboxKey = "",
|
||||
removed = false,
|
||||
hasAttachments = false
|
||||
)
|
||||
|
|
|
@ -19,6 +19,6 @@ val debugSchoolAnnouncementItems = listOf(
|
|||
private fun generateAnnouncement(subject: String, content: String) = SchoolAnnouncement(
|
||||
subject = subject,
|
||||
content = content,
|
||||
userLoginId = 0,
|
||||
studentId = 0,
|
||||
date = LocalDate.now()
|
||||
)
|
||||
|
|
|
@ -3,7 +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.*
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.ALPHABETIC
|
||||
import io.github.wulkanowy.data.enums.GradeSortingMode.DATE
|
||||
import io.github.wulkanowy.data.repositories.GradeRepository
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||
|
@ -131,17 +132,16 @@ class GradeDetailsPresenter @Inject constructor(
|
|||
}
|
||||
.logResourceStatus("load grade details")
|
||||
.onResourceData {
|
||||
val gradeItems = createGradeItems(it)
|
||||
view?.run {
|
||||
enableSwipe(true)
|
||||
showProgress(false)
|
||||
showErrorView(false)
|
||||
showContent(gradeItems.isNotEmpty())
|
||||
showEmpty(gradeItems.isEmpty())
|
||||
showContent(it.isNotEmpty())
|
||||
showEmpty(it.isEmpty())
|
||||
updateNewGradesAmount(it)
|
||||
updateMarkAsDoneButton()
|
||||
updateData(
|
||||
data = gradeItems,
|
||||
data = createGradeItems(it),
|
||||
expandMode = preferencesRepository.gradeExpandMode,
|
||||
preferencesRepository.gradeColorTheme
|
||||
)
|
||||
|
@ -204,7 +204,6 @@ class GradeDetailsPresenter @Inject constructor(
|
|||
ALPHABETIC -> gradeSubjects.sortedBy { gradeDetailsWithAverage ->
|
||||
gradeDetailsWithAverage.subject.lowercase()
|
||||
}
|
||||
AVERAGE -> gradeSubjects.sortedByDescending { it.average }
|
||||
}
|
||||
}
|
||||
.map { (subject, average, points, _, grades) ->
|
||||
|
|
|
@ -10,7 +10,6 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
|
|||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.ItemGradeSummaryBinding
|
||||
import io.github.wulkanowy.databinding.ScrollableHeaderGradeSummaryBinding
|
||||
import io.github.wulkanowy.sdk.scrapper.grades.isGradeValid
|
||||
import io.github.wulkanowy.utils.calcFinalAverage
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
@ -62,7 +61,7 @@ class GradeSummaryAdapter @Inject constructor(
|
|||
if (items.isEmpty()) return
|
||||
|
||||
val context = binding.root.context
|
||||
val finalItemsCount = items.count { isGradeValid(it.finalGrade) }
|
||||
val finalItemsCount = items.count { it.finalGrade.matches("[0-6][+-]?".toRegex()) }
|
||||
val calculatedItemsCount = items.count { value -> value.average != 0.0 }
|
||||
val allItemsCount = items.count { !it.subject.equals("zachowanie", true) }
|
||||
val finalAverage = items.calcFinalAverage(
|
||||
|
|
|
@ -2,9 +2,6 @@ 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
|
||||
|
@ -17,7 +14,6 @@ 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) {
|
||||
|
@ -131,17 +127,7 @@ class GradeSummaryPresenter @Inject constructor(
|
|||
private fun createGradeSummaryItems(items: List<GradeSubject>): List<GradeSummary> {
|
||||
return items
|
||||
.filter { !checkEmpty(it) }
|
||||
.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 }
|
||||
}
|
||||
}
|
||||
.sortedBy { it.subject }
|
||||
.map { it.summary.copy(average = it.average) }
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ 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)
|
||||
|
|
|
@ -10,7 +10,6 @@ import androidx.core.widget.doOnTextChanged
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
|
@ -33,9 +32,6 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
fun newInstance() = LoginFormFragment()
|
||||
}
|
||||
|
@ -264,9 +260,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
|
|||
R.string.login_email_text,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||
appInfo.versionName,
|
||||
"$formHostValue/$formHostSymbol",
|
||||
preferencesRepository.installationId,
|
||||
lastError
|
||||
)
|
||||
)
|
||||
|
|
|
@ -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.equals(emailDomain, true)) {
|
||||
if (emailHost != emailDomain) {
|
||||
view?.setErrorEmailInvalid(domain = emailDomain)
|
||||
isCorrect = false
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.webkit.*
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
|
@ -204,9 +206,10 @@ class LoginRecoverFragment :
|
|||
}
|
||||
|
||||
override fun onReceivedError(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?,
|
||||
error: WebResourceError?
|
||||
view: WebView,
|
||||
errorCode: Int,
|
||||
description: String,
|
||||
failingUrl: String
|
||||
) {
|
||||
recoverWebViewSuccess = false
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginStudentSelectBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
|
@ -33,9 +32,6 @@ class LoginStudentSelectFragment :
|
|||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
const val ARG_STUDENTS = "STUDENTS"
|
||||
|
||||
|
@ -115,12 +111,10 @@ class LoginStudentSelectFragment :
|
|||
email = "wulkanowyinc@gmail.com",
|
||||
subject = requireContext().getString(R.string.login_email_subject),
|
||||
body = requireContext().getString(
|
||||
R.string.login_email_text,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
R.string.login_email_text, appInfo.systemModel,
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||
appInfo.versionName,
|
||||
"Select users to log in",
|
||||
preferencesRepository.installationId,
|
||||
lastError
|
||||
)
|
||||
)
|
||||
|
|
|
@ -13,7 +13,6 @@ import androidx.core.widget.doOnTextChanged
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.databinding.FragmentLoginSymbolBinding
|
||||
import io.github.wulkanowy.ui.base.BaseFragment
|
||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||
|
@ -35,9 +34,6 @@ class LoginSymbolFragment :
|
|||
@Inject
|
||||
lateinit var appInfo: AppInfo
|
||||
|
||||
@Inject
|
||||
lateinit var preferencesRepository: PreferencesRepository
|
||||
|
||||
companion object {
|
||||
private const val SAVED_LOGIN_DATA = "LOGIN_DATA"
|
||||
|
||||
|
@ -163,9 +159,8 @@ class LoginSymbolFragment :
|
|||
R.string.login_email_text,
|
||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||
appInfo.systemVersion.toString(),
|
||||
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||
appInfo.versionName,
|
||||
"$host/${binding.loginSymbolName.text}",
|
||||
preferencesRepository.installationId,
|
||||
lastError
|
||||
)
|
||||
)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue