Compare commits
No commits in common. "1.6.3" and "1.2.2" have entirely different histories.
467 changed files with 6900 additions and 34111 deletions
|
@ -1,12 +0,0 @@
|
|||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=true
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
[*.json]
|
||||
indent_size=2
|
||||
|
||||
[*.{kt,kts}]
|
||||
disabled_rules=import-ordering,no-wildcard-imports
|
14
.github/workflows/deploy-store.yml
vendored
14
.github/workflows/deploy-store.yml
vendored
|
@ -28,17 +28,15 @@ jobs:
|
|||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||
- name: Upload apk to google play
|
||||
env:
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
||||
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
||||
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
||||
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
||||
PLAY_SERVICE_ACCOUNT_EMAIL: ${{ secrets.PLAY_SERVICE_ACCOUNT_EMAIL }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew publishPlayRelease -PenableFirebase --stacktrace;
|
||||
|
||||
deploy-app-gallery:
|
||||
name: Deploy to AppGallery
|
||||
|
@ -62,6 +60,7 @@ jobs:
|
|||
SERVICES_ENCRYPT_KEY: ${{ secrets.SERVICES_ENCRYPT_KEY }}
|
||||
run: |
|
||||
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg
|
||||
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg
|
||||
- name: Prepare credentials
|
||||
env:
|
||||
|
@ -69,8 +68,7 @@ jobs:
|
|||
run: echo $AGC_CREDENTIALS > ./app/src/release/agconnect-credentials.json
|
||||
- name: Build and publish HMS version
|
||||
env:
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
||||
|
|
15
.idea/codeStyles/Project.xml
generated
15
.idea/codeStyles/Project.xml
generated
|
@ -2,6 +2,14 @@
|
|||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<JetCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
<package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||
<option name="WRAP_ELVIS_EXPRESSIONS" value="0" />
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="XML">
|
||||
|
@ -118,6 +126,13 @@
|
|||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
2
LICENSE
2
LICENSE
|
@ -186,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Wulkanowy
|
||||
Copyright 2019 Wulkanowy
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
78
README.cs.md
78
README.cs.md
|
@ -1,78 +0,0 @@
|
|||
[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://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
|
||||
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
||||
|
||||
## Funkce
|
||||
|
||||
* přihlášení pomocí emailu a hesla
|
||||
* funkce z webové stránky deníku:
|
||||
* známky
|
||||
* statistiky známek
|
||||
* frekvence
|
||||
* procento frekvence
|
||||
* zkoušky
|
||||
* plán lekce
|
||||
* dokončené lekce
|
||||
* zprávy
|
||||
* domácí úkoly
|
||||
* poznámky
|
||||
* šťastné číslo
|
||||
* další lekce
|
||||
* školní setkání
|
||||
* informace o žáku a škole
|
||||
* výpočet průměru nezávisle na preferencích školy
|
||||
* upozornění, např. o nových známkách
|
||||
* podpora více účtů s možností přejmenování žáků
|
||||
* tmavý a černý (AMOLED) motiv
|
||||
* offline režim
|
||||
* žádné reklamy
|
||||
|
||||
## Stáhnout
|
||||
|
||||
Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGallery
|
||||
|
||||
[<img src="https://play.google.com/intl/cs-CZ/badges/images/generic/cs_badge_web_generic.png"
|
||||
alt="Nyní na Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Stáhnout s F-Droid"
|
||||
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[<img src="https://i.imgur.com/baTGiDP.png"
|
||||
alt="Objevuj v AppGallery"
|
||||
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||
|
||||
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
|
||||
|
||||
|
||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||
* [Hilt](https://dagger.dev/hilt/)
|
||||
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||
|
||||
## Spolupráce
|
||||
|
||||
Přispějte do projektu vytvořením PR nebo odesláním issue na GitHub.
|
||||
|
||||
Pro zájemce o překlad aplikace do různých jazyků poskytujeme Crowdin:
|
||||
https://crowdin.com/project/wulkanowy2
|
||||
|
||||
## Licence
|
||||
|
||||
Tento projekt je licencován pod licencí Apache License 2.0 - podrobnosti v souboru [LICENSE](LICENSE)
|
74
README.de.md
74
README.de.md
|
@ -1,74 +0,0 @@
|
|||
[Polska wersja README](README.md)
|
||||
|
||||
[English version of README](README.en.md)
|
||||
|
||||
# Wulkanowy
|
||||
|
||||
[](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)
|
||||
|
||||
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
||||
|
||||
## Merkmale
|
||||
|
||||
* Einloggen mit E-Mail und Passwort
|
||||
* Funktionen von der Registerwebsite:
|
||||
* Noten
|
||||
* Notenstatistik
|
||||
* Anwesenheit
|
||||
* Prozentsatz der Anwesenheit
|
||||
* Prüfungen
|
||||
* Stundenplan
|
||||
* Unterricht abgeschlossen
|
||||
* Nachrichten
|
||||
* Hausaufgaben
|
||||
* Anmerkungen
|
||||
* Glückszahl
|
||||
* Zusätzliche Lektionen
|
||||
* Schulkonferenzen
|
||||
* Schüler- und Schulinformationen
|
||||
* Berechnung des Durchschnitts unabhängig von den Präferenzen der Schule
|
||||
* Benachrichtigungen, z. B. über eine neue Note
|
||||
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
|
||||
* dunkles und schwarzes (AMOLED) Thema
|
||||
* Offline-Modus
|
||||
* keine Werbung
|
||||
|
||||
## Herunterladen
|
||||
|
||||
Die aktuelle Version können Sie von der Google Play, F-Droid oder Huawei AppGallery store herunterladen
|
||||
|
||||
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
|
||||
alt="Get it on Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[<img src="appgallery_badge.png"
|
||||
alt="Explore it on AppGallery"
|
||||
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||
|
||||
Sie können auch ein [Entwicklungsversion herunterladen](https://wulkanowy.github.io/#download) das beinhaltet neue Funktionen, die für die nächste Version vorbereitet werden
|
||||
|
||||
## Gebaut mit
|
||||
|
||||
|
||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||
* [Hilt](https://dagger.dev/hilt/)
|
||||
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||
|
||||
## Beitragen
|
||||
|
||||
Bitte tragen Sie zum Projekt bei, indem Sie entweder eine PR erstellen oder ein Issue auf GitHub einreichen.
|
||||
|
||||
Für Personen, die daran interessiert sind, die Anwendung in verschiedene Sprachen zu übersetzen, bieten wir Crowdin
|
||||
https://crowdin.com/project/wulkanowy2
|
||||
|
||||
## Lizenz
|
||||
|
||||
Dieses Projekt ist unter der Apache License 2.0 lizenziert - siehe die [LIZENZ](LICENSE) Datei für Details
|
|
@ -1,11 +1,5 @@
|
|||
[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)
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
[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)
|
||||
|
|
78
README.sk.md
78
README.sk.md
|
@ -1,78 +0,0 @@
|
|||
[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://codecov.io/gh/wulkanowy/wulkanowy)
|
||||
[](https://discord.gg/vccAQBr)
|
||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||
|
||||
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
||||
|
||||
## Funkcie
|
||||
|
||||
* prihlásenie pomocou emailu a hesla
|
||||
* funkcie z webovej stránky denníka:
|
||||
* známky
|
||||
* štatistiky známok
|
||||
* frekvencia
|
||||
* percento frekvencie
|
||||
* skúšky
|
||||
* plán lekcie
|
||||
* dokončené lekcie
|
||||
* správy
|
||||
* domáce úlohy
|
||||
* poznámky
|
||||
* šťastné číslo
|
||||
* ďalšie lekcie
|
||||
* školské stretnutie
|
||||
* informácie o žiakovi a škole
|
||||
* výpočet priemeru nezávisle od preferencií školy
|
||||
* upozornenia, napr. o nových známkach
|
||||
* podpora viacerých účtov s možnosťou premenovania žiakov
|
||||
* tmavý a čierny (AMOLED) motív
|
||||
* offline režim
|
||||
* žiadne reklamy
|
||||
|
||||
## Stiahnuť
|
||||
|
||||
Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGallery
|
||||
|
||||
[<img src="https://play.google.com/intl/sk/badges/images/generic/sk_badge_web_generic.png"
|
||||
alt="Nyní na Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Stiahnuť s F-Droid"
|
||||
height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||
[<img src="https://i.imgur.com/sX8UyAw.png"
|
||||
alt="Objavíte v AppGallery"
|
||||
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
|
||||
|
||||
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
|
||||
|
||||
|
||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||
* [Kotlin Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html)
|
||||
* [Hilt](https://dagger.dev/hilt/)
|
||||
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||
|
||||
## Spolupráca
|
||||
|
||||
Prispejte do projektu vytvorením PR alebo odoslaním issue na GitHub.
|
||||
|
||||
Pre záujemcov o preklad aplikácie do rôznych jazykov poskytujeme Crowdin:
|
||||
https://crowdin.com/project/wulkanowy2
|
||||
|
||||
## Licencia
|
||||
|
||||
Tento projekt je licencovaný pod licenciou Apache License 2.0 - podrobnosti v súbore [LICENSE](LICENSE)
|
124
app/build.gradle
124
app/build.gradle
|
@ -1,6 +1,5 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlinx-serialization'
|
||||
apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'dagger.hilt.android.plugin'
|
||||
|
@ -15,22 +14,23 @@ apply from: 'sonarqube.gradle'
|
|||
apply from: 'hooks.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdkVersion 30
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.wulkanowy"
|
||||
testApplicationId "io.github.tests.wulkanowy"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 107
|
||||
versionName "1.6.3"
|
||||
targetSdkVersion 30
|
||||
versionCode 95
|
||||
versionName "1.2.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
resValue "string", "app_name", "Wulkanowy"
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||
|
||||
manifestPlaceholders = [
|
||||
firebase_enabled: project.hasProperty("enableFirebase"),
|
||||
admob_project_id: ""
|
||||
firebase_enabled: project.hasProperty("enableFirebase")
|
||||
]
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
|
@ -40,14 +40,6 @@ android {
|
|||
]
|
||||
}
|
||||
}
|
||||
|
||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
|
||||
|
||||
if (System.env.SET_BUILD_TIMESTAMP) {
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", String.valueOf(System.currentTimeMillis())
|
||||
} else {
|
||||
buildConfigField "long", "BUILD_TIMESTAMP", "1486235849000"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
@ -70,16 +62,12 @@ android {
|
|||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||
}
|
||||
debug {
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
resValue "string", "app_name", "Wulkanowy DEV"
|
||||
resValue "string", "app_name", "Wulkanowy DEV " + defaultConfig.versionCode
|
||||
applicationIdSuffix ".dev"
|
||||
versionNameSuffix "-dev"
|
||||
ext.enableCrashlytics = project.hasProperty("enableFirebase")
|
||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,38 +76,30 @@ android {
|
|||
productFlavors {
|
||||
hms {
|
||||
dimension "platform"
|
||||
manifestPlaceholders = [install_channel: "AppGallery"]
|
||||
manifestPlaceholders = [
|
||||
install_channel: "AppGallery"
|
||||
]
|
||||
}
|
||||
|
||||
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"
|
||||
]
|
||||
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "\"${System.getenv("SINGLE_SUPPORT_AD_ID") ?: "ca-app-pub-3940256099942544/5354046379"}\""
|
||||
}
|
||||
|
||||
fdroid {
|
||||
dimension "platform"
|
||||
manifestPlaceholders = [install_channel: "F-Droid"]
|
||||
manifestPlaceholders = [
|
||||
install_channel: "F-Droid"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
playConfigs {
|
||||
play { enabled.set(true) }
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
bundle {
|
||||
language {
|
||||
enableSplit = false
|
||||
}
|
||||
}
|
||||
|
||||
testOptions.unitTests {
|
||||
includeAndroidResources = true
|
||||
}
|
||||
|
@ -150,12 +130,11 @@ kapt {
|
|||
}
|
||||
|
||||
play {
|
||||
serviceAccountEmail = System.getenv("PLAY_SERVICE_ACCOUNT_EMAIL") ?: "jan@fakelog.cf"
|
||||
serviceAccountCredentials = file('key.p12')
|
||||
defaultToAppBundles = false
|
||||
track = 'production'
|
||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||
userFraction = 0.50d
|
||||
updatePriority = 1
|
||||
enabled.set(false)
|
||||
updatePriority = 3
|
||||
}
|
||||
|
||||
huaweiPublish {
|
||||
|
@ -169,36 +148,35 @@ huaweiPublish {
|
|||
}
|
||||
|
||||
ext {
|
||||
work_manager = "2.7.1"
|
||||
work_manager = "2.6.0"
|
||||
android_hilt = "1.0.0"
|
||||
room = "2.4.2"
|
||||
room = "2.3.0"
|
||||
chucker = "3.5.2"
|
||||
mockk = "1.12.2"
|
||||
coroutines = "1.6.1"
|
||||
mockk = "1.12.0"
|
||||
moshi = "1.12.0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.6.0"
|
||||
implementation "io.github.wulkanowy:sdk:1.2.2"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
||||
|
||||
implementation "androidx.core:core-ktx:1.7.0"
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-beta02'
|
||||
implementation "androidx.activity:activity-ktx:1.4.0"
|
||||
implementation "androidx.appcompat:appcompat:1.4.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||
implementation "androidx.annotation:annotation:1.3.0"
|
||||
implementation "androidx.core:core-ktx:1.6.0"
|
||||
implementation "androidx.activity:activity-ktx:1.3.1"
|
||||
implementation "androidx.appcompat:appcompat:1.3.1"
|
||||
implementation "androidx.appcompat:appcompat-resources:1.3.1"
|
||||
implementation "androidx.fragment:fragment-ktx:1.3.6"
|
||||
implementation "androidx.annotation:annotation:1.2.0"
|
||||
|
||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
||||
implementation "androidx.viewpager:viewpager:1.0.0"
|
||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||
implementation "com.google.android.material:material:1.5.0"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.0"
|
||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||
implementation "com.google.android.material:material:1.4.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.2.0'
|
||||
|
@ -206,7 +184,7 @@ dependencies {
|
|||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room"
|
||||
implementation "androidx.room:room-ktx:$room"
|
||||
|
@ -220,42 +198,40 @@ dependencies {
|
|||
implementation 'com.github.ncapdevi:FragNav:3.3.0'
|
||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
|
||||
implementation "com.squareup.moshi:moshi:$moshi"
|
||||
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
||||
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi"
|
||||
|
||||
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.github.bastienpaulfr:Treessence:1.0.4'
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||
implementation "io.coil-kt:coil:1.4.0"
|
||||
implementation "io.coil-kt:coil:1.3.2"
|
||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
|
||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
||||
implementation 'com.fredporciuncula:flow-preferences:1.5.0'
|
||||
|
||||
playImplementation platform('com.google.firebase:firebase-bom:29.3.1')
|
||||
playImplementation platform('com.google.firebase:firebase-bom:28.4.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:1.10.1'
|
||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
|
||||
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.302'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.300'
|
||||
hmsImplementation 'com.huawei.hms:hianalytics:6.2.0.301'
|
||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.0.300'
|
||||
|
||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||
|
||||
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
|
||||
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
|
||||
debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04'
|
||||
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:v1.0.6'
|
||||
|
||||
testImplementation "junit:junit:4.13.2"
|
||||
testImplementation "io.mockk:mockk:$mockk"
|
||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||
testImplementation 'org.robolectric:robolectric:4.6.1'
|
||||
testImplementation "androidx.test:runner:1.4.0"
|
||||
testImplementation "androidx.test.ext:junit:1.1.3"
|
||||
testImplementation "androidx.test:core:1.4.0"
|
||||
|
|
BIN
app/key.p12.gpg
Normal file
BIN
app/key.p12.gpg
Normal file
Binary file not shown.
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
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
|
@ -6,7 +6,6 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||
import javax.inject.Singleton
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("UNUSED_PARAMETER", "unused")
|
||||
@Singleton
|
||||
class InAppReviewHelper @Inject constructor(
|
||||
@ApplicationContext private val context: Context
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
|
@ -39,14 +38,13 @@
|
|||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="false"
|
||||
android:theme="@style/WulkanowyTheme"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
||||
<activity
|
||||
android:name=".ui.modules.splash.SplashActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/WulkanowyTheme.SplashScreen"
|
||||
tools:ignore="LockedOrientationActivity">
|
||||
|
@ -76,7 +74,6 @@
|
|||
<activity
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
|
@ -86,7 +83,6 @@
|
|||
<activity
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity"
|
||||
android:excludeFromRecents="true"
|
||||
android:exported="true"
|
||||
android:noHistory="true"
|
||||
android:theme="@style/WulkanowyTheme.WidgetAccountSwitcher">
|
||||
<intent-filter>
|
||||
|
@ -97,22 +93,6 @@
|
|||
<service
|
||||
android:name=".services.widgets.TimetableWidgetService"
|
||||
android:permission="android.permission.BIND_REMOTEVIEWS" />
|
||||
<service
|
||||
android:name=".services.piggyback.VulcanNotificationListenerService"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.service.notification.NotificationListenerService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service
|
||||
android:name=".services.messaging.AppMessagingService"
|
||||
android:exported="false"
|
||||
tools:ignore="MissingClass">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||
|
@ -127,7 +107,6 @@
|
|||
</receiver>
|
||||
<receiver
|
||||
android:name=".ui.modules.luckynumberwidget.LuckyNumberWidgetProvider"
|
||||
android:exported="true"
|
||||
android:label="@string/lucky_number_title">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
|
@ -153,44 +132,44 @@
|
|||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<meta-data
|
||||
android:name="install_channel"
|
||||
android:value="${install_channel}" />
|
||||
|
||||
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
|
||||
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
|
||||
<provider
|
||||
android:name="com.google.firebase.provider.FirebaseInitProvider"
|
||||
android:authorities="${applicationId}.firebaseinitprovider"
|
||||
android:enabled="${firebase_enabled}"
|
||||
android:exported="false"
|
||||
tools:ignore="MissingClass" />
|
||||
android:exported="false" />
|
||||
|
||||
<meta-data
|
||||
android:name="install_channel"
|
||||
android:value="${install_channel}" />
|
||||
<meta-data
|
||||
android:name="firebase_analytics_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
|
||||
<meta-data
|
||||
android:name="google_analytics_adid_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
|
||||
<meta-data
|
||||
android:name="firebase_crashlytics_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
|
||||
<meta-data
|
||||
android:name="firebase_messaging_auto_init_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
|
||||
<meta-data
|
||||
android:name="firebase_inapp_messaging_auto_data_collection_enabled"
|
||||
android:value="${firebase_enabled}" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_stat_all" />
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_channel_id"
|
||||
android:value="push_channel" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="${admob_project_id}" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.DELAY_APP_MEASUREMENT_INIT"
|
||||
android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package io.github.wulkanowy
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.util.Log.*
|
||||
import android.util.Log.DEBUG
|
||||
import android.util.Log.INFO
|
||||
import android.util.Log.VERBOSE
|
||||
import android.webkit.WebView
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import com.yariksoffice.lingver.Lingver
|
||||
|
@ -9,7 +14,12 @@ import dagger.hilt.android.HiltAndroidApp
|
|||
import fr.bipi.tressence.file.FileLoggerTree
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.ui.base.ThemeManager
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import io.github.wulkanowy.utils.CrashLogExceptionTree
|
||||
import io.github.wulkanowy.utils.CrashLogTree
|
||||
import io.github.wulkanowy.utils.DebugLogTree
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -31,11 +41,14 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||
@Inject
|
||||
lateinit var analyticsHelper: AnalyticsHelper
|
||||
|
||||
@SuppressLint("UnsafeOptInUsageWarning")
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FragmentManager.enableNewStateManager(false)
|
||||
initializeAppLanguage()
|
||||
themeManager.applyDefaultTheme()
|
||||
initLogging()
|
||||
fixWebViewLocale()
|
||||
}
|
||||
|
||||
private fun initLogging() {
|
||||
|
@ -67,6 +80,15 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||
}
|
||||
}
|
||||
|
||||
private fun fixWebViewLocale() {
|
||||
//https://stackoverflow.com/questions/40398528/android-webview-language-changes-abruptly-on-android-7-0-and-above
|
||||
try {
|
||||
WebView(this).destroy()
|
||||
} catch (e: Throwable) {
|
||||
//Ignore exceptions
|
||||
}
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
||||
.setWorkerFactory(workerFactory)
|
||||
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
|
||||
|
|
|
@ -2,100 +2,62 @@ package io.github.wulkanowy.data
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.AssetManager
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.chuckerteam.chucker.api.ChuckerCollector
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.chuckerteam.chucker.api.RetentionManager
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import io.github.wulkanowy.data.api.AdminMessageService
|
||||
import io.github.wulkanowy.data.db.AppDatabase
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.create
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal class DataModule {
|
||||
internal class RepositoryModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor) =
|
||||
Sdk().apply {
|
||||
fun provideSdk(chuckerCollector: ChuckerCollector, @ApplicationContext context: Context): Sdk {
|
||||
return Sdk().apply {
|
||||
androidVersion = android.os.Build.VERSION.RELEASE
|
||||
buildTag = android.os.Build.MODEL
|
||||
setSimpleHttpLogger { Timber.d(it) }
|
||||
|
||||
// for debug only
|
||||
addInterceptor(chuckerInterceptor, network = true)
|
||||
addInterceptor(
|
||||
ChuckerInterceptor.Builder(context)
|
||||
.collector(chuckerCollector)
|
||||
.alwaysReadResponseBody(true)
|
||||
.build(), network = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideChuckerCollector(
|
||||
@ApplicationContext context: Context,
|
||||
prefRepository: PreferencesRepository
|
||||
) = ChuckerCollector(
|
||||
context = context,
|
||||
showNotification = prefRepository.isDebugNotificationEnable,
|
||||
retentionPeriod = RetentionManager.Period.ONE_HOUR
|
||||
)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideChuckerInterceptor(
|
||||
@ApplicationContext context: Context,
|
||||
chuckerCollector: ChuckerCollector
|
||||
) = ChuckerInterceptor.Builder(context)
|
||||
.collector(chuckerCollector)
|
||||
.alwaysReadResponseBody(true)
|
||||
.build()
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideOkHttpClient(chuckerInterceptor: ChuckerInterceptor): OkHttpClient =
|
||||
OkHttpClient.Builder()
|
||||
.addNetworkInterceptor(chuckerInterceptor)
|
||||
.addInterceptor(HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.BASIC
|
||||
})
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideRetrofit(
|
||||
okHttpClient: OkHttpClient,
|
||||
json: Json,
|
||||
appInfo: AppInfo
|
||||
): Retrofit = Retrofit.Builder()
|
||||
.baseUrl(appInfo.messagesBaseUrl)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAdminMessageService(retrofit: Retrofit): AdminMessageService = retrofit.create()
|
||||
): ChuckerCollector {
|
||||
return ChuckerCollector(
|
||||
context = context,
|
||||
showNotification = prefRepository.isDebugNotificationEnable,
|
||||
retentionPeriod = RetentionManager.Period.ONE_HOUR
|
||||
)
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -105,6 +67,14 @@ internal class DataModule {
|
|||
appInfo: AppInfo
|
||||
) = AppDatabase.newInstance(context, sharedPrefProvider, appInfo)
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideResources(@ApplicationContext context: Context): Resources = context.resources
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAssets(@ApplicationContext context: Context): AssetManager = context.assets
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences =
|
||||
|
@ -118,9 +88,7 @@ internal class DataModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideJson() = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
fun provideMoshi() = Moshi.Builder().build()
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
|
@ -234,12 +202,4 @@ internal class DataModule {
|
|||
@Singleton
|
||||
@Provides
|
||||
fun provideSchoolAnnouncementDao(database: AppDatabase) = database.schoolAnnouncementDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideNotificationDao(database: AppDatabase) = database.notificationDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAdminMessageDao(database: AppDatabase) = database.adminMessagesDao
|
||||
}
|
|
@ -1,173 +1,23 @@
|
|||
package io.github.wulkanowy.data
|
||||
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import timber.log.Timber
|
||||
|
||||
sealed class Resource<T> {
|
||||
|
||||
open class Loading<T> : Resource<T>()
|
||||
|
||||
data class Intermediate<T>(val data: T) : Loading<T>()
|
||||
|
||||
data class Success<T>(val data: T) : Resource<T>()
|
||||
|
||||
data class Error<T>(val error: Throwable) : Resource<T>()
|
||||
}
|
||||
|
||||
val <T> Resource<T>.dataOrNull: T?
|
||||
get() = when (this) {
|
||||
is Resource.Success -> this.data
|
||||
is Resource.Intermediate -> this.data
|
||||
is Resource.Loading -> null
|
||||
is Resource.Error -> null
|
||||
}
|
||||
|
||||
val <T> Resource<T>.errorOrNull: Throwable?
|
||||
get() = when (this) {
|
||||
is Resource.Error -> this.error
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun <T> resourceFlow(block: suspend () -> T) = flow {
|
||||
emit(Resource.Loading())
|
||||
emit(Resource.Success(block()))
|
||||
}.catch { emit(Resource.Error(it)) }
|
||||
|
||||
fun <T> flatResourceFlow(block: suspend () -> Flow<Resource<T>>) = flow {
|
||||
emit(Resource.Loading())
|
||||
emitAll(block().filter { it is Resource.Intermediate || it !is Resource.Loading })
|
||||
}.catch { emit(Resource.Error(it)) }
|
||||
|
||||
fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
||||
is Resource.Success -> Resource.Success(block(this.data))
|
||||
is Resource.Intermediate -> Resource.Intermediate(block(this.data))
|
||||
is Resource.Loading -> Resource.Loading()
|
||||
is Resource.Error -> Resource.Error(this.error)
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||
val description = when (it) {
|
||||
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}"
|
||||
}
|
||||
Timber.i("$name: $description")
|
||||
}
|
||||
|
||||
fun <T, U> Flow<Resource<T>>.mapResourceData(block: (T) -> U) = map {
|
||||
it.mapData(block)
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceData(block: suspend (T) -> Unit) = onEach {
|
||||
when (it) {
|
||||
is Resource.Success -> block(it.data)
|
||||
is Resource.Intermediate -> block(it.data)
|
||||
is Resource.Error,
|
||||
is Resource.Loading -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceLoading(block: suspend () -> Unit) = onEach {
|
||||
if (it is Resource.Loading) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceIntermediate(block: suspend (T) -> Unit) = onEach {
|
||||
if (it is Resource.Intermediate) {
|
||||
block(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceSuccess(block: suspend (T) -> Unit) = onEach {
|
||||
if (it is Resource.Success) {
|
||||
block(it.data)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceError(block: (Throwable) -> Unit) = onEach {
|
||||
if (it is Resource.Error) {
|
||||
block(it.error)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<Resource<T>>.onResourceNotLoading(block: () -> Unit) = onEach {
|
||||
if (it !is Resource.Loading) {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T> Flow<Resource<T>>.toFirstResult() = filter { it !is Resource.Loading }.first()
|
||||
|
||||
suspend fun <T> Flow<Resource<T>>.waitForResult() = takeWhile { it is Resource.Loading }.collect()
|
||||
|
||||
inline fun <ResultType, RequestType> networkBoundResource(
|
||||
mutex: Mutex = Mutex(),
|
||||
showSavedOnLoading: Boolean = true,
|
||||
crossinline isResultEmpty: (ResultType) -> Boolean,
|
||||
crossinline query: () -> Flow<ResultType>,
|
||||
crossinline fetch: suspend (ResultType) -> RequestType,
|
||||
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
|
||||
crossinline onFetchFailed: (Throwable) -> Unit = { },
|
||||
crossinline shouldFetch: (ResultType) -> Boolean = { true },
|
||||
crossinline filterResult: (ResultType) -> ResultType = { it }
|
||||
) = flow {
|
||||
emit(Resource.Loading())
|
||||
|
||||
val data = query().first()
|
||||
emitAll(if (shouldFetch(data)) {
|
||||
val filteredResult = filterResult(data)
|
||||
|
||||
if (showSavedOnLoading && !isResultEmpty(filteredResult)) {
|
||||
emit(Resource.Intermediate(filteredResult))
|
||||
data class Resource<T>(val status: Status, val data: T?, val error: Throwable?) {
|
||||
companion object {
|
||||
fun <T> success(data: T?): Resource<T> {
|
||||
return Resource(Status.SUCCESS, data, null)
|
||||
}
|
||||
|
||||
try {
|
||||
val newData = fetch(data)
|
||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||
query().map { Resource.Success(filterResult(it)) }
|
||||
} catch (throwable: Throwable) {
|
||||
onFetchFailed(throwable)
|
||||
query().map { Resource.Error(throwable) }
|
||||
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
|
||||
return Resource(Status.ERROR, data, error)
|
||||
}
|
||||
} else {
|
||||
query().map { Resource.Success(filterResult(it)) }
|
||||
})
|
||||
|
||||
fun <T> loading(data: T? = null): Resource<T> {
|
||||
return Resource(Status.LOADING, data, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmName("networkBoundResourceWithMap")
|
||||
inline fun <ResultType, RequestType, T> networkBoundResource(
|
||||
mutex: Mutex = Mutex(),
|
||||
showSavedOnLoading: Boolean = true,
|
||||
crossinline isResultEmpty: (T) -> Boolean,
|
||||
crossinline query: () -> Flow<ResultType>,
|
||||
crossinline fetch: suspend (ResultType) -> RequestType,
|
||||
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
|
||||
crossinline onFetchFailed: (Throwable) -> Unit = { },
|
||||
crossinline shouldFetch: (ResultType) -> Boolean = { true },
|
||||
crossinline mapResult: (ResultType) -> T
|
||||
) = flow {
|
||||
emit(Resource.Loading())
|
||||
|
||||
val data = query().first()
|
||||
emitAll(if (shouldFetch(data)) {
|
||||
val mappedResult = mapResult(data)
|
||||
|
||||
if (showSavedOnLoading && !isResultEmpty(mappedResult)) {
|
||||
emit(Resource.Intermediate(mappedResult))
|
||||
}
|
||||
try {
|
||||
val newData = fetch(data)
|
||||
mutex.withLock { saveFetchResult(query().first(), newData) }
|
||||
query().map { Resource.Success(mapResult(it)) }
|
||||
} catch (throwable: Throwable) {
|
||||
onFetchFailed(throwable)
|
||||
query().map { Resource.Error(throwable) }
|
||||
}
|
||||
} else {
|
||||
query().map { Resource.Success(mapResult(it)) }
|
||||
})
|
||||
enum class Status {
|
||||
LOADING,
|
||||
SUCCESS,
|
||||
ERROR
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package io.github.wulkanowy.data.api
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import retrofit2.http.GET
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
interface AdminMessageService {
|
||||
|
||||
@GET("/v1.json")
|
||||
suspend fun getAdminMessages(): List<AdminMessage>
|
||||
}
|
|
@ -1,11 +1,105 @@
|
|||
package io.github.wulkanowy.data.db
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.*
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
||||
import io.github.wulkanowy.data.db.dao.*
|
||||
import io.github.wulkanowy.data.db.entities.*
|
||||
import io.github.wulkanowy.data.db.migrations.*
|
||||
import androidx.room.TypeConverters
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
||||
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
||||
import io.github.wulkanowy.data.db.dao.ConferenceDao
|
||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||
import io.github.wulkanowy.data.db.dao.ExamDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeDao
|
||||
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
|
||||
import io.github.wulkanowy.data.db.dao.GradeSummaryDao
|
||||
import io.github.wulkanowy.data.db.dao.HomeworkDao
|
||||
import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||
import io.github.wulkanowy.data.db.dao.MobileDeviceDao
|
||||
import io.github.wulkanowy.data.db.dao.NoteDao
|
||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
||||
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
||||
import io.github.wulkanowy.data.db.dao.SchoolDao
|
||||
import io.github.wulkanowy.data.db.dao.SemesterDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentDao
|
||||
import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
||||
import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.data.db.entities.Exam
|
||||
import io.github.wulkanowy.data.db.entities.Grade
|
||||
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradePointsStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
|
||||
import io.github.wulkanowy.data.db.entities.GradeSummary
|
||||
import io.github.wulkanowy.data.db.entities.Homework
|
||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.data.db.entities.Note
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.ReportingUnit
|
||||
import io.github.wulkanowy.data.db.entities.School
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentInfo
|
||||
import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.data.db.entities.Teacher
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.db.migrations.Migration10
|
||||
import io.github.wulkanowy.data.db.migrations.Migration11
|
||||
import io.github.wulkanowy.data.db.migrations.Migration12
|
||||
import io.github.wulkanowy.data.db.migrations.Migration13
|
||||
import io.github.wulkanowy.data.db.migrations.Migration14
|
||||
import io.github.wulkanowy.data.db.migrations.Migration15
|
||||
import io.github.wulkanowy.data.db.migrations.Migration16
|
||||
import io.github.wulkanowy.data.db.migrations.Migration17
|
||||
import io.github.wulkanowy.data.db.migrations.Migration18
|
||||
import io.github.wulkanowy.data.db.migrations.Migration19
|
||||
import io.github.wulkanowy.data.db.migrations.Migration2
|
||||
import io.github.wulkanowy.data.db.migrations.Migration20
|
||||
import io.github.wulkanowy.data.db.migrations.Migration21
|
||||
import io.github.wulkanowy.data.db.migrations.Migration22
|
||||
import io.github.wulkanowy.data.db.migrations.Migration23
|
||||
import io.github.wulkanowy.data.db.migrations.Migration24
|
||||
import io.github.wulkanowy.data.db.migrations.Migration25
|
||||
import io.github.wulkanowy.data.db.migrations.Migration26
|
||||
import io.github.wulkanowy.data.db.migrations.Migration27
|
||||
import io.github.wulkanowy.data.db.migrations.Migration28
|
||||
import io.github.wulkanowy.data.db.migrations.Migration29
|
||||
import io.github.wulkanowy.data.db.migrations.Migration3
|
||||
import io.github.wulkanowy.data.db.migrations.Migration30
|
||||
import io.github.wulkanowy.data.db.migrations.Migration31
|
||||
import io.github.wulkanowy.data.db.migrations.Migration32
|
||||
import io.github.wulkanowy.data.db.migrations.Migration33
|
||||
import io.github.wulkanowy.data.db.migrations.Migration34
|
||||
import io.github.wulkanowy.data.db.migrations.Migration35
|
||||
import io.github.wulkanowy.data.db.migrations.Migration36
|
||||
import io.github.wulkanowy.data.db.migrations.Migration37
|
||||
import io.github.wulkanowy.data.db.migrations.Migration38
|
||||
import io.github.wulkanowy.data.db.migrations.Migration39
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
import io.github.wulkanowy.data.db.migrations.Migration7
|
||||
import io.github.wulkanowy.data.db.migrations.Migration8
|
||||
import io.github.wulkanowy.data.db.migrations.Migration9
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -40,13 +134,6 @@ import javax.inject.Singleton
|
|||
StudentInfo::class,
|
||||
TimetableHeader::class,
|
||||
SchoolAnnouncement::class,
|
||||
Notification::class,
|
||||
AdminMessage::class
|
||||
],
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 44, to = 45),
|
||||
AutoMigration(from = 46, to = 47),
|
||||
AutoMigration(from = 47, to = 48),
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
|
@ -55,7 +142,7 @@ import javax.inject.Singleton
|
|||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 48
|
||||
const val VERSION_SCHEMA = 39
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
|
@ -96,12 +183,6 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
Migration37(),
|
||||
Migration38(),
|
||||
Migration39(),
|
||||
Migration40(),
|
||||
Migration41(sharedPrefProvider),
|
||||
Migration42(),
|
||||
Migration43(),
|
||||
Migration44(),
|
||||
Migration46(),
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
|
@ -171,8 +252,4 @@ abstract class AppDatabase : RoomDatabase() {
|
|||
abstract val timetableHeaderDao: TimetableHeaderDao
|
||||
|
||||
abstract val schoolAnnouncementDao: SchoolAnnouncementDao
|
||||
|
||||
abstract val notificationDao: NotificationDao
|
||||
|
||||
abstract val adminMessagesDao: AdminMessageDao
|
||||
}
|
||||
|
|
|
@ -1,36 +1,47 @@
|
|||
package io.github.wulkanowy.data.db
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.*
|
||||
import java.util.*
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import io.github.wulkanowy.data.db.adapters.PairAdapterFactory
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.Month
|
||||
import java.time.ZoneOffset
|
||||
import java.util.*
|
||||
import java.util.Date
|
||||
|
||||
class Converters {
|
||||
|
||||
private val json = Json
|
||||
private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() }
|
||||
|
||||
private val integerListAdapter by lazy {
|
||||
moshi.adapter<List<Int>>(Types.newParameterizedType(List::class.java, Integer::class.java))
|
||||
}
|
||||
|
||||
private val stringListPairAdapter by lazy {
|
||||
moshi.adapter<List<Pair<String, String>>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java))
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun timestampToLocalDate(value: Long?): LocalDate? =
|
||||
value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
|
||||
fun timestampToDate(value: Long?): LocalDate? = value?.run {
|
||||
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
|
||||
fun dateToTimestamp(date: LocalDate?): Long? {
|
||||
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
|
||||
fun timestampToTime(value: Long?): LocalDateTime? = value?.let {
|
||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
|
||||
fun timeToTimestamp(date: LocalDateTime?): Long? {
|
||||
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun monthToInt(month: Month?) = month?.value
|
||||
|
@ -40,32 +51,21 @@ class Converters {
|
|||
|
||||
@TypeConverter
|
||||
fun intListToJson(list: List<Int>): String {
|
||||
return json.encodeToString(list)
|
||||
return integerListAdapter.toJson(list)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToIntList(value: String): List<Int> {
|
||||
return json.decodeFromString(value)
|
||||
return integerListAdapter.fromJson(value).orEmpty()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun stringPairListToJson(list: List<Pair<String, String>>): String {
|
||||
return json.encodeToString(list)
|
||||
return stringListPairAdapter.toJson(list)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun jsonToStringPairList(value: String): List<Pair<String, String>> {
|
||||
return try {
|
||||
json.decodeFromString(value)
|
||||
} catch (e: SerializationException) {
|
||||
emptyList() // handle errors from old gson Pair serialized data
|
||||
}
|
||||
return stringListPairAdapter.fromJson(value).orEmpty()
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun destinationToString(destination: Destination) = json.encodeToString(destination)
|
||||
|
||||
@TypeConverter
|
||||
fun stringToDestination(destination: String): Destination = json.decodeFromString(destination)
|
||||
|
||||
}
|
||||
|
|
|
@ -22,14 +22,11 @@ class SharedPrefProvider @Inject constructor(
|
|||
|
||||
fun getString(key: String) = sharedPref.getString(key, null)
|
||||
|
||||
fun getString(key: String, defaultValue: String): String =
|
||||
sharedPref.getString(key, defaultValue) ?: defaultValue
|
||||
fun getString(key: String, defaultValue: String): String = sharedPref.getString(key, defaultValue) ?: defaultValue
|
||||
|
||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean =
|
||||
sharedPref.getBoolean(key, defaultValue)
|
||||
fun getBoolean(key: String, defaultValue: Boolean): Boolean = sharedPref.getBoolean(key, defaultValue)
|
||||
|
||||
fun putBoolean(key: String, value: Boolean, sync: Boolean = false) =
|
||||
sharedPref.edit(sync) { putBoolean(key, value) }
|
||||
fun putBoolean(key: String, value: Boolean, sync: Boolean = false) = sharedPref.edit(sync) { putBoolean(key, value) }
|
||||
|
||||
fun putString(key: String, value: String?, sync: Boolean = false) {
|
||||
sharedPref.edit(sync) { putString(key, value) }
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
package io.github.wulkanowy.data.db.adapters
|
||||
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.JsonReader
|
||||
import com.squareup.moshi.JsonWriter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import java.lang.reflect.ParameterizedType
|
||||
import java.lang.reflect.Type
|
||||
|
||||
object PairAdapterFactory : JsonAdapter.Factory {
|
||||
|
||||
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
|
||||
if (type !is ParameterizedType || List::class.java != type.rawType) return null
|
||||
if (type.actualTypeArguments[0] != Pair::class.java) return null
|
||||
|
||||
val listType = Types.newParameterizedType(List::class.java, Map::class.java, String::class.java)
|
||||
val listAdapter = moshi.adapter<List<Map<String, String>>>(listType)
|
||||
|
||||
val mapType = Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java)
|
||||
val mapAdapter = moshi.adapter<Map<String, String>>(mapType)
|
||||
|
||||
return PairAdapter(listAdapter, mapAdapter)
|
||||
}
|
||||
|
||||
private class PairAdapter(
|
||||
private val listAdapter: JsonAdapter<List<Map<String, String>>>,
|
||||
private val mapAdapter: JsonAdapter<Map<String, String>>,
|
||||
) : JsonAdapter<List<Pair<String, String>>>() {
|
||||
|
||||
override fun toJson(writer: JsonWriter, value: List<Pair<String, String>>?) {
|
||||
writer.beginArray()
|
||||
value?.forEach {
|
||||
writer.beginObject()
|
||||
writer.name("first").value(it.first)
|
||||
writer.name("second").value(it.second)
|
||||
writer.endObject()
|
||||
}
|
||||
writer.endArray()
|
||||
}
|
||||
|
||||
override fun fromJson(reader: JsonReader): List<Pair<String, String>>? {
|
||||
return if (reader.peek() == JsonReader.Token.BEGIN_OBJECT) deserializeMoshiMap(reader)
|
||||
else deserializeGsonPair(reader)
|
||||
}
|
||||
|
||||
// for compatibility with 0.21.0
|
||||
private fun deserializeMoshiMap(reader: JsonReader): List<Pair<String, String>>? {
|
||||
val map = mapAdapter.fromJson(reader) ?: return null
|
||||
|
||||
return map.entries.map {
|
||||
it.key to it.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun deserializeGsonPair(reader: JsonReader): List<Pair<String, String>>? {
|
||||
val list = listAdapter.fromJson(reader) ?: return null
|
||||
|
||||
return list.map {
|
||||
require(it.size == 2) {
|
||||
"pair with more or less than two elements: $list"
|
||||
}
|
||||
|
||||
it["first"].orEmpty() to it["second"].orEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import io.github.wulkanowy.data.db.entities.AdminMessage
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
abstract class AdminMessageDao : BaseDao<AdminMessage> {
|
||||
|
||||
@Query("SELECT * FROM AdminMessages")
|
||||
abstract fun loadAll(): Flow<List<AdminMessage>>
|
||||
|
||||
@Transaction
|
||||
open suspend fun removeOldAndSaveNew(
|
||||
oldMessages: List<AdminMessage>,
|
||||
newMessages: List<AdminMessage>
|
||||
) {
|
||||
deleteAll(oldMessages)
|
||||
insertAll(newMessages)
|
||||
}
|
||||
}
|
|
@ -11,11 +11,6 @@ import javax.inject.Singleton
|
|||
@Dao
|
||||
interface AttendanceDao : BaseDao<Attendance> {
|
||||
|
||||
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :start AND date <= :end")
|
||||
fun loadAll(
|
||||
diaryId: Int,
|
||||
studentId: Int,
|
||||
start: LocalDate,
|
||||
end: LocalDate
|
||||
): Flow<List<Attendance>>
|
||||
@Query("SELECT * FROM Attendance 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<Attendance>>
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.Dao
|
|||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Conference
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Dao
|
||||
|
@ -12,5 +12,5 @@ import javax.inject.Singleton
|
|||
interface ConferenceDao : BaseDao<Conference> {
|
||||
|
||||
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
|
||||
fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow<List<Conference>>
|
||||
fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow<List<Conference>>
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Dao
|
||||
interface NotificationDao : BaseDao<Notification> {
|
||||
|
||||
@Query("SELECT * FROM Notifications WHERE student_id = :studentId OR student_id = -1")
|
||||
fun loadAll(studentId: Long): Flow<List<Notification>>
|
||||
}
|
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC")
|
||||
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId")
|
||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy.ABORT
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
|
@ -33,10 +38,6 @@ abstract class StudentDao {
|
|||
@Query("SELECT * FROM Students")
|
||||
abstract suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Students WHERE id = :id")
|
||||
abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters?
|
||||
|
||||
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||
abstract suspend fun updateCurrent(id: Long)
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import androidx.room.Query
|
|||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.time.LocalDate
|
||||
import java.util.UUID
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Dao
|
||||
|
@ -13,13 +12,5 @@ import javax.inject.Singleton
|
|||
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
|
||||
|
||||
@Query("SELECT * FROM TimetableAdditional 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<TimetableAdditional>>
|
||||
|
||||
@Query("DELETE FROM TimetableAdditional WHERE repeat_id = :repeatId")
|
||||
suspend fun deleteAllByRepeatId(repeatId: UUID)
|
||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<TimetableAdditional>>
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Entity(tableName = "AdminMessages")
|
||||
data class AdminMessage(
|
||||
|
||||
@PrimaryKey
|
||||
val id: Int,
|
||||
|
||||
val title: String,
|
||||
|
||||
val content: String,
|
||||
|
||||
@ColumnInfo(name = "version_name")
|
||||
val versionMin: Int? = null,
|
||||
|
||||
@ColumnInfo(name = "version_max")
|
||||
val versionMax: Int? = null,
|
||||
|
||||
@ColumnInfo(name = "target_register_host")
|
||||
val targetRegisterHost: String? = null,
|
||||
|
||||
@ColumnInfo(name = "target_flavor")
|
||||
val targetFlavor: String? = null,
|
||||
|
||||
@ColumnInfo(name = "destination_url")
|
||||
val destinationUrl: String? = null,
|
||||
|
||||
val priority: String,
|
||||
|
||||
val type: String,
|
||||
|
||||
@ColumnInfo(name = "is_dismissible")
|
||||
val isDismissible: Boolean = false
|
||||
)
|
|
@ -47,7 +47,4 @@ data class Attendance(
|
|||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "Conferences")
|
||||
data class Conference(
|
||||
|
@ -27,7 +27,7 @@ data class Conference(
|
|||
@ColumnInfo(name = "conference_id")
|
||||
val conferenceId: Int,
|
||||
|
||||
val date: Instant,
|
||||
val date: LocalDateTime
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
|
|
@ -24,8 +24,5 @@ data class GradeSemesterStatistics(
|
|||
var id: Long = 0
|
||||
|
||||
@Transient
|
||||
var classAverage: String = ""
|
||||
|
||||
@Transient
|
||||
var studentAverage: String = ""
|
||||
var average: String = ""
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "GradesSummary")
|
||||
data class GradeSummary(
|
||||
|
@ -45,8 +45,8 @@ data class GradeSummary(
|
|||
var isFinalGradeNotified: Boolean = true
|
||||
|
||||
@ColumnInfo(name = "predicted_grade_last_change")
|
||||
var predictedGradeLastChange: Instant = Instant.now()
|
||||
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
|
||||
|
||||
@ColumnInfo(name = "final_grade_last_change")
|
||||
var finalGradeLastChange: Instant = Instant.now()
|
||||
var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
|
||||
}
|
||||
|
|
|
@ -40,7 +40,4 @@ data class Homework(
|
|||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
|
||||
@ColumnInfo(name = "is_added_by_user")
|
||||
var isAddedByUser: Boolean = false
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "Messages")
|
||||
data class Message(
|
||||
|
@ -29,7 +29,7 @@ data class Message(
|
|||
|
||||
val subject: String,
|
||||
|
||||
val date: Instant,
|
||||
val date: LocalDateTime,
|
||||
|
||||
@ColumnInfo(name = "folder_id")
|
||||
val folderId: Int,
|
||||
|
|
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "MobileDevices")
|
||||
data class MobileDevice(
|
||||
|
@ -17,7 +17,7 @@ data class MobileDevice(
|
|||
|
||||
val name: String,
|
||||
|
||||
val date: Instant,
|
||||
val date: LocalDateTime
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
import java.time.Instant
|
||||
|
||||
@Entity(tableName = "Notifications")
|
||||
data class Notification(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Long,
|
||||
|
||||
val title: String,
|
||||
|
||||
val content: String,
|
||||
|
||||
val type: NotificationType,
|
||||
|
||||
@ColumnInfo(defaultValue = "{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}")
|
||||
val destination: Destination,
|
||||
|
||||
val date: Instant,
|
||||
|
||||
val data: String? = null
|
||||
) {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
}
|
|
@ -3,9 +3,10 @@ package io.github.wulkanowy.data.db.entities
|
|||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.io.Serializable
|
||||
|
||||
@kotlinx.serialization.Serializable
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Entity(tableName = "Recipients")
|
||||
data class Recipient(
|
||||
|
||||
|
|
|
@ -7,12 +7,7 @@ import androidx.room.PrimaryKey
|
|||
import java.io.Serializable
|
||||
import java.time.LocalDate
|
||||
|
||||
@Entity(
|
||||
tableName = "Semesters", indices = [Index(
|
||||
value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"],
|
||||
unique = true
|
||||
)]
|
||||
)
|
||||
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
|
||||
data class Semester(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
|
@ -21,9 +16,6 @@ data class Semester(
|
|||
@ColumnInfo(name = "diary_id")
|
||||
val diaryId: Int,
|
||||
|
||||
@ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0")
|
||||
val kindergartenDiaryId: Int,
|
||||
|
||||
@ColumnInfo(name = "diary_name")
|
||||
val diaryName: String,
|
||||
|
||||
|
@ -45,11 +37,12 @@ data class Semester(
|
|||
|
||||
@ColumnInfo(name = "unit_id")
|
||||
val unitId: Int
|
||||
) : Serializable {
|
||||
): Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
|
||||
@ColumnInfo(name = "is_current")
|
||||
var current: Boolean = false
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.room.Entity
|
|||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(
|
||||
tableName = "Students",
|
||||
|
@ -74,7 +74,7 @@ data class Student(
|
|||
val isCurrent: Boolean,
|
||||
|
||||
@ColumnInfo(name = "registration_date")
|
||||
val registrationDate: Instant,
|
||||
val registrationDate: LocalDateTime
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
|
|
|
@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "Timetable")
|
||||
data class Timetable(
|
||||
|
@ -18,9 +18,9 @@ data class Timetable(
|
|||
|
||||
val number: Int,
|
||||
|
||||
val start: Instant,
|
||||
val start: LocalDateTime,
|
||||
|
||||
val end: Instant,
|
||||
val end: LocalDateTime,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
@ -50,7 +50,4 @@ data class Timetable(
|
|||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@ColumnInfo(name = "is_notified")
|
||||
var isNotified: Boolean = true
|
||||
}
|
||||
|
|
|
@ -4,9 +4,8 @@ import androidx.room.ColumnInfo
|
|||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
import java.time.LocalDateTime
|
||||
|
||||
@Entity(tableName = "TimetableAdditional")
|
||||
data class TimetableAdditional(
|
||||
|
@ -17,9 +16,9 @@ data class TimetableAdditional(
|
|||
@ColumnInfo(name = "diary_id")
|
||||
val diaryId: Int,
|
||||
|
||||
val start: Instant,
|
||||
val start: LocalDateTime,
|
||||
|
||||
val end: Instant,
|
||||
val end: LocalDateTime,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
|
@ -28,10 +27,4 @@ data class TimetableAdditional(
|
|||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
|
||||
@ColumnInfo(name = "repeat_id", defaultValue = "NULL")
|
||||
var repeatId: UUID? = null
|
||||
|
||||
@ColumnInfo(name = "is_added_by_user", defaultValue = "0")
|
||||
var isAddedByUser: Boolean = false
|
||||
}
|
||||
|
|
|
@ -43,14 +43,12 @@ class Migration12 : Migration(11, 12) {
|
|||
|
||||
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
|
||||
val students = mutableListOf<Int>()
|
||||
database.query("SELECT student_id FROM Students").use {
|
||||
if (it.moveToFirst()) {
|
||||
do {
|
||||
students.add(it.getInt(0))
|
||||
} while (it.moveToNext())
|
||||
}
|
||||
val studentsCursor = database.query("SELECT student_id FROM Students")
|
||||
if (studentsCursor.moveToFirst()) {
|
||||
do {
|
||||
students.add(studentsCursor.getInt(0))
|
||||
} while (studentsCursor.moveToNext())
|
||||
}
|
||||
|
||||
return students
|
||||
}
|
||||
|
||||
|
|
|
@ -25,14 +25,12 @@ class Migration13 : Migration(12, 13) {
|
|||
|
||||
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
||||
val students = mutableListOf<Pair<Int, String>>()
|
||||
database.query("SELECT id, school_name FROM Students").use {
|
||||
if (it.moveToFirst()) {
|
||||
do {
|
||||
students.add(it.getInt(0) to it.getString(1))
|
||||
} while (it.moveToNext())
|
||||
}
|
||||
val studentsCursor = database.query("SELECT id, school_name FROM Students")
|
||||
if (studentsCursor.moveToFirst()) {
|
||||
do {
|
||||
students.add(studentsCursor.getInt(0) to studentsCursor.getString(1))
|
||||
} while (studentsCursor.moveToNext())
|
||||
}
|
||||
|
||||
return students
|
||||
}
|
||||
|
||||
|
@ -44,14 +42,12 @@ class Migration13 : Migration(12, 13) {
|
|||
|
||||
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
|
||||
val students = mutableListOf<Pair<Int, Int>>()
|
||||
database.query("SELECT student_id, class_id FROM Students").use {
|
||||
if (it.moveToFirst()) {
|
||||
do {
|
||||
students.add(it.getInt(0) to it.getInt(1))
|
||||
} while (it.moveToNext())
|
||||
}
|
||||
val studentsCursor = database.query("SELECT student_id, class_id FROM Students")
|
||||
if (studentsCursor.moveToFirst()) {
|
||||
do {
|
||||
students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1))
|
||||
} while (studentsCursor.moveToNext())
|
||||
}
|
||||
|
||||
return students
|
||||
}
|
||||
|
||||
|
|
|
@ -22,28 +22,24 @@ class Migration27 : Migration(26, 27) {
|
|||
|
||||
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
|
||||
val students = mutableListOf<Triple<Long, Int, String>>()
|
||||
database.query("SELECT id, user_login_id, student_name FROM Students").use {
|
||||
if (it.moveToFirst()) {
|
||||
do {
|
||||
students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
|
||||
} while (it.moveToNext())
|
||||
}
|
||||
val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students")
|
||||
if (studentsCursor.moveToFirst()) {
|
||||
do {
|
||||
students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2)))
|
||||
} while (studentsCursor.moveToNext())
|
||||
}
|
||||
|
||||
return students
|
||||
}
|
||||
|
||||
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
||||
val units = mutableListOf<Pair<Int, String>>()
|
||||
database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
|
||||
if (it.moveToFirst()) {
|
||||
do {
|
||||
units.add(it.getInt(0) to it.getString(1))
|
||||
} while (it.moveToNext())
|
||||
}
|
||||
val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits")
|
||||
if (unitsCursor.moveToFirst()) {
|
||||
do {
|
||||
units.add(unitsCursor.getInt(0) to unitsCursor.getString(1))
|
||||
} while (unitsCursor.moveToNext())
|
||||
}
|
||||
|
||||
|
||||
return units
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,15 @@ class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
|
|||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
|
||||
|
||||
database.query("SELECT * FROM Students").use {
|
||||
while (it.moveToNext()) {
|
||||
val studentId = it.getLongOrNull(0)
|
||||
database.execSQL(
|
||||
"""
|
||||
UPDATE Students
|
||||
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
|
||||
WHERE id = $studentId
|
||||
"""
|
||||
)
|
||||
}
|
||||
val studentsCursor = database.query("SELECT * FROM Students")
|
||||
|
||||
while (studentsCursor.moveToNext()) {
|
||||
val studentId = studentsCursor.getLongOrNull(0)
|
||||
database.execSQL(
|
||||
"""UPDATE Students
|
||||
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
|
||||
WHERE id = $studentId"""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration40 : Migration(39, 40) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `Notifications` (
|
||||
`student_id` INTEGER NOT NULL,
|
||||
`title` TEXT NOT NULL,
|
||||
`content` TEXT NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
`date` INTEGER NOT NULL,
|
||||
`data` TEXT,
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
import io.github.wulkanowy.data.enums.GradeExpandMode
|
||||
|
||||
class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
migrateSharedPreferences()
|
||||
database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
|
||||
private fun migrateSharedPreferences() {
|
||||
if (sharedPrefProvider.getBoolean("pref_key_expand_grade", false)) {
|
||||
sharedPrefProvider.putString("pref_key_expand_grade_mode", GradeExpandMode.ALWAYS_EXPANDED.value)
|
||||
}
|
||||
sharedPrefProvider.delete("pref_key_expand_grade")
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration42 : Migration(41, 42) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""CREATE TABLE IF NOT EXISTS `AdminMessages` (
|
||||
`id` INTEGER NOT NULL,
|
||||
`title` TEXT NOT NULL,
|
||||
`content` TEXT NOT NULL,
|
||||
`version_name` INTEGER,
|
||||
`version_max` INTEGER,
|
||||
`target_register_host` TEXT,
|
||||
`target_flavor` TEXT,
|
||||
`destination_url` TEXT,
|
||||
`priority` TEXT NOT NULL,
|
||||
`type` TEXT NOT NULL,
|
||||
PRIMARY KEY(`id`))"""
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration43 : Migration(42, 43) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||
database.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration44 : Migration(43, 44) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
|
||||
class Migration46 : Migration(45, 46) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
migrateConferences(database)
|
||||
migrateMessages(database)
|
||||
migrateMobileDevices(database)
|
||||
migrateNotifications(database)
|
||||
migrateTimetable(database)
|
||||
migrateTimetableAdditional(database)
|
||||
}
|
||||
|
||||
private fun migrateConferences(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM Conferences").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
|
||||
val timestampUtc = timestampLocal.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateMessages(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM Messages").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
|
||||
val timestampUtc = timestampLocal.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateMobileDevices(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM MobileDevices").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
|
||||
val timestampUtc = timestampLocal.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateNotifications(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM Notifications").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
|
||||
val timestampUtc = timestampLocal.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateTimetable(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM Timetable").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
|
||||
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
|
||||
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
|
||||
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) {
|
||||
database.query("SELECT * FROM TimetableAdditional").use {
|
||||
while (it.moveToNext()) {
|
||||
val id = it.getLong(it.getColumnIndexOrThrow("id"))
|
||||
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
|
||||
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
|
||||
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
|
||||
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
|
||||
|
||||
database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this)
|
||||
.atZone(ZoneOffset.UTC)
|
||||
.withZoneSameLocal(ZoneId.of("Europe/Warsaw"))
|
||||
.withZoneSameInstant(ZoneId.systemDefault())
|
||||
.toInstant()
|
||||
.toEpochMilli()
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class AppTheme(val value: String) {
|
||||
SYSTEM("system"),
|
||||
LIGHT("light"),
|
||||
DARK("dark"),
|
||||
BLACK("black");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: LIGHT
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
import java.io.Serializable
|
||||
|
||||
enum class GradeColorTheme(val value: String) : Serializable {
|
||||
VULCAN("vulcan"),
|
||||
MATERIAL("material"),
|
||||
GRADE_COLOR("grade_color");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: VULCAN
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class GradeExpandMode(val value: String) {
|
||||
ONE("one"),
|
||||
UNLIMITED("any"),
|
||||
ALWAYS_EXPANDED("always");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: ONE
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class GradeSortingMode(val value: String) {
|
||||
ALPHABETIC("alphabetic"),
|
||||
DATE("date");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: ALPHABETIC
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package io.github.wulkanowy.data.enums
|
||||
|
||||
enum class TimetableMode(val value: String) {
|
||||
WHOLE_PLAN("yes"),
|
||||
ONLY_CURRENT_GROUP("no"),
|
||||
SMALL_OTHER_GROUP("small");
|
||||
|
||||
companion object {
|
||||
fun getByValue(value: String) = values().find { it.value == value } ?: ONLY_CURRENT_GROUP
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ fun List<SdkConference>.mapToEntities(semester: Semester) = map {
|
|||
diaryId = semester.diaryId,
|
||||
agenda = it.agenda,
|
||||
conferenceId = it.id,
|
||||
date = it.dateZoned.toInstant(),
|
||||
date = it.date,
|
||||
presentOnConference = it.presentOnConference,
|
||||
subject = it.subject,
|
||||
title = it.title
|
||||
|
|
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
|
|||
|
||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||
SchoolAnnouncement(
|
||||
studentId = student.userLoginId,
|
||||
studentId = student.studentId,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
content = it.content,
|
||||
|
|
|
@ -4,7 +4,7 @@ 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 java.time.LocalDateTime
|
||||
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
|
||||
|
@ -18,7 +18,7 @@ fun List<SdkMessage>.mapToEntities(student: Student) = map {
|
|||
senderId = it.sender?.loginId ?: 0,
|
||||
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
|
||||
subject = it.subject.trim(),
|
||||
date = it.dateZoned?.toInstant() ?: Instant.now(),
|
||||
date = it.date ?: LocalDateTime.now(),
|
||||
folderId = it.folderId,
|
||||
unread = it.unread ?: false,
|
||||
removed = it.removed,
|
||||
|
|
|
@ -3,13 +3,13 @@ package io.github.wulkanowy.data.mappers
|
|||
import io.github.wulkanowy.data.db.entities.MobileDevice
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
|
||||
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
||||
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
|
||||
|
||||
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
|
||||
MobileDevice(
|
||||
userLoginId = semester.studentId,
|
||||
date = it.createDateZoned.toInstant(),
|
||||
date = it.createDate,
|
||||
deviceId = it.id,
|
||||
name = it.name
|
||||
)
|
||||
|
|
|
@ -7,7 +7,6 @@ fun List<SdkSemester>.mapToEntities(studentId: Int) = map {
|
|||
Semester(
|
||||
studentId = studentId,
|
||||
diaryId = it.diaryId,
|
||||
kindergartenDiaryId = it.kindergartenDiaryId,
|
||||
diaryName = it.diaryName,
|
||||
schoolYear = it.schoolYear,
|
||||
semesterId = it.semesterId,
|
||||
|
|
|
@ -2,7 +2,7 @@ package io.github.wulkanowy.data.mappers
|
|||
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
||||
|
||||
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
|
||||
|
@ -24,7 +24,7 @@ fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) =
|
|||
scrapperBaseUrl = it.scrapperBaseUrl,
|
||||
loginType = it.loginType.name,
|
||||
isCurrent = false,
|
||||
registrationDate = Instant.now(),
|
||||
registrationDate = LocalDateTime.now(),
|
||||
mobileBaseUrl = it.mobileBaseUrl,
|
||||
privateKey = it.privateKey,
|
||||
certificateKey = it.certificateKey,
|
||||
|
|
|
@ -21,8 +21,8 @@ fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
|
|||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
number = it.number,
|
||||
start = it.startZoned.toInstant(),
|
||||
end = it.endZoned.toInstant(),
|
||||
start = it.start,
|
||||
end = it.end,
|
||||
date = it.date,
|
||||
subject = it.subject,
|
||||
subjectOld = it.subjectOld,
|
||||
|
@ -45,8 +45,8 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
|
|||
diaryId = semester.diaryId,
|
||||
subject = it.subject,
|
||||
date = it.date,
|
||||
start = it.startZoned.toInstant(),
|
||||
end = it.endZoned.toInstant(),
|
||||
start = it.start,
|
||||
end = it.end
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@Serializable
|
||||
@JsonClass(generateAdapter = true)
|
||||
class Contributor(
|
||||
val displayName: String,
|
||||
val githubUsername: String
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import io.github.wulkanowy.ui.modules.message.send.RecipientChipItem
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageDraft(
|
||||
val recipients: List<RecipientChipItem>,
|
||||
val subject: String,
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.PluralsRes
|
||||
import androidx.annotation.StringRes
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.modules.main.MainView
|
||||
|
||||
sealed interface Notification {
|
||||
val type: NotificationType
|
||||
val startMenu: MainView.Section
|
||||
val icon: Int
|
||||
val titleStringRes: Int
|
||||
val contentStringRes: Int
|
||||
}
|
||||
|
||||
data class MultipleNotifications(
|
||||
override val type: NotificationType,
|
||||
override val startMenu: MainView.Section,
|
||||
@DrawableRes override val icon: Int,
|
||||
@PluralsRes override val titleStringRes: Int,
|
||||
@PluralsRes override val contentStringRes: Int,
|
||||
|
||||
@PluralsRes val summaryStringRes: Int,
|
||||
val lines: List<String>,
|
||||
) : Notification
|
||||
|
||||
data class OneNotification(
|
||||
override val type: NotificationType,
|
||||
override val startMenu: MainView.Section,
|
||||
@DrawableRes override val icon: Int,
|
||||
@StringRes override val titleStringRes: Int,
|
||||
@StringRes override val contentStringRes: Int,
|
||||
|
||||
val contentValues: List<String>,
|
||||
) : Notification
|
|
@ -1,19 +0,0 @@
|
|||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||
import io.github.wulkanowy.ui.modules.Destination
|
||||
|
||||
data class NotificationData(
|
||||
val destination: Destination,
|
||||
val title: String,
|
||||
val content: String
|
||||
)
|
||||
|
||||
data class GroupNotificationData(
|
||||
val notificationDataList: List<NotificationData>,
|
||||
val title: String,
|
||||
val content: String,
|
||||
val destination: Destination,
|
||||
val type: NotificationType
|
||||
)
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.api.AdminMessageService
|
||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.utils.AppInfo
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AdminMessageRepository @Inject constructor(
|
||||
private val adminMessageService: AdminMessageService,
|
||||
private val adminMessageDao: AdminMessageDao,
|
||||
private val appInfo: AppInfo
|
||||
) {
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
suspend fun getAdminMessages(student: Student) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
query = { adminMessageDao.loadAll() },
|
||||
fetch = { adminMessageService.getAdminMessages() },
|
||||
shouldFetch = { true },
|
||||
saveFetchResult = { oldItems, newItems ->
|
||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||
},
|
||||
showSavedOnLoading = false,
|
||||
mapResult = { adminMessages ->
|
||||
adminMessages.filter { adminMessage ->
|
||||
val isCorrectRegister = adminMessage.targetRegisterHost?.let {
|
||||
student.scrapperBaseUrl.contains(it, true)
|
||||
} ?: true
|
||||
val isCorrectFlavor =
|
||||
adminMessage.targetFlavor?.equals(appInfo.buildFlavor, true) ?: true
|
||||
val isCorrectMaxVersion =
|
||||
adminMessage.versionMax?.let { it >= appInfo.versionCode } ?: true
|
||||
val isCorrectMinVersion =
|
||||
adminMessage.versionMin?.let { it <= appInfo.versionCode } ?: true
|
||||
|
||||
isCorrectRegister && isCorrectFlavor && isCorrectMaxVersion && isCorrectMinVersion
|
||||
}.maxByOrNull { it.id }
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,27 +1,25 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import android.content.res.AssetManager
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import io.github.wulkanowy.data.pojos.Contributor
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AppCreatorRepository @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val dispatchers: DispatchersProvider,
|
||||
private val json: Json,
|
||||
private val assets: AssetManager,
|
||||
private val dispatchers: DispatchersProvider
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
suspend fun getAppCreators() = withContext(dispatchers.io) {
|
||||
val inputStream = context.assets.open("contributors.json").buffered()
|
||||
json.decodeFromStream<List<Contributor>>(inputStream)
|
||||
suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val type = Types.newParameterizedType(List::class.java, Contributor::class.java)
|
||||
val adapter = moshi.adapter<List<Contributor>>(type)
|
||||
adapter.fromJson(assets.open("contributors.json").bufferedReader().use { it.readText() })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.sdk.pojo.Absent
|
||||
import io.github.wulkanowy.utils.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
|
@ -28,67 +32,30 @@ class AttendanceRepository @Inject constructor(
|
|||
|
||||
private val cacheKey = "attendance"
|
||||
|
||||
fun getAttendance(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
) = networkBoundResource(
|
||||
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, semester, start, end)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||
val attendanceToAdd = (new uniqueSubtract old).map { newAttendance ->
|
||||
newAttendance.apply { if (notify) isNotified = false }
|
||||
}
|
||||
attendanceDb.insertAll(attendanceToAdd)
|
||||
attendanceDb.insertAll(new uniqueSubtract old)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
filterResult = { it.filter { item -> item.date in start..end } }
|
||||
)
|
||||
|
||||
fun getAttendanceFromDatabase(
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate
|
||||
): Flow<List<Attendance>> {
|
||||
return attendanceDb.loadAll(semester.diaryId, semester.studentId, start, end)
|
||||
}
|
||||
|
||||
suspend fun updateTimetable(timetable: List<Attendance>) {
|
||||
return attendanceDb.updateAll(timetable)
|
||||
}
|
||||
|
||||
suspend fun excuseForAbsence(
|
||||
student: Student, semester: Semester,
|
||||
absenceList: List<Attendance>, reason: String? = null
|
||||
) {
|
||||
val items = absenceList.map { attendance ->
|
||||
suspend fun excuseForAbsence(student: Student, semester: Semester, absenceList: List<Attendance>, reason: String? = null) {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
|
||||
Absent(
|
||||
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),
|
||||
timeId = attendance.timeId
|
||||
)
|
||||
}
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.excuseForAbsence(items, reason)
|
||||
}, reason)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
|
@ -29,18 +29,16 @@ class AttendanceSummaryRepository @Inject constructor(
|
|||
student: Student,
|
||||
semester: Semester,
|
||||
subjectId: Int,
|
||||
forceRefresh: Boolean,
|
||||
forceRefresh: Boolean
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
it.isEmpty() || forceRefresh
|
||||
|| refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
},
|
||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getAttendanceSummary(subjectId)
|
||||
.mapToEntities(semester, subjectId)
|
||||
},
|
||||
|
|
|
@ -4,9 +4,14 @@ import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
@ -23,32 +28,12 @@ class CompletedLessonsRepository @Inject constructor(
|
|||
|
||||
private val cacheKey = "completed"
|
||||
|
||||
fun getCompletedLessons(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, semester, start, end)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = {
|
||||
completedLessonsDb.loadAll(
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
from = start.monday,
|
||||
end = end.sunday
|
||||
)
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
|
||||
query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getCompletedLessons(start.monday, end.sunday)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
|
|
@ -5,15 +5,17 @@ import io.github.wulkanowy.data.db.entities.Conference
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneOffset
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -33,20 +35,18 @@ class ConferenceRepository @Inject constructor(
|
|||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
startDate: Instant = Instant.EPOCH,
|
||||
startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
it.isEmpty() || forceRefresh
|
||||
|| refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
},
|
||||
query = {
|
||||
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getConferences()
|
||||
.mapToEntities(semester)
|
||||
.filter { it.date >= startDate }
|
||||
|
@ -66,7 +66,7 @@ class ConferenceRepository @Inject constructor(
|
|||
conferenceDb.loadAll(
|
||||
diaryId = semester.diaryId,
|
||||
studentId = semester.studentId,
|
||||
startDate = Instant.EPOCH,
|
||||
startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)
|
||||
)
|
||||
|
||||
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)
|
||||
|
|
|
@ -5,9 +5,14 @@ import io.github.wulkanowy.data.db.entities.Exam
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.endExamsDay
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.startExamsDay
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
|
@ -31,15 +36,14 @@ class ExamRepository @Inject constructor(
|
|||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, semester, start, end)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
it.isEmpty() || forceRefresh || isShouldBeRefreshed
|
||||
},
|
||||
query = {
|
||||
examDb.loadAll(
|
||||
|
@ -50,8 +54,7 @@ class ExamRepository @Inject constructor(
|
|||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
|
|
@ -7,14 +7,17 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.Instant
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -34,16 +37,13 @@ class GradeRepository @Inject constructor(
|
|||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = {
|
||||
//When details is empty and summary is not, app will not use summary cache - edge case
|
||||
it.first.isEmpty()
|
||||
},
|
||||
shouldFetch = { (details, summaries) ->
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
|
||||
val isShouldBeRefreshed =
|
||||
refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||
details.isEmpty() || summaries.isEmpty() || forceRefresh || isShouldBeRefreshed
|
||||
},
|
||||
query = {
|
||||
val detailsFlow = gradeDb.loadAll(semester.semesterId, semester.studentId)
|
||||
|
@ -52,7 +52,7 @@ class GradeRepository @Inject constructor(
|
|||
},
|
||||
fetch = {
|
||||
val (details, summary) = sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getGrades(semester.semesterId)
|
||||
|
||||
details.mapToEntities(semester) to summary.mapToEntities(semester)
|
||||
|
@ -71,8 +71,8 @@ class GradeRepository @Inject constructor(
|
|||
newDetails: List<Grade>,
|
||||
notify: Boolean
|
||||
) {
|
||||
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
||||
?: student.registrationDate.toLocalDate()
|
||||
val notifyBreakDate =
|
||||
oldGrades.maxByOrNull { it.date }?.date ?: student.registrationDate.toLocalDate()
|
||||
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
|
||||
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
|
||||
if (it.date >= notifyBreakDate) it.apply {
|
||||
|
@ -89,7 +89,8 @@ class GradeRepository @Inject constructor(
|
|||
) {
|
||||
gradeSummaryDb.deleteAll(oldSummaries uniqueSubtract newSummary)
|
||||
gradeSummaryDb.insertAll((newSummary uniqueSubtract oldSummaries).onEach { summary ->
|
||||
val oldSummary = oldSummaries.find { old -> old.subject == summary.subject }
|
||||
val oldSummary =
|
||||
oldSummaries.find { oldSummary -> oldSummary.subject == summary.subject }
|
||||
summary.isPredictedGradeNotified = when {
|
||||
summary.predictedGrade.isEmpty() -> true
|
||||
notify && oldSummary?.predictedGrade != summary.predictedGrade -> false
|
||||
|
@ -102,13 +103,13 @@ class GradeRepository @Inject constructor(
|
|||
}
|
||||
|
||||
summary.predictedGradeLastChange = when {
|
||||
oldSummary == null -> Instant.now()
|
||||
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
||||
oldSummary == null -> LocalDateTime.now()
|
||||
summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now()
|
||||
else -> oldSummary.predictedGradeLastChange
|
||||
}
|
||||
summary.finalGradeLastChange = when {
|
||||
oldSummary == null -> Instant.now()
|
||||
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
||||
oldSummary == null -> LocalDateTime.now()
|
||||
summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now()
|
||||
else -> oldSummary.finalGradeLastChange
|
||||
}
|
||||
})
|
||||
|
|
|
@ -11,14 +11,14 @@ import io.github.wulkanowy.data.mappers.mapPartialToStatisticItems
|
|||
import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
|
||||
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -39,24 +39,12 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
private val semesterCacheKey = "grade_stats_semester"
|
||||
private val pointsCacheKey = "grade_stats_points"
|
||||
|
||||
fun getGradesPartialStatistics(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
subjectName: String,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = partialMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(partialCacheKey, semester)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) },
|
||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getGradesPartialStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -68,40 +56,32 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
mapResult = { items ->
|
||||
when (subjectName) {
|
||||
"Wszystkie" -> {
|
||||
val summaryItem = GradePartialStatistics(
|
||||
val numerator = items.map {
|
||||
it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
|
||||
}.filterNot { it == .0 }
|
||||
(items.reversed() + GradePartialStatistics(
|
||||
studentId = semester.studentId,
|
||||
semesterId = semester.semesterId,
|
||||
subject = subjectName,
|
||||
classAverage = items.map { it.classAverage }.getSummaryAverage(),
|
||||
studentAverage = items.map { it.studentAverage }.getSummaryAverage(),
|
||||
classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
},
|
||||
studentAverage = "",
|
||||
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
|
||||
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
|
||||
)
|
||||
listOf(summaryItem) + items
|
||||
)).reversed()
|
||||
}
|
||||
else -> items.filter { it.subject == subjectName }
|
||||
}.mapPartialToStatisticItems()
|
||||
}
|
||||
)
|
||||
|
||||
fun getGradesSemesterStatistics(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
subjectName: String,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = semesterMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(semesterCacheKey, semester)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) },
|
||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getGradesSemesterStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -114,50 +94,36 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
val itemsWithAverage = items.map { item ->
|
||||
item.copy().apply {
|
||||
val denominator = item.amounts.sum()
|
||||
classAverage = if (denominator == 0) "" else {
|
||||
(item.amounts.mapIndexed { gradeValue, amount ->
|
||||
(gradeValue + 1) * amount
|
||||
}.sum().toDouble() / denominator).asAverageString()
|
||||
average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount ->
|
||||
(gradeValue + 1) * amount
|
||||
}.sum().toDouble() / denominator).let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
when (subjectName) {
|
||||
"Wszystkie" -> {
|
||||
val summaryItem = GradeSemesterStatistics(
|
||||
studentId = semester.studentId,
|
||||
semesterId = semester.semesterId,
|
||||
subject = subjectName,
|
||||
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
|
||||
studentGrade = 0,
|
||||
).apply {
|
||||
classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage()
|
||||
studentAverage = items
|
||||
.mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } }
|
||||
.average().asAverageString()
|
||||
"Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics(
|
||||
studentId = semester.studentId,
|
||||
semesterId = semester.semesterId,
|
||||
subject = subjectName,
|
||||
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
|
||||
studentGrade = 0
|
||||
).apply {
|
||||
average = itemsWithAverage.mapNotNull { it.average.replace(",", ".").toDoubleOrNull() }.average().let {
|
||||
"%.2f".format(Locale.FRANCE, it)
|
||||
}
|
||||
listOf(summaryItem) + itemsWithAverage
|
||||
}
|
||||
}).reversed()
|
||||
else -> itemsWithAverage.filter { it.subject == subjectName }
|
||||
}.mapSemesterToStatisticItems()
|
||||
}
|
||||
)
|
||||
|
||||
fun getGradesPointsStatistics(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
subjectName: String,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = pointsMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) },
|
||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getGradesPointsStatistics(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -174,19 +140,6 @@ class GradeStatisticsRepository @Inject constructor(
|
|||
}
|
||||
)
|
||||
|
||||
private fun List<String>.getSummaryAverage(): String {
|
||||
val averages = mapNotNull {
|
||||
it.replace(",", ".").toDoubleOrNull()
|
||||
}
|
||||
|
||||
return averages.average()
|
||||
.asAverageString()
|
||||
.takeIf { averages.isNotEmpty() }
|
||||
.orEmpty()
|
||||
}
|
||||
|
||||
private fun Double.asAverageString(): String = "%.2f".format(Locale.FRANCE, this)
|
||||
|
||||
private fun List<List<Int>>.sumGradeAmounts(): List<Int> {
|
||||
val result = mutableListOf(0, 0, 0, 0, 0, 0)
|
||||
forEach {
|
||||
|
|
|
@ -5,9 +5,14 @@ import io.github.wulkanowy.data.db.entities.Homework
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
|
@ -25,20 +30,16 @@ class HomeworkRepository @Inject constructor(
|
|||
private val cacheKey = "homework"
|
||||
|
||||
fun getHomework(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
start: LocalDate,
|
||||
end: LocalDate,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
student: Student, semester: Semester,
|
||||
start: LocalDate, end: LocalDate,
|
||||
forceRefresh: Boolean, notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
val isShouldBeRefreshed = refreshHelper.isShouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, semester, start, end)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
it.isEmpty() || forceRefresh || isShouldBeRefreshed
|
||||
},
|
||||
query = {
|
||||
homeworkDb.loadAll(
|
||||
|
@ -49,8 +50,7 @@ class HomeworkRepository @Inject constructor(
|
|||
)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getHomework(start.monday, end.sunday)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -58,9 +58,8 @@ class HomeworkRepository @Inject constructor(
|
|||
val homeWorkToSave = (new uniqueSubtract old).onEach {
|
||||
if (notify) it.isNotified = false
|
||||
}
|
||||
val filteredOld = old.filterNot { it.isAddedByUser }
|
||||
|
||||
homeworkDb.deleteAll(filteredOld uniqueSubtract new)
|
||||
homeworkDb.deleteAll(old uniqueSubtract new)
|
||||
homeworkDb.insertAll(homeWorkToSave)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
|
@ -77,8 +76,4 @@ class HomeworkRepository @Inject constructor(
|
|||
homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday)
|
||||
|
||||
suspend fun updateHomework(homework: List<Homework>) = homeworkDb.updateAll(homework)
|
||||
|
||||
suspend fun saveHomework(homework: Homework) = homeworkDb.insertAll(listOf(homework))
|
||||
|
||||
suspend fun deleteHomework(homework: Homework) = homeworkDb.deleteAll(listOf(homework))
|
||||
}
|
||||
|
|
|
@ -15,23 +15,24 @@ class LoggerRepository @Inject constructor(
|
|||
|
||||
suspend fun getLastLogLines() = getLastModified().readText().split("\n")
|
||||
|
||||
suspend fun getLogFiles() = withContext(dispatchers.io) {
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)
|
||||
?.filter { it.name.endsWith(".log") }!!
|
||||
suspend fun getLogFiles() = withContext(dispatchers.backgroundThread) {
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)?.filter {
|
||||
it.name.endsWith(".log")
|
||||
}!!
|
||||
}
|
||||
|
||||
private suspend fun getLastModified() = withContext(dispatchers.io) {
|
||||
var lastModifiedTime = Long.MIN_VALUE
|
||||
var chosenFile: File? = null
|
||||
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)
|
||||
?.forEach { file ->
|
||||
private suspend fun getLastModified(): File {
|
||||
return withContext(dispatchers.backgroundThread) {
|
||||
var lastModifiedTime = Long.MIN_VALUE
|
||||
var chosenFile: File? = null
|
||||
File(context.filesDir.absolutePath).listFiles(File::isFile)?.forEach { file ->
|
||||
if (file.lastModified() > lastModifiedTime) {
|
||||
lastModifiedTime = file.lastModified()
|
||||
chosenFile = file
|
||||
}
|
||||
}
|
||||
|
||||
chosenFile ?: throw FileNotFoundException("Log file not found")
|
||||
if (chosenFile == null) throw FileNotFoundException("Log file not found")
|
||||
chosenFile!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.LuckyNumberDao
|
|||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
|
@ -23,18 +23,11 @@ class LuckyNumberRepository @Inject constructor(
|
|||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getLuckyNumber(
|
||||
student: Student,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
) = networkBoundResource(
|
||||
fun getLuckyNumber(student: Student, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||
fetch = {
|
||||
sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student)
|
||||
},
|
||||
fetch = { sdk.init(student).getLuckyNumber(student.schoolShortName)?.mapToEntity(student) },
|
||||
saveFetchResult = { old, new ->
|
||||
if (new != old) {
|
||||
old?.let { luckyNumberDb.deleteAll(listOfNotNull(it)) }
|
||||
|
@ -48,11 +41,9 @@ class LuckyNumberRepository @Inject constructor(
|
|||
fun getLuckyNumberHistory(student: Student, start: LocalDate, end: LocalDate) =
|
||||
luckyNumberDb.getAll(student.studentId, start, end)
|
||||
|
||||
suspend fun getNotNotifiedLuckyNumber(student: Student) =
|
||||
luckyNumberDb.load(student.studentId, now()).map {
|
||||
if (it?.isNotified == false) it else null
|
||||
}.first()
|
||||
suspend fun getNotNotifiedLuckyNumber(student: Student) = luckyNumberDb.load(student.studentId, now()).map {
|
||||
if (it?.isNotified == false) it else null
|
||||
}.first()
|
||||
|
||||
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) =
|
||||
luckyNumberDb.updateAll(listOfNotNull(luckyNumber))
|
||||
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = luckyNumberDb.updateAll(listOfNotNull(luckyNumber))
|
||||
}
|
||||
|
|
|
@ -1,31 +1,34 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.Resource
|
||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||
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.db.entities.Message
|
||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||
import io.github.wulkanowy.data.db.entities.Recipient
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.enums.MessageFolder
|
||||
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||
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.data.pojos.MessageDraftJsonAdapter
|
||||
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.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
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
|
||||
|
@ -39,7 +42,7 @@ class MessageRepository @Inject constructor(
|
|||
@ApplicationContext private val context: Context,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val sharedPrefProvider: SharedPrefProvider,
|
||||
private val json: Json,
|
||||
private val moshi: Moshi,
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
@ -48,19 +51,14 @@ class MessageRepository @Inject constructor(
|
|||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun getMessages(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
folder: MessageFolder,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
student: Student, semester: Semester,
|
||||
folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false
|
||||
): Flow<Resource<List<Message>>> = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, student, folder)
|
||||
it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(
|
||||
getRefreshKey(cacheKey, student, folder)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
|
||||
fetch = {
|
||||
|
@ -79,8 +77,7 @@ class MessageRepository @Inject constructor(
|
|||
)
|
||||
|
||||
private fun getMessagesWithReadByChange(
|
||||
old: List<Message>,
|
||||
new: List<Message>,
|
||||
old: List<Message>, new: List<Message>,
|
||||
setNotified: Boolean
|
||||
): List<Message> {
|
||||
val oldMeta = old.map { Triple(it, it.readBy, it.unreadBy) }
|
||||
|
@ -99,13 +96,10 @@ class MessageRepository @Inject constructor(
|
|||
}
|
||||
|
||||
fun getMessage(
|
||||
student: Student,
|
||||
message: Message,
|
||||
markAsRead: Boolean = false,
|
||||
student: Student, message: Message, markAsRead: Boolean = false
|
||||
): Flow<Resource<MessageWithAttachment?>> = networkBoundResource(
|
||||
isResultEmpty = { it?.message?.content.isNullOrBlank() },
|
||||
shouldFetch = {
|
||||
checkNotNull(it) { "This message no longer exist!" }
|
||||
checkNotNull(it, { "This message no longer exist!" })
|
||||
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||
it.message.unread || it.message.content.isEmpty()
|
||||
},
|
||||
|
@ -121,7 +115,7 @@ class MessageRepository @Inject constructor(
|
|||
}
|
||||
},
|
||||
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||
checkNotNull(old, { "Fetched message no longer exist!" })
|
||||
messagesDb.updateAll(listOf(old.message.apply {
|
||||
id = old.message.id
|
||||
unread = !markAsRead
|
||||
|
@ -141,42 +135,33 @@ class MessageRepository @Inject constructor(
|
|||
}
|
||||
|
||||
suspend fun sendMessage(
|
||||
student: Student,
|
||||
subject: String,
|
||||
content: String,
|
||||
recipients: List<Recipient>,
|
||||
student: Student, subject: String, content: String,
|
||||
recipients: List<Recipient>
|
||||
): SentMessage = sdk.init(student).sendMessage(
|
||||
subject = subject,
|
||||
content = content,
|
||||
recipients = recipients.mapFromEntities()
|
||||
)
|
||||
|
||||
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||
val folderId = messages.first().folderId
|
||||
val isDeleted = sdk.init(student)
|
||||
.deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
|
||||
suspend fun deleteMessage(student: Student, message: Message) {
|
||||
val isDeleted = sdk.init(student).deleteMessages(
|
||||
messages = listOf(message.messageId), message.folderId
|
||||
)
|
||||
|
||||
if (folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||
val deletedMessages = messages.map {
|
||||
it.copy(folderId = MessageFolder.TRASHED.id)
|
||||
.apply {
|
||||
id = it.id
|
||||
content = it.content
|
||||
}
|
||||
if (message.folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||
val deletedMessage = message.copy(folderId = MessageFolder.TRASHED.id).apply {
|
||||
id = message.id
|
||||
content = message.content
|
||||
}
|
||||
|
||||
messagesDb.updateAll(deletedMessages)
|
||||
} else messagesDb.deleteAll(messages)
|
||||
messagesDb.updateAll(listOf(deletedMessage))
|
||||
} else messagesDb.deleteAll(listOf(message))
|
||||
}
|
||||
|
||||
suspend fun deleteMessage(student: Student, message: Message) =
|
||||
deleteMessages(student, listOf(message))
|
||||
|
||||
var draftMessage: MessageDraft?
|
||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
||||
?.let { json.decodeFromString(it) }
|
||||
?.let { MessageDraftJsonAdapter(moshi).fromJson(it) }
|
||||
set(value) = sharedPrefProvider.putString(
|
||||
context.getString(R.string.pref_key_message_send_draft),
|
||||
value?.let { json.encodeToString(it) }
|
||||
value?.let { MessageDraftJsonAdapter(moshi).toJson(it) }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
|
@ -28,21 +28,12 @@ class MobileDeviceRepository @Inject constructor(
|
|||
|
||||
private val cacheKey = "devices"
|
||||
|
||||
fun getDevices(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) },
|
||||
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getRegisteredDevices()
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
@ -55,16 +46,14 @@ class MobileDeviceRepository @Inject constructor(
|
|||
)
|
||||
|
||||
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.unregisterDevice(device.deviceId)
|
||||
|
||||
mobileDb.deleteAll(listOf(device))
|
||||
}
|
||||
|
||||
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
||||
return sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getToken()
|
||||
.mapToMobileDeviceToken()
|
||||
}
|
||||
|
|
|
@ -5,10 +5,14 @@ import io.github.wulkanowy.data.db.entities.Note
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -24,24 +28,12 @@ class NoteRepository @Inject constructor(
|
|||
|
||||
private val cacheKey = "note"
|
||||
|
||||
fun getNotes(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false,
|
||||
) = networkBoundResource(
|
||||
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
getRefreshKey(cacheKey, semester)
|
||||
)
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
},
|
||||
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
|
||||
query = { noteDb.loadAll(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getNotes(semester.semesterId)
|
||||
.mapToEntities(semester)
|
||||
},
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.NotificationDao
|
||||
import io.github.wulkanowy.data.db.entities.Notification
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NotificationRepository @Inject constructor(
|
||||
private val notificationDao: NotificationDao,
|
||||
) {
|
||||
|
||||
fun getNotifications(studentId: Long) = notificationDao.loadAll(studentId)
|
||||
|
||||
suspend fun saveNotification(notification: Notification) =
|
||||
notificationDao.insertAll(listOf(notification))
|
||||
}
|
|
@ -5,30 +5,39 @@ import android.content.SharedPreferences
|
|||
import androidx.core.content.edit
|
||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||
import com.fredporciuncula.flow.preferences.Preference
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.adapter
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import io.github.wulkanowy.R
|
||||
import io.github.wulkanowy.data.enums.*
|
||||
import io.github.wulkanowy.sdk.toLocalDate
|
||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
import io.github.wulkanowy.utils.toLocalDateTime
|
||||
import io.github.wulkanowy.utils.toTimestamp
|
||||
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.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Singleton
|
||||
class PreferencesRepository @Inject constructor(
|
||||
@ApplicationContext val context: Context,
|
||||
private val sharedPref: SharedPreferences,
|
||||
private val flowSharedPref: FlowSharedPreferences,
|
||||
private val json: Json,
|
||||
@ApplicationContext val context: Context,
|
||||
moshi: Moshi
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private val dashboardItemsPositionAdapter: JsonAdapter<Map<DashboardItem.Type, Int>> =
|
||||
moshi.adapter()
|
||||
|
||||
val startMenuIndex: Int
|
||||
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
|
||||
|
||||
|
@ -52,13 +61,8 @@ class PreferencesRepository @Inject constructor(
|
|||
R.bool.pref_default_grade_average_force_calc
|
||||
)
|
||||
|
||||
val gradeExpandMode: GradeExpandMode
|
||||
get() = GradeExpandMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_expand_grade_mode,
|
||||
R.string.pref_default_expand_grade_mode
|
||||
)
|
||||
)
|
||||
val isGradeExpandable: Boolean
|
||||
get() = !getBoolean(R.string.pref_key_expand_grade, R.bool.pref_default_expand_grade)
|
||||
|
||||
val showAllSubjectsOnStatisticsList: Boolean
|
||||
get() = getBoolean(
|
||||
|
@ -67,15 +71,13 @@ class PreferencesRepository @Inject constructor(
|
|||
)
|
||||
|
||||
val appThemeKey = context.getString(R.string.pref_key_app_theme)
|
||||
val appTheme: AppTheme
|
||||
get() = AppTheme.getByValue(getString(appThemeKey, R.string.pref_default_app_theme))
|
||||
val appTheme: String
|
||||
get() = getString(appThemeKey, R.string.pref_default_app_theme)
|
||||
|
||||
val gradeColorTheme: GradeColorTheme
|
||||
get() = GradeColorTheme.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_grade_color_scheme,
|
||||
R.string.pref_default_grade_color_scheme
|
||||
)
|
||||
val gradeColorTheme: String
|
||||
get() = getString(
|
||||
R.string.pref_key_grade_color_scheme,
|
||||
R.string.pref_default_grade_color_scheme
|
||||
)
|
||||
|
||||
val appLanguageKey = context.getString(R.string.pref_key_app_language)
|
||||
|
@ -100,37 +102,12 @@ class PreferencesRepository @Inject constructor(
|
|||
|
||||
val isUpcomingLessonsNotificationsEnableKey =
|
||||
context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
||||
var isUpcomingLessonsNotificationsEnable: Boolean
|
||||
set(value) {
|
||||
sharedPref.edit { putBoolean(isUpcomingLessonsNotificationsEnableKey, value) }
|
||||
}
|
||||
val isUpcomingLessonsNotificationsEnable: Boolean
|
||||
get() = getBoolean(
|
||||
isUpcomingLessonsNotificationsEnableKey,
|
||||
R.bool.pref_default_notification_upcoming_lessons_enable
|
||||
)
|
||||
|
||||
val isUpcomingLessonsNotificationsPersistentKey =
|
||||
context.getString(R.string.pref_key_notifications_upcoming_lessons_persistent)
|
||||
val isUpcomingLessonsNotificationsPersistent: Boolean
|
||||
get() = getBoolean(
|
||||
isUpcomingLessonsNotificationsPersistentKey,
|
||||
R.bool.pref_default_notification_upcoming_lessons_persistent
|
||||
)
|
||||
|
||||
val isNotificationPiggybackEnabledKey =
|
||||
context.getString(R.string.pref_key_notifications_piggyback)
|
||||
val isNotificationPiggybackEnabled: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_notifications_piggyback,
|
||||
R.bool.pref_default_notification_piggyback
|
||||
)
|
||||
|
||||
val isNotificationPiggybackRemoveOriginalEnabled: Boolean
|
||||
get() = getBoolean(
|
||||
R.string.pref_key_notifications_piggyback_cancel_original,
|
||||
R.bool.pref_default_notification_piggyback_cancel_original
|
||||
)
|
||||
|
||||
val isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
|
||||
val isDebugNotificationEnable: Boolean
|
||||
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
|
||||
|
@ -159,12 +136,10 @@ class PreferencesRepository @Inject constructor(
|
|||
R.bool.pref_default_timetable_show_groups
|
||||
)
|
||||
|
||||
val showWholeClassPlan: TimetableMode
|
||||
get() = TimetableMode.getByValue(
|
||||
getString(
|
||||
R.string.pref_key_timetable_show_whole_class,
|
||||
R.string.pref_default_timetable_show_whole_class
|
||||
)
|
||||
val showWholeClassPlan: String
|
||||
get() = getString(
|
||||
R.string.pref_key_timetable_show_whole_class,
|
||||
R.string.pref_default_timetable_show_whole_class
|
||||
)
|
||||
|
||||
val gradeSortingMode: GradeSortingMode
|
||||
|
@ -200,21 +175,23 @@ class PreferencesRepository @Inject constructor(
|
|||
R.bool.pref_default_optional_arithmetic_average
|
||||
)
|
||||
|
||||
var lasSyncDate: Instant?
|
||||
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
|
||||
.takeIf { it != 0L }?.let(Instant::ofEpochMilli)
|
||||
set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply()
|
||||
var lasSyncDate: LocalDateTime
|
||||
get() = getLong(
|
||||
R.string.pref_key_last_sync_date,
|
||||
R.string.pref_default_last_sync_date
|
||||
).toLocalDateTime()
|
||||
set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply()
|
||||
|
||||
var dashboardItemsPosition: Map<DashboardItem.Type, Int>?
|
||||
get() {
|
||||
val value = sharedPref.getString(PREF_KEY_DASHBOARD_ITEMS_POSITION, null) ?: return null
|
||||
val json = sharedPref.getString(PREF_KEY_DASHBOARD_ITEMS_POSITION, null) ?: return null
|
||||
|
||||
return json.decodeFromString(value)
|
||||
return dashboardItemsPositionAdapter.fromJson(json)
|
||||
}
|
||||
set(value) = sharedPref.edit {
|
||||
putString(
|
||||
PREF_KEY_DASHBOARD_ITEMS_POSITION,
|
||||
json.encodeToString(value)
|
||||
dashboardItemsPositionAdapter.toJson(value)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -223,7 +200,6 @@ class PreferencesRepository @Inject constructor(
|
|||
.map { set ->
|
||||
set.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.toSet()
|
||||
}
|
||||
|
||||
|
@ -231,7 +207,6 @@ class PreferencesRepository @Inject constructor(
|
|||
get() = selectedDashboardTilesPreference.get()
|
||||
.map { DashboardItem.Tile.valueOf(it) }
|
||||
.plus(DashboardItem.Tile.ACCOUNT)
|
||||
.plus(DashboardItem.Tile.ADMIN_MESSAGE)
|
||||
.toSet()
|
||||
set(value) {
|
||||
val filteredValue = value.filterNot { it == DashboardItem.Tile.ACCOUNT }
|
||||
|
@ -250,24 +225,13 @@ class PreferencesRepository @Inject constructor(
|
|||
return flowSharedPref.getStringSet(prefKey, defaultSet)
|
||||
}
|
||||
|
||||
var dismissedAdminMessageIds: List<Int>
|
||||
get() = sharedPref.getStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, emptySet())
|
||||
.orEmpty()
|
||||
.map { it.toInt() }
|
||||
set(value) = sharedPref.edit {
|
||||
putStringSet(PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS, value.map { it.toString() }.toSet())
|
||||
}
|
||||
|
||||
var inAppReviewCount: Int
|
||||
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
|
||||
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
|
||||
|
||||
var inAppReviewDate: Instant?
|
||||
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
|
||||
?.let(Instant::ofEpochMilli)
|
||||
set(value) = sharedPref.edit {
|
||||
putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0)
|
||||
}
|
||||
var inAppReviewDate: LocalDate?
|
||||
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }?.toLocalDate()
|
||||
set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp()).apply()
|
||||
|
||||
var isAppReviewDone: Boolean
|
||||
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
|
||||
|
@ -288,9 +252,6 @@ 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_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
|
||||
|
@ -300,7 +261,5 @@ class PreferencesRepository @Inject constructor(
|
|||
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_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ 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
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import javax.inject.Inject
|
||||
|
@ -17,34 +15,26 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class RecipientRepository @Inject constructor(
|
||||
private val recipientDb: RecipientDao,
|
||||
private val sdk: Sdk,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val cacheKey = "recipient"
|
||||
|
||||
suspend fun refreshRecipients(student: Student, unit: ReportingUnit, role: Int) {
|
||||
val new = sdk.init(student).getRecipients(unit.unitId, role).mapToEntities(unit.studentId)
|
||||
val old = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
|
||||
recipientDb.deleteAll(old uniqueSubtract new)
|
||||
recipientDb.insertAll(new uniqueSubtract old)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
|
||||
suspend fun getRecipients(student: Student, unit: ReportingUnit, role: Int): List<Recipient> {
|
||||
val cached = recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
return if (cached.isEmpty() || isExpired) {
|
||||
return recipientDb.loadAll(unit.studentId, unit.unitId, role).ifEmpty {
|
||||
refreshRecipients(student, unit, role)
|
||||
|
||||
recipientDb.loadAll(unit.studentId, unit.unitId, role)
|
||||
} else cached
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getMessageRecipients(student: Student, message: Message): List<Recipient> {
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId)
|
||||
.mapToEntities(student.studentId)
|
||||
return sdk.init(student).getMessageRecipients(message.messageId, message.senderId).mapToEntities(student.studentId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ class RecoverRepository @Inject constructor(private val sdk: Sdk) {
|
|||
return sdk.getPasswordResetCaptchaCode(host, symbol)
|
||||
}
|
||||
|
||||
suspend fun sendRecoverRequest(
|
||||
url: String, symbol: String, email: String, reCaptchaResponse: String
|
||||
): String = sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||
suspend fun sendRecoverRequest(url: String, symbol: String, email: String, reCaptchaResponse: String): String {
|
||||
return sdk.sendPasswordResetRequest(url, symbol, email, reCaptchaResponse)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,17 @@ package io.github.wulkanowy.data.repositories
|
|||
|
||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -28,16 +30,17 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||
|
||||
fun getSchoolAnnouncements(
|
||||
student: Student,
|
||||
forceRefresh: Boolean, notify: Boolean = false
|
||||
forceRefresh: Boolean,
|
||||
notify: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it.isEmpty() },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
it.isEmpty() || forceRefresh || isExpired
|
||||
it.isEmpty() || forceRefresh
|
||||
|| refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||
},
|
||||
query = {
|
||||
schoolAnnouncementDb.loadAll(student.studentId)
|
||||
schoolAnnouncementDb.loadAll(
|
||||
student.studentId)
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
|
@ -54,11 +57,9 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
)
|
||||
|
||||
fun getSchoolAnnouncementFromDatabase(student: Student): Flow<List<SchoolAnnouncement>> {
|
||||
return schoolAnnouncementDb.loadAll(student.studentId)
|
||||
}
|
||||
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) =
|
||||
schoolAnnouncementDb.updateAll(schoolAnnouncement)
|
||||
suspend fun updateSchoolAnnouncement(schoolAnnouncement: List<SchoolAnnouncement>) = schoolAnnouncementDb.updateAll(schoolAnnouncement)
|
||||
}
|
||||
|
|
|
@ -4,11 +4,9 @@ import io.github.wulkanowy.data.db.dao.SchoolDao
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.getRefreshKey
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -16,44 +14,29 @@ import javax.inject.Singleton
|
|||
@Singleton
|
||||
class SchoolRepository @Inject constructor(
|
||||
private val schoolDb: SchoolDao,
|
||||
private val sdk: Sdk,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
private val sdk: Sdk
|
||||
) {
|
||||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
private val cacheKey = "school_info"
|
||||
|
||||
fun getSchoolInfo(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
shouldFetch = {
|
||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||
key = getRefreshKey(cacheKey, student)
|
||||
)
|
||||
it == null || forceRefresh || isExpired
|
||||
},
|
||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getSchool()
|
||||
.mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(schoolDb) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
fun getSchoolInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
|
||||
.mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(schoolDb) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
schoolDb.insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
schoolDb.insertAll(listOf(new))
|
||||
}
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.*
|
||||
import io.github.wulkanowy.utils.DispatchersProvider
|
||||
import io.github.wulkanowy.utils.getCurrentOrLast
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.isCurrent
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
@ -22,7 +26,7 @@ class SemesterRepository @Inject constructor(
|
|||
student: Student,
|
||||
forceRefresh: Boolean = false,
|
||||
refreshOnNoCurrent: Boolean = false
|
||||
) = withContext(dispatchers.io) {
|
||||
) = withContext(dispatchers.backgroundThread) {
|
||||
val semesters = semesterDb.loadAll(student.studentId, student.classId)
|
||||
|
||||
if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) {
|
||||
|
@ -39,14 +43,10 @@ class SemesterRepository @Inject constructor(
|
|||
): Boolean {
|
||||
val isNoSemesters = semesters.isEmpty()
|
||||
|
||||
val isRefreshOnModeChangeRequired = when {
|
||||
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
|
||||
semesters.firstOrNull { it.isCurrent }?.let {
|
||||
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||
} == true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
val isRefreshOnModeChangeRequired =
|
||||
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
|
||||
} else false
|
||||
|
||||
val isRefreshOnNoCurrentAppropriate =
|
||||
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
|
||||
|
@ -64,7 +64,7 @@ class SemesterRepository @Inject constructor(
|
|||
}
|
||||
|
||||
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) =
|
||||
withContext(dispatchers.io) {
|
||||
withContext(dispatchers.backgroundThread) {
|
||||
getSemesters(student, forceRefresh).getCurrentOrLast()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ import io.github.wulkanowy.data.db.dao.StudentInfoDao
|
|||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||
import io.github.wulkanowy.data.networkBoundResource
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.utils.init
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
@ -19,29 +19,24 @@ class StudentInfoRepository @Inject constructor(
|
|||
|
||||
private val saveFetchResultMutex = Mutex()
|
||||
|
||||
fun getStudentInfo(
|
||||
student: Student,
|
||||
semester: Semester,
|
||||
forceRefresh: Boolean,
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
isResultEmpty = { it == null },
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||
.getStudentInfo().mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(studentInfoDao) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
fun getStudentInfo(student: Student, semester: Semester, forceRefresh: Boolean) =
|
||||
networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
shouldFetch = { it == null || forceRefresh },
|
||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getStudentInfo().mapToEntity(semester)
|
||||
},
|
||||
saveFetchResult = { old, new ->
|
||||
if (old != null && new != old) {
|
||||
with(studentInfoDao) {
|
||||
deleteAll(listOf(old))
|
||||
insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
studentInfoDao.insertAll(listOf(new))
|
||||
}
|
||||
} else if (old == null) {
|
||||
studentInfoDao.insertAll(listOf(new))
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue