Compare commits
156 Commits
Author | SHA1 | Date | |
---|---|---|---|
ae65228805 | |||
391ee6e621 | |||
0a87df3d82 | |||
cb4ae21903 | |||
679cf2554d | |||
d473d53879 | |||
6531061b48 | |||
3347e8fba8 | |||
84067126a1 | |||
da9bebe923 | |||
b371fd6709 | |||
884d443c5b | |||
df58aa78ae | |||
2131e892ad | |||
63380d3e12 | |||
c572a91b38 | |||
20dde6e896 | |||
042b66ca5c | |||
8d8990761a | |||
a07741b5c5 | |||
f851a4d2c5 | |||
c8d069c787 | |||
5ce30a3000 | |||
26e0f43fa0 | |||
08c1bedca1 | |||
15537586c4 | |||
a04ba4ae10 | |||
57ea6379ab | |||
c3abe50ed4 | |||
f48caf9f70 | |||
9a413c14c3 | |||
8915c5dd8e | |||
e7561d4794 | |||
820b99dbc7 | |||
aff0fb3a60 | |||
d3bf5c3e0a | |||
dec2703cc7 | |||
edd1c9442e | |||
18568c86be | |||
84d0ba525f | |||
6290663f02 | |||
be046a1ddd | |||
96ee4bd9e5 | |||
923af85d18 | |||
ce36e86bb2 | |||
a4f455b38f | |||
01b8bd9d4a | |||
cfcc051ce4 | |||
0b0993be9a | |||
d07b0dbc98 | |||
daa7b54dab | |||
de9fcb9af9 | |||
40e0934504 | |||
009ec433be | |||
e1d82d70ee | |||
7a9ba04ff4 | |||
513b4b7d3e | |||
ce4157933f | |||
5d1085a64a | |||
a00f2dcbda | |||
b52a6f7f61 | |||
5146e44574 | |||
90e1cea679 | |||
c9b506ae10 | |||
14ebdad7b2 | |||
210308695b | |||
d5cc2263f5 | |||
47e3f2dc58 | |||
6dc16b288d | |||
daf97be9ad | |||
2bb2190410 | |||
aff1a7030d | |||
8877322357 | |||
bc672e94f8 | |||
a03bcf8e62 | |||
20673c4ead | |||
5321d00ee9 | |||
e6b2acabd5 | |||
bfd7f688ab | |||
ba02531aa4 | |||
210c3a0e28 | |||
68f0ecc45c | |||
0965d03f1a | |||
496641f594 | |||
684c258e2d | |||
5e96917508 | |||
17096ad11b | |||
bd883c9f38 | |||
6520f8a0d7 | |||
2eee50ad81 | |||
8560fd7e81 | |||
f718147ae9 | |||
cd12c4c891 | |||
65f114ce05 | |||
497083be97 | |||
e26860ea5a | |||
094df212b4 | |||
cc22985dc5 | |||
ad9a2711c4 | |||
daf44c531c | |||
4e12eb1552 | |||
c846cc999f | |||
47d430292c | |||
5e1ff2243f | |||
a35bef58f2 | |||
0005d84974 | |||
19558cb871 | |||
45e884127f | |||
c87085a226 | |||
79b970256f | |||
70f038f15f | |||
9cabd7ef08 | |||
d89e4ccfdf | |||
1bcc4d199e | |||
2e85e88c5d | |||
6ab67fe25b | |||
8563277a89 | |||
e458cc90b0 | |||
651e3a21b9 | |||
f6b969cfb1 | |||
d2aa940d46 | |||
ab435a72ea | |||
a56f4b8745 | |||
d003b0897c | |||
581bb2de77 | |||
495e385228 | |||
10ba36ba44 | |||
eae396424f | |||
a7891bb266 | |||
6e82409dbc | |||
984db18be3 | |||
c99bc96c08 | |||
3e7030abc2 | |||
6dad3b299b | |||
5e997f5a3e | |||
601d573283 | |||
6ae6ca7fbb | |||
c3d38afc3d | |||
4e19964249 | |||
a6c0efcb81 | |||
fcc71c0d5f | |||
a59d10b6c1 | |||
a48e4eb4ee | |||
2a3668bb18 | |||
804d0d9113 | |||
88b893e6c0 | |||
2874a7495e | |||
40d8f7a93d | |||
84cd51205f | |||
bac1832f27 | |||
8bf1e22407 | |||
e9f43f925c | |||
aa632edf5c | |||
57315d75c6 | |||
4552bc85b0 | |||
6b7795118c |
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[*]
|
||||||
|
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
|
2
.github/workflows/deploy-store.yml
vendored
2
.github/workflows/deploy-store.yml
vendored
@ -37,6 +37,7 @@ jobs:
|
|||||||
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
ANDROID_PUBLISHER_CREDENTIALS: ${{ secrets.ANDROID_PUBLISHER_CREDENTIALS }}
|
||||||
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
ADMOB_PROJECT_ID: ${{ secrets.ADMOB_PROJECT_ID }}
|
||||||
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
SINGLE_SUPPORT_AD_ID: ${{ secrets.SINGLE_SUPPORT_AD_ID }}
|
||||||
|
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||||
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
run: ./gradlew publishPlayReleaseApps -PenableFirebase --stacktrace;
|
||||||
|
|
||||||
deploy-app-gallery:
|
deploy-app-gallery:
|
||||||
@ -71,4 +72,5 @@ jobs:
|
|||||||
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
PLAY_STORE_PASSWORD: ${{ secrets.PLAY_STORE_PASSWORD }}
|
||||||
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
PLAY_KEY_ALIAS: ${{ secrets.PLAY_KEY_ALIAS }}
|
||||||
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
PLAY_KEY_PASSWORD: ${{ secrets.PLAY_KEY_PASSWORD }}
|
||||||
|
SET_BUILD_TIMESTAMP: ${{ secrets.SET_BUILD_TIMESTAMP }}
|
||||||
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
run: ./gradlew bundleHmsRelease --stacktrace && ./gradlew publishHuaweiAppGalleryHmsRelease --stacktrace
|
||||||
|
15
.idea/codeStyles/Project.xml
generated
15
.idea/codeStyles/Project.xml
generated
@ -2,14 +2,6 @@
|
|||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
<JetCodeStyleSettings>
|
<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" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
<codeStyleSettings language="XML">
|
||||||
@ -126,13 +118,6 @@
|
|||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="kotlin">
|
<codeStyleSettings language="kotlin">
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<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>
|
</codeStyleSettings>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
2
LICENSE
2
LICENSE
@ -186,7 +186,7 @@
|
|||||||
same "printed page" as the copyright notice for easier
|
same "printed page" as the copyright notice for easier
|
||||||
identification within third-party archives.
|
identification within third-party archives.
|
||||||
|
|
||||||
Copyright 2021 Wulkanowy
|
Copyright 2022 Wulkanowy
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
78
README.cs.md
Normal file
78
README.cs.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
[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
Normal file
74
README.de.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
[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,5 +1,11 @@
|
|||||||
[Polska wersja README](README.md)
|
[Polska wersja README](README.md)
|
||||||
|
|
||||||
|
[Deutsche Version von README](README.de.md)
|
||||||
|
|
||||||
|
[Česká verze README](README.cs.md)
|
||||||
|
|
||||||
|
[Slovenská verzia README](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
[English version of README](README.en.md)
|
[English version of README](README.en.md)
|
||||||
|
|
||||||
|
[Deutsche Version von README](README.de.md)
|
||||||
|
|
||||||
|
[Česká verze README](README.cs.md)
|
||||||
|
|
||||||
|
[Slovenská verzia README](README.sk.md)
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
|
78
README.sk.md
Normal file
78
README.sk.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
[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)
|
@ -22,8 +22,8 @@ android {
|
|||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 99
|
versionCode 105
|
||||||
versionName "1.4.1"
|
versionName "1.6.1"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
@ -73,6 +73,8 @@ android {
|
|||||||
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
|
minifyEnabled false
|
||||||
|
shrinkResources false
|
||||||
resValue "string", "app_name", "Wulkanowy DEV"
|
resValue "string", "app_name", "Wulkanowy DEV"
|
||||||
applicationIdSuffix ".dev"
|
applicationIdSuffix ".dev"
|
||||||
versionNameSuffix "-dev"
|
versionNameSuffix "-dev"
|
||||||
@ -149,7 +151,9 @@ kapt {
|
|||||||
|
|
||||||
play {
|
play {
|
||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'beta'
|
track = 'production'
|
||||||
|
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||||
|
userFraction = 0.25d
|
||||||
updatePriority = 1
|
updatePriority = 1
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
@ -165,36 +169,36 @@ huaweiPublish {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.7.0"
|
work_manager = "2.7.1"
|
||||||
android_hilt = "1.0.0"
|
android_hilt = "1.0.0"
|
||||||
room = "2.3.0"
|
room = "2.4.2"
|
||||||
chucker = "3.5.2"
|
chucker = "3.5.2"
|
||||||
mockk = "1.12.0"
|
mockk = "1.12.2"
|
||||||
coroutines = "1.5.2"
|
coroutines = "1.6.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:1.4.0"
|
implementation "io.github.wulkanowy:sdk:1.6.0"
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1"
|
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:$coroutines"
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.7.0"
|
implementation "androidx.core:core-ktx:1.7.0"
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha02'
|
implementation 'androidx.core:core-splashscreen:1.0.0-beta02'
|
||||||
implementation "androidx.activity:activity-ktx:1.4.0"
|
implementation "androidx.activity:activity-ktx:1.4.0"
|
||||||
implementation "androidx.appcompat:appcompat:1.4.0-rc01"
|
implementation "androidx.appcompat:appcompat:1.4.1"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.4.0-rc01"
|
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||||
implementation "androidx.annotation:annotation:1.3.0"
|
implementation "androidx.annotation:annotation:1.3.0"
|
||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.1.1"
|
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||||
implementation "androidx.viewpager:viewpager:1.0.0"
|
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.1.1"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||||
implementation "com.google.android.material:material:1.4.0"
|
implementation "com.google.android.material:material:1.5.0"
|
||||||
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
|
||||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||||
implementation 'com.github.lopspower:CircularImageView:4.2.0'
|
implementation 'com.github.lopspower:CircularImageView:4.2.0'
|
||||||
@ -202,7 +206,7 @@ dependencies {
|
|||||||
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
implementation "androidx.work:work-runtime-ktx:$work_manager"
|
||||||
playImplementation "androidx.work:work-gcm:$work_manager"
|
playImplementation "androidx.work:work-gcm:$work_manager"
|
||||||
|
|
||||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
|
||||||
|
|
||||||
implementation "androidx.room:room-runtime:$room"
|
implementation "androidx.room:room-runtime:$room"
|
||||||
implementation "androidx.room:room-ktx:$room"
|
implementation "androidx.room:room-ktx:$room"
|
||||||
@ -218,7 +222,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.9.2"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.9.3"
|
||||||
|
|
||||||
implementation "com.jakewharton.timber:timber:5.0.1"
|
implementation "com.jakewharton.timber:timber:5.0.1"
|
||||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||||
@ -226,31 +230,32 @@ dependencies {
|
|||||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||||
implementation "io.coil-kt:coil:1.4.0"
|
implementation "io.coil-kt:coil:1.4.0"
|
||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
|
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||||
implementation 'com.fredporciuncula:flow-preferences:1.5.0'
|
implementation 'com.fredporciuncula:flow-preferences:1.6.0'
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:29.0.0')
|
playImplementation platform('com.google.firebase:firebase-bom:29.3.0')
|
||||||
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
playImplementation 'com.google.firebase:firebase-analytics-ktx'
|
||||||
playImplementation 'com.google.firebase:firebase-messaging:'
|
playImplementation 'com.google.firebase:firebase-messaging:'
|
||||||
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
playImplementation 'com.google.firebase:firebase-crashlytics:'
|
||||||
playImplementation 'com.google.android.play:core:1.10.2'
|
playImplementation 'com.google.android.play:core:1.10.3'
|
||||||
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
playImplementation 'com.google.android.play:core-ktx:1.8.1'
|
||||||
playImplementation 'com.google.android.gms:play-services-ads:20.4.0'
|
playImplementation 'com.google.android.gms:play-services-ads:20.6.0'
|
||||||
|
|
||||||
hmsImplementation 'com.huawei.hms:hianalytics:6.3.0.303'
|
hmsImplementation 'com.huawei.hms:hianalytics:6.4.1.301'
|
||||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.1.300'
|
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.6.5.200'
|
||||||
|
|
||||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||||
|
|
||||||
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
|
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
|
||||||
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
|
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
|
||||||
|
debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04'
|
||||||
|
|
||||||
testImplementation "junit:junit:4.13.2"
|
testImplementation "junit:junit:4.13.2"
|
||||||
testImplementation "io.mockk:mockk:$mockk"
|
testImplementation "io.mockk:mockk:$mockk"
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
|
||||||
testImplementation 'org.robolectric:robolectric:4.7'
|
testImplementation 'org.robolectric:robolectric:4.7.3'
|
||||||
testImplementation "androidx.test:runner:1.4.0"
|
testImplementation "androidx.test:runner:1.4.0"
|
||||||
testImplementation "androidx.test.ext:junit:1.1.3"
|
testImplementation "androidx.test.ext:junit:1.1.3"
|
||||||
testImplementation "androidx.test:core:1.4.0"
|
testImplementation "androidx.test:core:1.4.0"
|
||||||
|
2414
app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json
Normal file
2414
app/schemas/io.github.wulkanowy.data.db.AppDatabase/44.json
Normal file
File diff suppressed because it is too large
Load Diff
2430
app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json
Normal file
2430
app/schemas/io.github.wulkanowy.data.db.AppDatabase/45.json
Normal file
File diff suppressed because it is too large
Load Diff
2430
app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json
Normal file
2430
app/schemas/io.github.wulkanowy.data.db.AppDatabase/46.json
Normal file
File diff suppressed because it is too large
Load Diff
2438
app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json
Normal file
2438
app/schemas/io.github.wulkanowy.data.db.AppDatabase/47.json
Normal file
File diff suppressed because it is too large
Load Diff
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json
Normal file
2445
app/schemas/io.github.wulkanowy.data.db.AppDatabase/48.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ import io.github.wulkanowy.ui.modules.main.MainActivity
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Suppress("UNUSED_PARAMETER", "unused")
|
||||||
@Singleton
|
@Singleton
|
||||||
class InAppReviewHelper @Inject constructor(
|
class InAppReviewHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@ -113,7 +114,6 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetProvider"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
package io.github.wulkanowy
|
package io.github.wulkanowy
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.util.Log.DEBUG
|
import android.util.Log.*
|
||||||
import android.util.Log.INFO
|
|
||||||
import android.util.Log.VERBOSE
|
|
||||||
import android.webkit.WebView
|
|
||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import com.yariksoffice.lingver.Lingver
|
import com.yariksoffice.lingver.Lingver
|
||||||
@ -12,12 +9,7 @@ import dagger.hilt.android.HiltAndroidApp
|
|||||||
import fr.bipi.tressence.file.FileLoggerTree
|
import fr.bipi.tressence.file.FileLoggerTree
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.ui.base.ThemeManager
|
import io.github.wulkanowy.ui.base.ThemeManager
|
||||||
import io.github.wulkanowy.utils.ActivityLifecycleLogger
|
import io.github.wulkanowy.utils.*
|
||||||
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 timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -44,7 +36,6 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||||||
initializeAppLanguage()
|
initializeAppLanguage()
|
||||||
themeManager.applyDefaultTheme()
|
themeManager.applyDefaultTheme()
|
||||||
initLogging()
|
initLogging()
|
||||||
fixWebViewLocale()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initLogging() {
|
private fun initLogging() {
|
||||||
@ -76,15 +67,6 @@ 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()
|
override fun getWorkManagerConfiguration() = Configuration.Builder()
|
||||||
.setWorkerFactory(workerFactory)
|
.setWorkerFactory(workerFactory)
|
||||||
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
|
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
|
||||||
|
@ -1,23 +1,173 @@
|
|||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
data class Resource<T>(val status: Status, val data: T?, val error: Throwable?) {
|
import kotlinx.coroutines.flow.*
|
||||||
companion object {
|
import kotlinx.coroutines.sync.Mutex
|
||||||
fun <T> success(data: T?): Resource<T> {
|
import kotlinx.coroutines.sync.withLock
|
||||||
return Resource(Status.SUCCESS, data, null)
|
import timber.log.Timber
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
|
sealed class Resource<T> {
|
||||||
return Resource(Status.ERROR, data, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> loading(data: T? = null): Resource<T> {
|
open class Loading<T> : Resource<T>()
|
||||||
return Resource(Status.LOADING, data, null)
|
|
||||||
}
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Status {
|
fun <T> Flow<Resource<T>>.onResourceLoading(block: suspend () -> Unit) = onEach {
|
||||||
LOADING,
|
if (it is Resource.Loading) {
|
||||||
SUCCESS,
|
block()
|
||||||
ERROR
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
query().map { Resource.Success(filterResult(it)) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)) }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,113 +1,11 @@
|
|||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Database
|
import androidx.room.*
|
||||||
import androidx.room.Room
|
|
||||||
import androidx.room.RoomDatabase
|
|
||||||
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
import androidx.room.RoomDatabase.JournalMode.TRUNCATE
|
||||||
import androidx.room.TypeConverters
|
import io.github.wulkanowy.data.db.dao.*
|
||||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.migrations.*
|
||||||
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.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.NotificationDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.RecipientDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.ReportingUnitDao
|
|
||||||
import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
|
||||||
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.AdminMessage
|
|
||||||
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.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.Notification
|
|
||||||
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.SchoolAnnouncement
|
|
||||||
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.Migration40
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration41
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration42
|
|
||||||
import io.github.wulkanowy.data.db.migrations.Migration43
|
|
||||||
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 io.github.wulkanowy.utils.AppInfo
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -145,6 +43,11 @@ import javax.inject.Singleton
|
|||||||
Notification::class,
|
Notification::class,
|
||||||
AdminMessage::class
|
AdminMessage::class
|
||||||
],
|
],
|
||||||
|
autoMigrations = [
|
||||||
|
AutoMigration(from = 44, to = 45),
|
||||||
|
AutoMigration(from = 46, to = 47),
|
||||||
|
AutoMigration(from = 47, to = 48),
|
||||||
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
)
|
)
|
||||||
@ -152,7 +55,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 43
|
const val VERSION_SCHEMA = 48
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
@ -196,7 +99,9 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration40(),
|
Migration40(),
|
||||||
Migration41(sharedPrefProvider),
|
Migration41(sharedPrefProvider),
|
||||||
Migration42(),
|
Migration42(),
|
||||||
Migration43()
|
Migration43(),
|
||||||
|
Migration44(),
|
||||||
|
Migration46(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
|
@ -1,40 +1,36 @@
|
|||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
|
import io.github.wulkanowy.utils.toTimestamp
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.time.*
|
||||||
|
import java.util.*
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.Month
|
import java.time.Month
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
import java.util.Date
|
import java.util.*
|
||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
||||||
private val json = Json
|
private val json = Json
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timestampToDate(value: Long?): LocalDate? = value?.run {
|
fun timestampToLocalDate(value: Long?): LocalDate? =
|
||||||
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
|
value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
|
||||||
}
|
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun dateToTimestamp(date: LocalDate?): Long? {
|
fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
|
||||||
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
|
|
||||||
}
|
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timestampToTime(value: Long?): LocalDateTime? = value?.let {
|
fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
|
||||||
LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
|
|
||||||
}
|
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timeToTimestamp(date: LocalDateTime?): Long? {
|
fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
|
||||||
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
|
|
||||||
}
|
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun monthToInt(month: Month?) = month?.value
|
fun monthToInt(month: Month?) = month?.value
|
||||||
@ -65,4 +61,11 @@ class Converters {
|
|||||||
emptyList() // handle errors from old gson Pair serialized data
|
emptyList() // handle errors from old gson Pair serialized data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun destinationToString(destination: Destination) = json.encodeToString(destination)
|
||||||
|
|
||||||
|
@TypeConverter
|
||||||
|
fun stringToDestination(destination: String): Destination = json.decodeFromString(destination)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@ -12,5 +12,5 @@ import javax.inject.Singleton
|
|||||||
interface ConferenceDao : BaseDao<Conference> {
|
interface ConferenceDao : BaseDao<Conference> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
|
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
|
||||||
fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow<List<Conference>>
|
fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow<List<Conference>>
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,6 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
interface SchoolAnnouncementDao : BaseDao<SchoolAnnouncement> {
|
||||||
|
|
||||||
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId")
|
@Query("SELECT * FROM SchoolAnnouncements WHERE student_id = :studentId ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
fun loadAll(studentId: Int): Flow<List<SchoolAnnouncement>>
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.db.dao
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.*
|
||||||
import androidx.room.Delete
|
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.OnConflictStrategy.ABORT
|
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.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
@ -38,6 +33,10 @@ abstract class StudentDao {
|
|||||||
@Query("SELECT * FROM Students")
|
@Query("SELECT * FROM Students")
|
||||||
abstract suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
|
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")
|
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
|
||||||
abstract suspend fun updateCurrent(id: Long)
|
abstract suspend fun updateCurrent(id: Long)
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import androidx.room.Query
|
|||||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
import java.util.UUID
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@ -12,5 +13,13 @@ import javax.inject.Singleton
|
|||||||
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
|
interface TimetableAdditionalDao : BaseDao<TimetableAdditional> {
|
||||||
|
|
||||||
@Query("SELECT * FROM TimetableAdditional WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@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>>
|
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)
|
||||||
}
|
}
|
||||||
|
@ -33,5 +33,8 @@ data class AdminMessage(
|
|||||||
|
|
||||||
val priority: String,
|
val priority: String,
|
||||||
|
|
||||||
val type: String
|
val type: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "is_dismissible")
|
||||||
|
val isDismissible: Boolean = false
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(tableName = "Conferences")
|
@Entity(tableName = "Conferences")
|
||||||
data class Conference(
|
data class Conference(
|
||||||
@ -27,7 +27,7 @@ data class Conference(
|
|||||||
@ColumnInfo(name = "conference_id")
|
@ColumnInfo(name = "conference_id")
|
||||||
val conferenceId: Int,
|
val conferenceId: Int,
|
||||||
|
|
||||||
val date: LocalDateTime
|
val date: Instant,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -24,5 +24,8 @@ data class GradeSemesterStatistics(
|
|||||||
var id: Long = 0
|
var id: Long = 0
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
var average: String = ""
|
var classAverage: String = ""
|
||||||
|
|
||||||
|
@Transient
|
||||||
|
var studentAverage: String = ""
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities
|
|||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(tableName = "GradesSummary")
|
@Entity(tableName = "GradesSummary")
|
||||||
data class GradeSummary(
|
data class GradeSummary(
|
||||||
@ -45,8 +45,8 @@ data class GradeSummary(
|
|||||||
var isFinalGradeNotified: Boolean = true
|
var isFinalGradeNotified: Boolean = true
|
||||||
|
|
||||||
@ColumnInfo(name = "predicted_grade_last_change")
|
@ColumnInfo(name = "predicted_grade_last_change")
|
||||||
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
|
var predictedGradeLastChange: Instant = Instant.now()
|
||||||
|
|
||||||
@ColumnInfo(name = "final_grade_last_change")
|
@ColumnInfo(name = "final_grade_last_change")
|
||||||
var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
|
var finalGradeLastChange: Instant = Instant.now()
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(tableName = "Messages")
|
@Entity(tableName = "Messages")
|
||||||
data class Message(
|
data class Message(
|
||||||
@ -29,7 +29,7 @@ data class Message(
|
|||||||
|
|
||||||
val subject: String,
|
val subject: String,
|
||||||
|
|
||||||
val date: LocalDateTime,
|
val date: Instant,
|
||||||
|
|
||||||
@ColumnInfo(name = "folder_id")
|
@ColumnInfo(name = "folder_id")
|
||||||
val folderId: Int,
|
val folderId: Int,
|
||||||
|
@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(tableName = "MobileDevices")
|
@Entity(tableName = "MobileDevices")
|
||||||
data class MobileDevice(
|
data class MobileDevice(
|
||||||
@ -17,7 +17,7 @@ data class MobileDevice(
|
|||||||
|
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
||||||
val date: LocalDateTime
|
val date: Instant,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -4,7 +4,8 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||||
import java.time.LocalDateTime
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(tableName = "Notifications")
|
@Entity(tableName = "Notifications")
|
||||||
data class Notification(
|
data class Notification(
|
||||||
@ -18,7 +19,10 @@ data class Notification(
|
|||||||
|
|
||||||
val type: NotificationType,
|
val type: NotificationType,
|
||||||
|
|
||||||
val date: LocalDateTime,
|
@ColumnInfo(defaultValue = "{\"type\":\"io.github.wulkanowy.ui.modules.Destination.Dashboard\"}")
|
||||||
|
val destination: Destination,
|
||||||
|
|
||||||
|
val date: Instant,
|
||||||
|
|
||||||
val data: String? = null
|
val data: String? = null
|
||||||
) {
|
) {
|
||||||
|
@ -7,7 +7,12 @@ import androidx.room.PrimaryKey
|
|||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
|
@Entity(
|
||||||
|
tableName = "Semesters", indices = [Index(
|
||||||
|
value = ["student_id", "diary_id", "kindergarten_diary_id", "semester_id"],
|
||||||
|
unique = true
|
||||||
|
)]
|
||||||
|
)
|
||||||
data class Semester(
|
data class Semester(
|
||||||
|
|
||||||
@ColumnInfo(name = "student_id")
|
@ColumnInfo(name = "student_id")
|
||||||
@ -16,6 +21,9 @@ data class Semester(
|
|||||||
@ColumnInfo(name = "diary_id")
|
@ColumnInfo(name = "diary_id")
|
||||||
val diaryId: Int,
|
val diaryId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "kindergarten_diary_id", defaultValue = "0")
|
||||||
|
val kindergartenDiaryId: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "diary_name")
|
@ColumnInfo(name = "diary_name")
|
||||||
val diaryName: String,
|
val diaryName: String,
|
||||||
|
|
||||||
@ -37,12 +45,11 @@ data class Semester(
|
|||||||
|
|
||||||
@ColumnInfo(name = "unit_id")
|
@ColumnInfo(name = "unit_id")
|
||||||
val unitId: Int
|
val unitId: Int
|
||||||
): Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0
|
var id: Long = 0
|
||||||
|
|
||||||
|
|
||||||
@ColumnInfo(name = "is_current")
|
@ColumnInfo(name = "is_current")
|
||||||
var current: Boolean = false
|
var current: Boolean = false
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import androidx.room.Entity
|
|||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "Students",
|
tableName = "Students",
|
||||||
@ -74,7 +74,7 @@ data class Student(
|
|||||||
val isCurrent: Boolean,
|
val isCurrent: Boolean,
|
||||||
|
|
||||||
@ColumnInfo(name = "registration_date")
|
@ColumnInfo(name = "registration_date")
|
||||||
val registrationDate: LocalDateTime
|
val registrationDate: Instant,
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
|
@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
|
||||||
|
|
||||||
@Entity(tableName = "Timetable")
|
@Entity(tableName = "Timetable")
|
||||||
data class Timetable(
|
data class Timetable(
|
||||||
@ -18,9 +18,9 @@ data class Timetable(
|
|||||||
|
|
||||||
val number: Int,
|
val number: Int,
|
||||||
|
|
||||||
val start: LocalDateTime,
|
val start: Instant,
|
||||||
|
|
||||||
val end: LocalDateTime,
|
val end: Instant,
|
||||||
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@ import androidx.room.ColumnInfo
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.util.*
|
||||||
|
|
||||||
@Entity(tableName = "TimetableAdditional")
|
@Entity(tableName = "TimetableAdditional")
|
||||||
data class TimetableAdditional(
|
data class TimetableAdditional(
|
||||||
@ -16,9 +17,9 @@ data class TimetableAdditional(
|
|||||||
@ColumnInfo(name = "diary_id")
|
@ColumnInfo(name = "diary_id")
|
||||||
val diaryId: Int,
|
val diaryId: Int,
|
||||||
|
|
||||||
val start: LocalDateTime,
|
val start: Instant,
|
||||||
|
|
||||||
val end: LocalDateTime,
|
val end: Instant,
|
||||||
|
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
|
|
||||||
@ -27,4 +28,10 @@ data class TimetableAdditional(
|
|||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
var id: Long = 0
|
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,12 +43,14 @@ class Migration12 : Migration(11, 12) {
|
|||||||
|
|
||||||
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
|
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
|
||||||
val students = mutableListOf<Int>()
|
val students = mutableListOf<Int>()
|
||||||
val studentsCursor = database.query("SELECT student_id FROM Students")
|
database.query("SELECT student_id FROM Students").use {
|
||||||
if (studentsCursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
students.add(studentsCursor.getInt(0))
|
students.add(it.getInt(0))
|
||||||
} while (studentsCursor.moveToNext())
|
} while (it.moveToNext())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return students
|
return students
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,12 +25,14 @@ class Migration13 : Migration(12, 13) {
|
|||||||
|
|
||||||
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
||||||
val students = mutableListOf<Pair<Int, String>>()
|
val students = mutableListOf<Pair<Int, String>>()
|
||||||
val studentsCursor = database.query("SELECT id, school_name FROM Students")
|
database.query("SELECT id, school_name FROM Students").use {
|
||||||
if (studentsCursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
students.add(studentsCursor.getInt(0) to studentsCursor.getString(1))
|
students.add(it.getInt(0) to it.getString(1))
|
||||||
} while (studentsCursor.moveToNext())
|
} while (it.moveToNext())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return students
|
return students
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,12 +44,14 @@ class Migration13 : Migration(12, 13) {
|
|||||||
|
|
||||||
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
|
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
|
||||||
val students = mutableListOf<Pair<Int, Int>>()
|
val students = mutableListOf<Pair<Int, Int>>()
|
||||||
val studentsCursor = database.query("SELECT student_id, class_id FROM Students")
|
database.query("SELECT student_id, class_id FROM Students").use {
|
||||||
if (studentsCursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1))
|
students.add(it.getInt(0) to it.getInt(1))
|
||||||
} while (studentsCursor.moveToNext())
|
} while (it.moveToNext())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return students
|
return students
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,24 +22,28 @@ class Migration27 : Migration(26, 27) {
|
|||||||
|
|
||||||
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
|
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
|
||||||
val students = mutableListOf<Triple<Long, Int, String>>()
|
val students = mutableListOf<Triple<Long, Int, String>>()
|
||||||
val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students")
|
database.query("SELECT id, user_login_id, student_name FROM Students").use {
|
||||||
if (studentsCursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2)))
|
students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
|
||||||
} while (studentsCursor.moveToNext())
|
} while (it.moveToNext())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return students
|
return students
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
|
||||||
val units = mutableListOf<Pair<Int, String>>()
|
val units = mutableListOf<Pair<Int, String>>()
|
||||||
val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits")
|
database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
|
||||||
if (unitsCursor.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
units.add(unitsCursor.getInt(0) to unitsCursor.getString(1))
|
units.add(it.getInt(0) to it.getString(1))
|
||||||
} while (unitsCursor.moveToNext())
|
} while (it.moveToNext())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return units
|
return units
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,17 @@ class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
|
|||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
|
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
|
||||||
|
|
||||||
val studentsCursor = database.query("SELECT * FROM Students")
|
database.query("SELECT * FROM Students").use {
|
||||||
|
while (it.moveToNext()) {
|
||||||
while (studentsCursor.moveToNext()) {
|
val studentId = it.getLongOrNull(0)
|
||||||
val studentId = studentsCursor.getLongOrNull(0)
|
database.execSQL(
|
||||||
database.execSQL(
|
"""
|
||||||
"""UPDATE Students
|
UPDATE Students
|
||||||
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
|
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
|
||||||
WHERE id = $studentId"""
|
WHERE id = $studentId
|
||||||
)
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.migrations
|
|||||||
import androidx.room.migration.Migration
|
import androidx.room.migration.Migration
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
|
import io.github.wulkanowy.data.enums.GradeExpandMode
|
||||||
|
|
||||||
class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) {
|
class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) {
|
||||||
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
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()
|
||||||
|
}
|
12
app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt
Normal file
12
app/src/main/java/io/github/wulkanowy/data/enums/AppTheme.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
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,
|
diaryId = semester.diaryId,
|
||||||
agenda = it.agenda,
|
agenda = it.agenda,
|
||||||
conferenceId = it.id,
|
conferenceId = it.id,
|
||||||
date = it.date,
|
date = it.dateZoned.toInstant(),
|
||||||
presentOnConference = it.presentOnConference,
|
presentOnConference = it.presentOnConference,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
title = it.title
|
title = it.title
|
||||||
|
@ -6,7 +6,7 @@ import io.github.wulkanowy.sdk.pojo.DirectorInformation as SdkDirectorInformatio
|
|||||||
|
|
||||||
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
fun List<SdkDirectorInformation>.mapToEntities(student: Student) = map {
|
||||||
SchoolAnnouncement(
|
SchoolAnnouncement(
|
||||||
studentId = student.studentId,
|
studentId = student.userLoginId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
content = it.content,
|
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.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Recipient
|
import io.github.wulkanowy.data.db.entities.Recipient
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
|
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
|
||||||
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
|
||||||
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
|
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,
|
senderId = it.sender?.loginId ?: 0,
|
||||||
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
|
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
|
||||||
subject = it.subject.trim(),
|
subject = it.subject.trim(),
|
||||||
date = it.date ?: LocalDateTime.now(),
|
date = it.dateZoned?.toInstant() ?: Instant.now(),
|
||||||
folderId = it.folderId,
|
folderId = it.folderId,
|
||||||
unread = it.unread ?: false,
|
unread = it.unread ?: false,
|
||||||
removed = it.removed,
|
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.MobileDevice
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||||
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
|
||||||
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
|
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
|
||||||
|
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
||||||
|
|
||||||
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
|
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
|
||||||
MobileDevice(
|
MobileDevice(
|
||||||
userLoginId = semester.studentId,
|
userLoginId = semester.studentId,
|
||||||
date = it.createDate,
|
date = it.createDateZoned.toInstant(),
|
||||||
deviceId = it.id,
|
deviceId = it.id,
|
||||||
name = it.name
|
name = it.name
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ fun List<SdkSemester>.mapToEntities(studentId: Int) = map {
|
|||||||
Semester(
|
Semester(
|
||||||
studentId = studentId,
|
studentId = studentId,
|
||||||
diaryId = it.diaryId,
|
diaryId = it.diaryId,
|
||||||
|
kindergartenDiaryId = it.kindergartenDiaryId,
|
||||||
diaryName = it.diaryName,
|
diaryName = it.diaryName,
|
||||||
schoolYear = it.schoolYear,
|
schoolYear = it.schoolYear,
|
||||||
semesterId = it.semesterId,
|
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.Student
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
||||||
|
|
||||||
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
|
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,
|
scrapperBaseUrl = it.scrapperBaseUrl,
|
||||||
loginType = it.loginType.name,
|
loginType = it.loginType.name,
|
||||||
isCurrent = false,
|
isCurrent = false,
|
||||||
registrationDate = LocalDateTime.now(),
|
registrationDate = Instant.now(),
|
||||||
mobileBaseUrl = it.mobileBaseUrl,
|
mobileBaseUrl = it.mobileBaseUrl,
|
||||||
privateKey = it.privateKey,
|
privateKey = it.privateKey,
|
||||||
certificateKey = it.certificateKey,
|
certificateKey = it.certificateKey,
|
||||||
|
@ -21,8 +21,8 @@ fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
|
|||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
number = it.number,
|
number = it.number,
|
||||||
start = it.start,
|
start = it.startZoned.toInstant(),
|
||||||
end = it.end,
|
end = it.endZoned.toInstant(),
|
||||||
date = it.date,
|
date = it.date,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
subjectOld = it.subjectOld,
|
subjectOld = it.subjectOld,
|
||||||
@ -45,8 +45,8 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
|
|||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
start = it.start,
|
start = it.startZoned.toInstant(),
|
||||||
end = it.end
|
end = it.endZoned.toInstant(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package io.github.wulkanowy.data.pojos
|
package io.github.wulkanowy.data.pojos
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
import io.github.wulkanowy.services.sync.notifications.NotificationType
|
||||||
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
|
|
||||||
data class NotificationData(
|
data class NotificationData(
|
||||||
val intentToStart: Intent,
|
val destination: Destination,
|
||||||
val title: String,
|
val title: String,
|
||||||
val content: String
|
val content: String
|
||||||
)
|
)
|
||||||
@ -13,7 +13,7 @@ data class GroupNotificationData(
|
|||||||
val notificationDataList: List<NotificationData>,
|
val notificationDataList: List<NotificationData>,
|
||||||
val title: String,
|
val title: String,
|
||||||
val content: String,
|
val content: String,
|
||||||
val intentToStart: Intent,
|
val destination: Destination,
|
||||||
val type: NotificationType
|
val type: NotificationType
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,9 +3,8 @@ package io.github.wulkanowy.data.repositories
|
|||||||
import io.github.wulkanowy.data.api.AdminMessageService
|
import io.github.wulkanowy.data.api.AdminMessageService
|
||||||
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
import io.github.wulkanowy.data.db.dao.AdminMessageDao
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -14,23 +13,18 @@ import javax.inject.Singleton
|
|||||||
class AdminMessageRepository @Inject constructor(
|
class AdminMessageRepository @Inject constructor(
|
||||||
private val adminMessageService: AdminMessageService,
|
private val adminMessageService: AdminMessageService,
|
||||||
private val adminMessageDao: AdminMessageDao,
|
private val adminMessageDao: AdminMessageDao,
|
||||||
private val appInfo: AppInfo,
|
private val appInfo: AppInfo
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
|
||||||
) {
|
) {
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
private val cacheKey = "admin_messages"
|
suspend fun getAdminMessages(student: Student) = networkBoundResource(
|
||||||
|
|
||||||
suspend fun getAdminMessages(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it == null },
|
||||||
query = { adminMessageDao.loadAll() },
|
query = { adminMessageDao.loadAll() },
|
||||||
fetch = { adminMessageService.getAdminMessages() },
|
fetch = { adminMessageService.getAdminMessages() },
|
||||||
shouldFetch = {
|
shouldFetch = { true },
|
||||||
refreshHelper.shouldBeRefreshed(cacheKey) || forceRefresh
|
|
||||||
},
|
|
||||||
saveFetchResult = { oldItems, newItems ->
|
saveFetchResult = { oldItems, newItems ->
|
||||||
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
|
||||||
refreshHelper.updateLastRefreshTimestamp(cacheKey)
|
|
||||||
},
|
},
|
||||||
showSavedOnLoading = false,
|
showSavedOnLoading = false,
|
||||||
mapResult = { adminMessages ->
|
mapResult = { adminMessages ->
|
||||||
|
@ -5,15 +5,10 @@ import io.github.wulkanowy.data.db.entities.Attendance
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.pojo.Absent
|
import io.github.wulkanowy.sdk.pojo.Absent
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -42,6 +37,7 @@ class AttendanceRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, semester, start, end)
|
key = getRefreshKey(cacheKey, semester, start, end)
|
||||||
@ -52,7 +48,8 @@ class AttendanceRepository @Inject constructor(
|
|||||||
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -90,7 +87,8 @@ class AttendanceRepository @Inject constructor(
|
|||||||
timeId = attendance.timeId
|
timeId = attendance.timeId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.excuseForAbsence(items, reason)
|
.excuseForAbsence(items, 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.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -32,13 +32,15 @@ class AttendanceSummaryRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getAttendanceSummary(subjectId)
|
.getAttendanceSummary(subjectId)
|
||||||
.mapToEntities(semester, subjectId)
|
.mapToEntities(semester, subjectId)
|
||||||
},
|
},
|
||||||
|
@ -4,14 +4,9 @@ import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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 kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -36,6 +31,7 @@ class CompletedLessonsRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, semester, start, end)
|
key = getRefreshKey(cacheKey, semester, start, end)
|
||||||
@ -51,7 +47,8 @@ class CompletedLessonsRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getCompletedLessons(start.monday, end.sunday)
|
.getCompletedLessons(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
|
@ -5,17 +5,15 @@ import io.github.wulkanowy.data.db.entities.Conference
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -35,9 +33,10 @@ class ConferenceRepository @Inject constructor(
|
|||||||
semester: Semester,
|
semester: Semester,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC),
|
startDate: Instant = Instant.EPOCH,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
@ -46,7 +45,8 @@ class ConferenceRepository @Inject constructor(
|
|||||||
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
conferenceDb.loadAll(semester.diaryId, student.studentId, startDate)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getConferences()
|
.getConferences()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
.filter { it.date >= startDate }
|
.filter { it.date >= startDate }
|
||||||
@ -66,7 +66,7 @@ class ConferenceRepository @Inject constructor(
|
|||||||
conferenceDb.loadAll(
|
conferenceDb.loadAll(
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)
|
startDate = Instant.EPOCH,
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)
|
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)
|
||||||
|
@ -5,14 +5,9 @@ import io.github.wulkanowy.data.db.entities.Exam
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -39,6 +34,7 @@ class ExamRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, semester, start, end)
|
key = getRefreshKey(cacheKey, semester, start, end)
|
||||||
@ -54,7 +50,8 @@ class ExamRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
|
@ -7,17 +7,14 @@ import io.github.wulkanowy.data.db.entities.GradeSummary
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -40,6 +37,10 @@ class GradeRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
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) ->
|
shouldFetch = { (details, summaries) ->
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||||
details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
|
details.isEmpty() || summaries.isEmpty() || forceRefresh || isExpired
|
||||||
@ -51,7 +52,7 @@ class GradeRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
val (details, summary) = sdk.init(student)
|
val (details, summary) = sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getGrades(semester.semesterId)
|
.getGrades(semester.semesterId)
|
||||||
|
|
||||||
details.mapToEntities(semester) to summary.mapToEntities(semester)
|
details.mapToEntities(semester) to summary.mapToEntities(semester)
|
||||||
@ -70,8 +71,8 @@ class GradeRepository @Inject constructor(
|
|||||||
newDetails: List<Grade>,
|
newDetails: List<Grade>,
|
||||||
notify: Boolean
|
notify: Boolean
|
||||||
) {
|
) {
|
||||||
val notifyBreakDate = oldGrades.maxByOrNull {it.date }
|
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
|
||||||
?.date ?: student.registrationDate.toLocalDate()
|
?: student.registrationDate.toLocalDate()
|
||||||
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
|
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
|
||||||
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
|
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
|
||||||
if (it.date >= notifyBreakDate) it.apply {
|
if (it.date >= notifyBreakDate) it.apply {
|
||||||
@ -101,13 +102,13 @@ class GradeRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
summary.predictedGradeLastChange = when {
|
summary.predictedGradeLastChange = when {
|
||||||
oldSummary == null -> LocalDateTime.now()
|
oldSummary == null -> Instant.now()
|
||||||
summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now()
|
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
|
||||||
else -> oldSummary.predictedGradeLastChange
|
else -> oldSummary.predictedGradeLastChange
|
||||||
}
|
}
|
||||||
summary.finalGradeLastChange = when {
|
summary.finalGradeLastChange = when {
|
||||||
oldSummary == null -> LocalDateTime.now()
|
oldSummary == null -> Instant.now()
|
||||||
summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now()
|
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
|
||||||
else -> oldSummary.finalGradeLastChange
|
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.mapPointsToStatisticsItems
|
||||||
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import java.util.Locale
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -46,6 +46,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = partialMutex,
|
mutex = partialMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(partialCacheKey, semester)
|
key = getRefreshKey(partialCacheKey, semester)
|
||||||
@ -54,7 +55,8 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getGradesPartialStatistics(semester.semesterId)
|
.getGradesPartialStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -66,20 +68,16 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
mapResult = { items ->
|
mapResult = { items ->
|
||||||
when (subjectName) {
|
when (subjectName) {
|
||||||
"Wszystkie" -> {
|
"Wszystkie" -> {
|
||||||
val numerator = items.map {
|
val summaryItem = GradePartialStatistics(
|
||||||
it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
|
|
||||||
}.filterNot { it == .0 }
|
|
||||||
(items.reversed() + GradePartialStatistics(
|
|
||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
semesterId = semester.semesterId,
|
semesterId = semester.semesterId,
|
||||||
subject = subjectName,
|
subject = subjectName,
|
||||||
classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
|
classAverage = items.map { it.classAverage }.getSummaryAverage(),
|
||||||
"%.2f".format(Locale.FRANCE, it)
|
studentAverage = items.map { it.studentAverage }.getSummaryAverage(),
|
||||||
},
|
|
||||||
studentAverage = "",
|
|
||||||
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
|
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
|
||||||
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
|
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
|
||||||
)).reversed()
|
)
|
||||||
|
listOf(summaryItem) + items
|
||||||
}
|
}
|
||||||
else -> items.filter { it.subject == subjectName }
|
else -> items.filter { it.subject == subjectName }
|
||||||
}.mapPartialToStatisticItems()
|
}.mapPartialToStatisticItems()
|
||||||
@ -93,6 +91,7 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = semesterMutex,
|
mutex = semesterMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(semesterCacheKey, semester)
|
key = getRefreshKey(semesterCacheKey, semester)
|
||||||
@ -101,7 +100,8 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getGradesSemesterStatistics(semester.semesterId)
|
.getGradesSemesterStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -114,29 +114,29 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
val itemsWithAverage = items.map { item ->
|
val itemsWithAverage = items.map { item ->
|
||||||
item.copy().apply {
|
item.copy().apply {
|
||||||
val denominator = item.amounts.sum()
|
val denominator = item.amounts.sum()
|
||||||
average = if (denominator == 0) "" else {
|
classAverage = if (denominator == 0) "" else {
|
||||||
(item.amounts.mapIndexed { gradeValue, amount ->
|
(item.amounts.mapIndexed { gradeValue, amount ->
|
||||||
(gradeValue + 1) * amount
|
(gradeValue + 1) * amount
|
||||||
}.sum().toDouble() / denominator).let {
|
}.sum().toDouble() / denominator).asAverageString()
|
||||||
"%.2f".format(Locale.FRANCE, it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
when (subjectName) {
|
when (subjectName) {
|
||||||
"Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics(
|
"Wszystkie" -> {
|
||||||
studentId = semester.studentId,
|
val summaryItem = GradeSemesterStatistics(
|
||||||
semesterId = semester.semesterId,
|
studentId = semester.studentId,
|
||||||
subject = subjectName,
|
semesterId = semester.semesterId,
|
||||||
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
|
subject = subjectName,
|
||||||
studentGrade = 0
|
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
|
||||||
).apply {
|
studentGrade = 0,
|
||||||
average = itemsWithAverage.mapNotNull {
|
).apply {
|
||||||
it.average.replace(",", ".").toDoubleOrNull()
|
classAverage = itemsWithAverage.map { it.classAverage }.getSummaryAverage()
|
||||||
}.average().let {
|
studentAverage = items
|
||||||
"%.2f".format(Locale.FRANCE, it)
|
.mapNotNull { summary -> summary.studentGrade.takeIf { it != 0 } }
|
||||||
|
.average().asAverageString()
|
||||||
}
|
}
|
||||||
}).reversed()
|
listOf(summaryItem) + itemsWithAverage
|
||||||
|
}
|
||||||
else -> itemsWithAverage.filter { it.subject == subjectName }
|
else -> itemsWithAverage.filter { it.subject == subjectName }
|
||||||
}.mapSemesterToStatisticItems()
|
}.mapSemesterToStatisticItems()
|
||||||
}
|
}
|
||||||
@ -149,13 +149,15 @@ class GradeStatisticsRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = pointsMutex,
|
mutex = pointsMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(pointsCacheKey, semester))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getGradesPointsStatistics(semester.semesterId)
|
.getGradesPointsStatistics(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -172,6 +174,19 @@ 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> {
|
private fun List<List<Int>>.sumGradeAmounts(): List<Int> {
|
||||||
val result = mutableListOf(0, 0, 0, 0, 0, 0)
|
val result = mutableListOf(0, 0, 0, 0, 0, 0)
|
||||||
forEach {
|
forEach {
|
||||||
|
@ -5,14 +5,9 @@ import io.github.wulkanowy.data.db.entities.Homework
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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 kotlinx.coroutines.sync.Mutex
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -38,6 +33,7 @@ class HomeworkRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, semester, start, end)
|
key = getRefreshKey(cacheKey, semester, start, end)
|
||||||
@ -53,7 +49,8 @@ class HomeworkRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getHomework(start.monday, end.sunday)
|
.getHomework(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
|
@ -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.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -29,6 +29,7 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it == null },
|
||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { luckyNumberDb.load(student.studentId, now()) },
|
query = { luckyNumberDb.load(student.studentId, now()) },
|
||||||
fetch = {
|
fetch = {
|
||||||
|
@ -7,15 +7,12 @@ import io.github.wulkanowy.data.Resource
|
|||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.entities.Message
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
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
|
||||||
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||||
import io.github.wulkanowy.data.mappers.mapFromEntities
|
import io.github.wulkanowy.data.mappers.mapFromEntities
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
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.MessageDraft
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.sdk.pojo.Folder
|
import io.github.wulkanowy.sdk.pojo.Folder
|
||||||
@ -23,7 +20,6 @@ import io.github.wulkanowy.sdk.pojo.SentMessage
|
|||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -59,6 +55,7 @@ class MessageRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
): Flow<Resource<List<Message>>> = networkBoundResource(
|
): Flow<Resource<List<Message>>> = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, student, folder)
|
key = getRefreshKey(cacheKey, student, folder)
|
||||||
@ -106,8 +103,9 @@ class MessageRepository @Inject constructor(
|
|||||||
message: Message,
|
message: Message,
|
||||||
markAsRead: Boolean = false,
|
markAsRead: Boolean = false,
|
||||||
): Flow<Resource<MessageWithAttachment?>> = networkBoundResource(
|
): Flow<Resource<MessageWithAttachment?>> = networkBoundResource(
|
||||||
|
isResultEmpty = { it?.message?.content.isNullOrBlank() },
|
||||||
shouldFetch = {
|
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()}")
|
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||||
it.message.unread || it.message.content.isEmpty()
|
it.message.unread || it.message.content.isEmpty()
|
||||||
},
|
},
|
||||||
@ -123,7 +121,7 @@ class MessageRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
||||||
checkNotNull(old, { "Fetched message no longer exist!" })
|
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||||
messagesDb.updateAll(listOf(old.message.apply {
|
messagesDb.updateAll(listOf(old.message.apply {
|
||||||
id = old.message.id
|
id = old.message.id
|
||||||
unread = !markAsRead
|
unread = !markAsRead
|
||||||
@ -153,20 +151,27 @@ class MessageRepository @Inject constructor(
|
|||||||
recipients = recipients.mapFromEntities()
|
recipients = recipients.mapFromEntities()
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun deleteMessage(student: Student, message: Message) {
|
suspend fun deleteMessages(student: Student, messages: List<Message>) {
|
||||||
val isDeleted = sdk.init(student).deleteMessages(
|
val folderId = messages.first().folderId
|
||||||
messages = listOf(message.messageId), message.folderId
|
val isDeleted = sdk.init(student)
|
||||||
)
|
.deleteMessages(messages = messages.map { it.messageId }, folderId = folderId)
|
||||||
|
|
||||||
if (message.folderId != MessageFolder.TRASHED.id && isDeleted) {
|
if (folderId != MessageFolder.TRASHED.id && isDeleted) {
|
||||||
val deletedMessage = message.copy(folderId = MessageFolder.TRASHED.id).apply {
|
val deletedMessages = messages.map {
|
||||||
id = message.id
|
it.copy(folderId = MessageFolder.TRASHED.id)
|
||||||
content = message.content
|
.apply {
|
||||||
|
id = it.id
|
||||||
|
content = it.content
|
||||||
|
}
|
||||||
}
|
}
|
||||||
messagesDb.updateAll(listOf(deletedMessage))
|
|
||||||
} else messagesDb.deleteAll(listOf(message))
|
messagesDb.updateAll(deletedMessages)
|
||||||
|
} else messagesDb.deleteAll(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteMessage(student: Student, message: Message) =
|
||||||
|
deleteMessages(student, listOf(message))
|
||||||
|
|
||||||
var draftMessage: MessageDraft?
|
var draftMessage: MessageDraft?
|
||||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_send_draft))
|
||||||
?.let { json.decodeFromString(it) }
|
?.let { json.decodeFromString(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.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
import io.github.wulkanowy.data.pojos.MobileDeviceToken
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -34,13 +34,15 @@ class MobileDeviceRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
query = { mobileDb.loadAll(student.userLoginId.takeIf { it != 0 } ?: student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getRegisteredDevices()
|
.getRegisteredDevices()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -53,14 +55,16 @@ class MobileDeviceRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
suspend fun unregisterDevice(student: Student, semester: Semester, device: MobileDevice) {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.unregisterDevice(device.deviceId)
|
.unregisterDevice(device.deviceId)
|
||||||
|
|
||||||
mobileDb.deleteAll(listOf(device))
|
mobileDb.deleteAll(listOf(device))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
suspend fun getToken(student: Student, semester: Semester): MobileDeviceToken {
|
||||||
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
return sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getToken()
|
.getToken()
|
||||||
.mapToMobileDeviceToken()
|
.mapToMobileDeviceToken()
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,9 @@ import io.github.wulkanowy.data.db.entities.Note
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -34,6 +31,7 @@ class NoteRepository @Inject constructor(
|
|||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
getRefreshKey(cacheKey, semester)
|
getRefreshKey(cacheKey, semester)
|
||||||
@ -42,7 +40,8 @@ class NoteRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { noteDb.loadAll(student.studentId) },
|
query = { noteDb.loadAll(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getNotes(semester.semesterId)
|
.getNotes(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
|
@ -7,23 +7,16 @@ import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
|||||||
import com.fredporciuncula.flow.preferences.Preference
|
import com.fredporciuncula.flow.preferences.Preference
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.sdk.toLocalDate
|
import io.github.wulkanowy.data.enums.*
|
||||||
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeExpandMode
|
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode
|
|
||||||
import io.github.wulkanowy.utils.toLocalDateTime
|
|
||||||
import io.github.wulkanowy.utils.toTimestamp
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.lang.ClassCastException
|
import java.time.Instant
|
||||||
import java.lang.IllegalStateException
|
|
||||||
import java.time.LocalDate
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -74,13 +67,15 @@ class PreferencesRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val appThemeKey = context.getString(R.string.pref_key_app_theme)
|
val appThemeKey = context.getString(R.string.pref_key_app_theme)
|
||||||
val appTheme: String
|
val appTheme: AppTheme
|
||||||
get() = getString(appThemeKey, R.string.pref_default_app_theme)
|
get() = AppTheme.getByValue(getString(appThemeKey, R.string.pref_default_app_theme))
|
||||||
|
|
||||||
val gradeColorTheme: String
|
val gradeColorTheme: GradeColorTheme
|
||||||
get() = getString(
|
get() = GradeColorTheme.getByValue(
|
||||||
R.string.pref_key_grade_color_scheme,
|
getString(
|
||||||
R.string.pref_default_grade_color_scheme
|
R.string.pref_key_grade_color_scheme,
|
||||||
|
R.string.pref_default_grade_color_scheme
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val appLanguageKey = context.getString(R.string.pref_key_app_language)
|
val appLanguageKey = context.getString(R.string.pref_key_app_language)
|
||||||
@ -105,7 +100,10 @@ class PreferencesRepository @Inject constructor(
|
|||||||
|
|
||||||
val isUpcomingLessonsNotificationsEnableKey =
|
val isUpcomingLessonsNotificationsEnableKey =
|
||||||
context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
context.getString(R.string.pref_key_notifications_upcoming_lessons_enable)
|
||||||
val isUpcomingLessonsNotificationsEnable: Boolean
|
var isUpcomingLessonsNotificationsEnable: Boolean
|
||||||
|
set(value) {
|
||||||
|
sharedPref.edit { putBoolean(isUpcomingLessonsNotificationsEnableKey, value) }
|
||||||
|
}
|
||||||
get() = getBoolean(
|
get() = getBoolean(
|
||||||
isUpcomingLessonsNotificationsEnableKey,
|
isUpcomingLessonsNotificationsEnableKey,
|
||||||
R.bool.pref_default_notification_upcoming_lessons_enable
|
R.bool.pref_default_notification_upcoming_lessons_enable
|
||||||
@ -127,6 +125,12 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.bool.pref_default_notification_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 isDebugNotificationEnableKey = context.getString(R.string.pref_key_notification_debug)
|
||||||
val isDebugNotificationEnable: Boolean
|
val isDebugNotificationEnable: Boolean
|
||||||
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
|
get() = getBoolean(isDebugNotificationEnableKey, R.bool.pref_default_notification_debug)
|
||||||
@ -155,10 +159,12 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.bool.pref_default_timetable_show_groups
|
R.bool.pref_default_timetable_show_groups
|
||||||
)
|
)
|
||||||
|
|
||||||
val showWholeClassPlan: String
|
val showWholeClassPlan: TimetableMode
|
||||||
get() = getString(
|
get() = TimetableMode.getByValue(
|
||||||
R.string.pref_key_timetable_show_whole_class,
|
getString(
|
||||||
R.string.pref_default_timetable_show_whole_class
|
R.string.pref_key_timetable_show_whole_class,
|
||||||
|
R.string.pref_default_timetable_show_whole_class
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val gradeSortingMode: GradeSortingMode
|
val gradeSortingMode: GradeSortingMode
|
||||||
@ -194,10 +200,10 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.bool.pref_default_optional_arithmetic_average
|
R.bool.pref_default_optional_arithmetic_average
|
||||||
)
|
)
|
||||||
|
|
||||||
var lasSyncDate: LocalDateTime
|
var lasSyncDate: Instant?
|
||||||
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
|
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
|
||||||
.toLocalDateTime()
|
.takeIf { it != 0L }?.let(Instant::ofEpochMilli)
|
||||||
set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply()
|
set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply()
|
||||||
|
|
||||||
var dashboardItemsPosition: Map<DashboardItem.Type, Int>?
|
var dashboardItemsPosition: Map<DashboardItem.Type, Int>?
|
||||||
get() {
|
get() {
|
||||||
@ -244,15 +250,24 @@ class PreferencesRepository @Inject constructor(
|
|||||||
return flowSharedPref.getStringSet(prefKey, defaultSet)
|
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
|
var inAppReviewCount: Int
|
||||||
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
|
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
|
||||||
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
|
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
|
||||||
|
|
||||||
var inAppReviewDate: LocalDate?
|
var inAppReviewDate: Instant?
|
||||||
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
|
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
|
||||||
?.toLocalDate()
|
?.let(Instant::ofEpochMilli)
|
||||||
set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp())
|
set(value) = sharedPref.edit {
|
||||||
.apply()
|
putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0)
|
||||||
|
}
|
||||||
|
|
||||||
var isAppReviewDone: Boolean
|
var isAppReviewDone: Boolean
|
||||||
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
|
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)
|
||||||
@ -285,5 +300,7 @@ 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_DATE = "in_app_review_date"
|
||||||
|
|
||||||
private const val PREF_KEY_IN_APP_REVIEW_DONE = "in_app_review_done"
|
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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.SchoolAnnouncementDao
|
|||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -31,6 +31,7 @@ class SchoolAnnouncementRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean, notify: Boolean = false
|
forceRefresh: Boolean, notify: Boolean = false
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
|
@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.SchoolDao
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -30,6 +30,7 @@ class SchoolRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it == null },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, student)
|
key = getRefreshKey(cacheKey, student)
|
||||||
@ -38,7 +39,9 @@ class SchoolRepository @Inject constructor(
|
|||||||
},
|
},
|
||||||
query = { schoolDb.load(semester.studentId, semester.classId) },
|
query = { schoolDb.load(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).getSchool()
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
|
.getSchool()
|
||||||
.mapToEntity(semester)
|
.mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -5,11 +5,7 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.*
|
||||||
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 kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -43,10 +39,14 @@ class SemesterRepository @Inject constructor(
|
|||||||
): Boolean {
|
): Boolean {
|
||||||
val isNoSemesters = semesters.isEmpty()
|
val isNoSemesters = semesters.isEmpty()
|
||||||
|
|
||||||
val isRefreshOnModeChangeRequired =
|
val isRefreshOnModeChangeRequired = when {
|
||||||
if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
|
||||||
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
|
semesters.firstOrNull { it.isCurrent }?.let {
|
||||||
} else false
|
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||||
|
} == true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
val isRefreshOnNoCurrentAppropriate =
|
val isRefreshOnNoCurrentAppropriate =
|
||||||
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
|
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
|
||||||
|
@ -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.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntity
|
import io.github.wulkanowy.data.mappers.mapToEntity
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -25,10 +25,12 @@ class StudentInfoRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it == null },
|
||||||
shouldFetch = { it == null || forceRefresh },
|
shouldFetch = { it == null || forceRefresh },
|
||||||
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
query = { studentInfoDao.loadStudentInfo(student.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getStudentInfo().mapToEntity(semester)
|
.getStudentInfo().mapToEntity(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -73,6 +73,15 @@ class StudentRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
|
||||||
|
studentDb.loadStudentWithSemestersById(id)?.apply {
|
||||||
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||||
|
student.password = withContext(dispatchers.io) {
|
||||||
|
decrypt(student.password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
|
suspend fun getStudentById(id: Long, decryptPass: Boolean = true): Student {
|
||||||
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
|
val student = studentDb.loadById(id) ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
@ -128,4 +137,7 @@ class StudentRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
|
suspend fun updateStudentNickAndAvatar(studentNickAndAvatar: StudentNickAndAvatar) =
|
||||||
studentDb.update(studentNickAndAvatar)
|
studentDb.update(studentNickAndAvatar)
|
||||||
|
|
||||||
|
suspend fun isOneUniqueStudent() = getSavedStudents(false)
|
||||||
|
.distinctBy { it.student.studentName }.size == 1
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.SubjectDao
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -31,13 +31,15 @@ class SubjectRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean = false,
|
forceRefresh: Boolean = false,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
query = { subjectDao.loadAll(semester.diaryId, semester.studentId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getSubjects().mapToEntities(semester)
|
.getSubjects().mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -4,11 +4,11 @@ import io.github.wulkanowy.data.db.dao.TeacherDao
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
import io.github.wulkanowy.utils.getRefreshKey
|
||||||
import io.github.wulkanowy.utils.init
|
import io.github.wulkanowy.utils.init
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -31,13 +31,15 @@ class TeacherRepository @Inject constructor(
|
|||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, semester))
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
query = { teacherDb.loadAll(semester.studentId, semester.classId) },
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
sdk.init(student)
|
||||||
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getTeachers(semester.semesterId)
|
.getTeachers(semester.semesterId)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
|
@ -3,22 +3,13 @@ package io.github.wulkanowy.data.repositories
|
|||||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
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.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||||
|
import io.github.wulkanowy.data.networkBoundResource
|
||||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.*
|
||||||
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.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
@ -40,6 +31,10 @@ class TimetableRepository @Inject constructor(
|
|||||||
|
|
||||||
private val cacheKey = "timetable"
|
private val cacheKey = "timetable"
|
||||||
|
|
||||||
|
enum class TimetableType {
|
||||||
|
NORMAL, ADDITIONAL
|
||||||
|
}
|
||||||
|
|
||||||
fun getTimetable(
|
fun getTimetable(
|
||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
@ -47,9 +42,16 @@ class TimetableRepository @Inject constructor(
|
|||||||
end: LocalDate,
|
end: LocalDate,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
refreshAdditional: Boolean = false,
|
refreshAdditional: Boolean = false,
|
||||||
notify: Boolean = false
|
notify: Boolean = false,
|
||||||
|
timetableType: TimetableType = TimetableType.NORMAL
|
||||||
) = networkBoundResource(
|
) = networkBoundResource(
|
||||||
mutex = saveFetchResultMutex,
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = {
|
||||||
|
when (timetableType) {
|
||||||
|
TimetableType.NORMAL -> it.lessons.isEmpty()
|
||||||
|
TimetableType.ADDITIONAL -> it.additional.isEmpty()
|
||||||
|
}
|
||||||
|
},
|
||||||
shouldFetch = { (timetable, additional, headers) ->
|
shouldFetch = { (timetable, additional, headers) ->
|
||||||
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
|
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(refreshKey)
|
val isExpired = refreshHelper.shouldBeRefreshed(refreshKey)
|
||||||
@ -62,7 +64,7 @@ class TimetableRepository @Inject constructor(
|
|||||||
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
||||||
fetch = {
|
fetch = {
|
||||||
val timetableFull = sdk.init(student)
|
val timetableFull = sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getTimetableFull(start.monday, end.sunday)
|
.getTimetableFull(start.monday, end.sunday)
|
||||||
|
|
||||||
timetableFull.mapToEntities(semester)
|
timetableFull.mapToEntities(semester)
|
||||||
@ -152,7 +154,8 @@ class TimetableRepository @Inject constructor(
|
|||||||
old: List<TimetableAdditional>,
|
old: List<TimetableAdditional>,
|
||||||
new: List<TimetableAdditional>
|
new: List<TimetableAdditional>
|
||||||
) {
|
) {
|
||||||
timetableAdditionalDb.deleteAll(old uniqueSubtract new)
|
val oldFiltered = old.filter { !it.isAddedByUser }
|
||||||
|
timetableAdditionalDb.deleteAll(oldFiltered uniqueSubtract new)
|
||||||
timetableAdditionalDb.insertAll(new uniqueSubtract old)
|
timetableAdditionalDb.insertAll(new uniqueSubtract old)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,4 +163,14 @@ class TimetableRepository @Inject constructor(
|
|||||||
timetableHeaderDb.deleteAll(old uniqueSubtract new)
|
timetableHeaderDb.deleteAll(old uniqueSubtract new)
|
||||||
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun saveAdditionalList(additionalList: List<TimetableAdditional>) =
|
||||||
|
timetableAdditionalDb.insertAll(additionalList)
|
||||||
|
|
||||||
|
suspend fun deleteAdditional(additional: TimetableAdditional, deleteSeries: Boolean) =
|
||||||
|
if (deleteSeries) {
|
||||||
|
timetableAdditionalDb.deleteAllByRepeatId(additional.repeatId!!)
|
||||||
|
} else {
|
||||||
|
timetableAdditionalDb.deleteAll(listOf(additional))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package io.github.wulkanowy.data.serializers
|
||||||
|
|
||||||
|
import kotlinx.serialization.ExperimentalSerializationApi
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.nullable
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
|
object LocalDateSerializer : KSerializer<LocalDate?> {
|
||||||
|
|
||||||
|
override val descriptor = PrimitiveSerialDescriptor("LocalDate", PrimitiveKind.LONG).nullable
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: LocalDate?) {
|
||||||
|
if (value == null) {
|
||||||
|
encoder.encodeNull()
|
||||||
|
} else {
|
||||||
|
encoder.encodeNotNullMark()
|
||||||
|
encoder.encodeLong(value.toEpochDay())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): LocalDate? =
|
||||||
|
if (decoder.decodeNotNullMark()) {
|
||||||
|
LocalDate.ofEpochDay(decoder.decodeLong())
|
||||||
|
} else {
|
||||||
|
decoder.decodeNull()
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +0,0 @@
|
|||||||
package io.github.wulkanowy.services
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
|
|
||||||
abstract class HiltBroadcastReceiver : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.services.alarm
|
package io.github.wulkanowy.services.alarm
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@ -9,26 +10,23 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.onResourceError
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.services.HiltBroadcastReceiver
|
import io.github.wulkanowy.data.resourceFlow
|
||||||
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
|
import io.github.wulkanowy.services.sync.channels.UpcomingLessonsChannel.Companion.CHANNEL_ID
|
||||||
import io.github.wulkanowy.ui.modules.Destination
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||||
import io.github.wulkanowy.utils.PendingIntentCompat
|
import io.github.wulkanowy.utils.PendingIntentCompat
|
||||||
import io.github.wulkanowy.utils.flowWithResource
|
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
import io.github.wulkanowy.utils.toLocalDateTime
|
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
class TimetableNotificationReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var studentRepository: StudentRepository
|
lateinit var studentRepository: StudentRepository
|
||||||
@ -41,6 +39,8 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
|||||||
const val NOTIFICATION_TYPE_UPCOMING = 2
|
const val NOTIFICATION_TYPE_UPCOMING = 2
|
||||||
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
|
const val NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION = 3
|
||||||
|
|
||||||
|
// FIXME only shows one notification even if there are multiple students.
|
||||||
|
// Probably want to fix after #721 is merged.
|
||||||
const val NOTIFICATION_ID = 2137
|
const val NOTIFICATION_ID = 2137
|
||||||
|
|
||||||
const val STUDENT_NAME = "student_name"
|
const val STUDENT_NAME = "student_name"
|
||||||
@ -56,20 +56,24 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
|||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
super.onReceive(context, intent)
|
|
||||||
Timber.d("Receiving intent... ${intent.toUri(0)}")
|
Timber.d("Receiving intent... ${intent.toUri(0)}")
|
||||||
|
|
||||||
flowWithResource {
|
resourceFlow {
|
||||||
|
val showStudentName = !studentRepository.isOneUniqueStudent()
|
||||||
val student = studentRepository.getCurrentStudent(false)
|
val student = studentRepository.getCurrentStudent(false)
|
||||||
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
||||||
if (student.studentId == studentId) prepareNotification(context, intent)
|
|
||||||
else Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
|
if (student.studentId == studentId) {
|
||||||
}.onEach {
|
prepareNotification(context, intent, showStudentName)
|
||||||
if (it.status == Status.ERROR) Timber.e(it.error!!)
|
} else {
|
||||||
}.launchIn(GlobalScope)
|
Timber.d("Notification studentId($studentId) differs from current(${student.studentId})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onResourceError { Timber.e(it) }
|
||||||
|
.launchIn(GlobalScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prepareNotification(context: Context, intent: Intent) {
|
private fun prepareNotification(context: Context, intent: Intent, showStudentName: Boolean) {
|
||||||
val type = intent.getIntExtra(LESSON_TYPE, 0)
|
val type = intent.getIntExtra(LESSON_TYPE, 0)
|
||||||
val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent
|
val isPersistent = preferencesRepository.isUpcomingLessonsNotificationsPersistent
|
||||||
|
|
||||||
@ -78,7 +82,7 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
val studentId = intent.getIntExtra(STUDENT_ID, 0)
|
||||||
val studentName = intent.getStringExtra(STUDENT_NAME)
|
val studentName = intent.getStringExtra(STUDENT_NAME).takeIf { showStudentName }
|
||||||
|
|
||||||
val subject = intent.getStringExtra(LESSON_TITLE)
|
val subject = intent.getStringExtra(LESSON_TITLE)
|
||||||
val room = intent.getStringExtra(LESSON_ROOM)
|
val room = intent.getStringExtra(LESSON_ROOM)
|
||||||
@ -89,21 +93,28 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
|||||||
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
|
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
|
||||||
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
|
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
|
||||||
|
|
||||||
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
|
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId")
|
||||||
|
|
||||||
|
val notificationTitleResId =
|
||||||
|
if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next
|
||||||
|
val notificationTitle =
|
||||||
|
context.getString(notificationTitleResId, "($room) $subject".removePrefix("()"))
|
||||||
|
|
||||||
|
val nextLessonText = nextSubject?.let {
|
||||||
|
context.getString(
|
||||||
|
R.string.timetable_later,
|
||||||
|
"($nextRoom) $nextSubject".removePrefix("()")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
showNotification(
|
showNotification(
|
||||||
context, isPersistent, studentName,
|
context = context,
|
||||||
if (type == NOTIFICATION_TYPE_CURRENT) end else start, end - start,
|
isPersistent = isPersistent,
|
||||||
context.getString(
|
studentName = studentName,
|
||||||
if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next,
|
countDown = if (type == NOTIFICATION_TYPE_CURRENT) end else start,
|
||||||
"($room) $subject".removePrefix("()")
|
timeout = end - start,
|
||||||
),
|
title = notificationTitle,
|
||||||
nextSubject?.let {
|
next = nextLessonText
|
||||||
context.getString(
|
|
||||||
R.string.timetable_later,
|
|
||||||
"($nextRoom) $nextSubject".removePrefix("()")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +141,11 @@ class TimetableNotificationReceiver : HiltBroadcastReceiver() {
|
|||||||
.setTimeoutAfter(timeout)
|
.setTimeoutAfter(timeout)
|
||||||
.setSmallIcon(R.drawable.ic_stat_timetable)
|
.setSmallIcon(R.drawable.ic_stat_timetable)
|
||||||
.setColor(context.getCompatColor(R.color.colorPrimary))
|
.setColor(context.getCompatColor(R.color.colorPrimary))
|
||||||
.setStyle(NotificationCompat.InboxStyle().also {
|
.setStyle(NotificationCompat.InboxStyle()
|
||||||
it.setSummaryText(studentName)
|
.addLine(next)
|
||||||
it.addLine(next)
|
.also { inboxStyle ->
|
||||||
})
|
studentName?.let { inboxStyle.setSummaryText(it) }
|
||||||
|
})
|
||||||
.setContentIntent(
|
.setContentIntent(
|
||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
|
@ -5,6 +5,7 @@ import android.app.AlarmManager.RTC_WAKEUP
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import androidx.core.app.AlarmManagerCompat
|
import androidx.core.app.AlarmManagerCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
@ -27,12 +28,12 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
|
|||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import io.github.wulkanowy.utils.PendingIntentCompat
|
import io.github.wulkanowy.utils.PendingIntentCompat
|
||||||
import io.github.wulkanowy.utils.nickOrName
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import io.github.wulkanowy.utils.toTimestamp
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.time.Duration.ofMinutes
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.Instant.now
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.LocalDateTime.now
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TimetableNotificationSchedulerHelper @Inject constructor(
|
class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||||
@ -42,14 +43,14 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
private val dispatchersProvider: DispatchersProvider,
|
private val dispatchersProvider: DispatchersProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) =
|
private fun getRequestCode(time: Instant, studentId: Int): Int =
|
||||||
(time.toTimestamp() * studentId).toInt()
|
(time.toEpochMilli() * studentId).toInt()
|
||||||
|
|
||||||
private fun getUpcomingLessonTime(
|
private fun getUpcomingLessonTime(
|
||||||
index: Int,
|
index: Int,
|
||||||
day: List<Timetable>,
|
day: List<Timetable>,
|
||||||
lesson: Timetable
|
lesson: Timetable
|
||||||
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30))
|
||||||
|
|
||||||
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
|
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
|
||||||
val studentId = student.studentId
|
val studentId = student.studentId
|
||||||
@ -70,7 +71,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
|
private fun cancelScheduledTo(range: ClosedRange<Instant>, requestCode: Int) {
|
||||||
if (now() in range) cancelNotification()
|
if (now() in range) cancelNotification()
|
||||||
|
|
||||||
alarmManager.cancel(
|
alarmManager.cancel(
|
||||||
@ -91,6 +92,12 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
return cancelScheduled(lessons, student)
|
return cancelScheduled(lessons, student)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!canScheduleExactAlarms()) {
|
||||||
|
Timber.w("Exact alarms are disabled by user")
|
||||||
|
preferencesRepository.isUpcomingLessonsNotificationsEnable = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (lessons.firstOrNull()?.date?.isAfter(LocalDate.now().plusDays(2)) == true) {
|
if (lessons.firstOrNull()?.date?.isAfter(LocalDate.now().plusDays(2)) == true) {
|
||||||
Timber.d("Timetable notification scheduling skipped - lessons are too far")
|
Timber.d("Timetable notification scheduling skipped - lessons are too far")
|
||||||
return
|
return
|
||||||
@ -143,8 +150,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
putExtra(STUDENT_ID, student.studentId)
|
putExtra(STUDENT_ID, student.studentId)
|
||||||
putExtra(STUDENT_NAME, student.nickOrName)
|
putExtra(STUDENT_NAME, student.nickOrName)
|
||||||
putExtra(LESSON_ROOM, lesson.room)
|
putExtra(LESSON_ROOM, lesson.room)
|
||||||
putExtra(LESSON_START, lesson.start.toTimestamp())
|
putExtra(LESSON_START, lesson.start.toEpochMilli())
|
||||||
putExtra(LESSON_END, lesson.end.toTimestamp())
|
putExtra(LESSON_END, lesson.end.toEpochMilli())
|
||||||
putExtra(LESSON_TITLE, lesson.subject)
|
putExtra(LESSON_TITLE, lesson.subject)
|
||||||
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
|
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
|
||||||
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
|
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
|
||||||
@ -155,11 +162,11 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
intent: Intent,
|
intent: Intent,
|
||||||
studentId: Int,
|
studentId: Int,
|
||||||
notificationType: Int,
|
notificationType: Int,
|
||||||
time: LocalDateTime
|
time: Instant
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
AlarmManagerCompat.setExactAndAllowWhileIdle(
|
||||||
alarmManager, RTC_WAKEUP, time.toTimestamp(),
|
alarmManager, RTC_WAKEUP, time.toEpochMilli(),
|
||||||
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
|
||||||
it.putExtra(LESSON_TYPE, notificationType)
|
it.putExtra(LESSON_TYPE, notificationType)
|
||||||
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
|
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)
|
||||||
@ -169,8 +176,18 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
intent.getStringExtra(LESSON_TITLE)
|
intent.getStringExtra(LESSON_TITLE)
|
||||||
}, start: $time, student: $studentId"
|
}, start: $time, student: $studentId"
|
||||||
)
|
)
|
||||||
} catch (e: IllegalStateException) {
|
} catch (e: Throwable) {
|
||||||
Timber.e(e)
|
Timber.e(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canScheduleExactAlarms(): Boolean {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
try {
|
||||||
|
alarmManager.canScheduleExactAlarms()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,9 @@ class VulcanNotificationListenerService : NotificationListenerService() {
|
|||||||
override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) {
|
override fun onNotificationPosted(statusBarNotification: StatusBarNotification?) {
|
||||||
if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) {
|
if (statusBarNotification?.packageName == "pl.edu.vulcan.hebe" && preferenceRepository.isNotificationPiggybackEnabled) {
|
||||||
syncManager.startOneTimeSyncWorker()
|
syncManager.startOneTimeSyncWorker()
|
||||||
|
if (preferenceRepository.isNotificationPiggybackRemoveOriginalEnabled) {
|
||||||
|
cancelNotification(statusBarNotification.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,6 +15,9 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
|
class ShortcutsHelper @Inject constructor(@ApplicationContext private val context: Context) {
|
||||||
|
|
||||||
|
// Destination cannot be used here as shortcuts
|
||||||
|
// require their intents to only use primitive types (see PersistableBundle.isValidType).
|
||||||
|
|
||||||
private val destinations = mapOf(
|
private val destinations = mapOf(
|
||||||
"grade" to Destination.Grade,
|
"grade" to Destination.Grade,
|
||||||
"attendance" to Destination.Attendance,
|
"attendance" to Destination.Attendance,
|
||||||
|
@ -74,10 +74,12 @@ class SyncManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startOneTimeSyncWorker(): Flow<WorkInfo?> {
|
// if quiet, no notifications will be sent
|
||||||
|
fun startOneTimeSyncWorker(quiet: Boolean = false): Flow<WorkInfo?> {
|
||||||
val work = OneTimeWorkRequestBuilder<SyncWorker>()
|
val work = OneTimeWorkRequestBuilder<SyncWorker>()
|
||||||
.setInputData(
|
.setInputData(
|
||||||
Data.Builder()
|
Data.Builder()
|
||||||
|
.putBoolean("quiet", quiet)
|
||||||
.putBoolean("one_time", true)
|
.putBoolean("one_time", true)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
@ -23,7 +23,7 @@ import io.github.wulkanowy.utils.DispatchersProvider
|
|||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@HiltWorker
|
@HiltWorker
|
||||||
@ -38,18 +38,23 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
private val dispatchersProvider: DispatchersProvider
|
private val dispatchersProvider: DispatchersProvider
|
||||||
) : CoroutineWorker(appContext, workerParameters) {
|
) : CoroutineWorker(appContext, workerParameters) {
|
||||||
|
|
||||||
override suspend fun doWork() = withContext(dispatchersProvider.io) {
|
override suspend fun doWork(): Result = withContext(dispatchersProvider.io) {
|
||||||
Timber.i("SyncWorker is starting")
|
Timber.i("SyncWorker is starting")
|
||||||
|
|
||||||
if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure()
|
if (!studentRepository.isCurrentStudentSet()) return@withContext Result.failure()
|
||||||
|
|
||||||
val student = studentRepository.getCurrentStudent()
|
val (student, semester) = try {
|
||||||
val semester = semesterRepository.getCurrentSemester(student, true)
|
val student = studentRepository.getCurrentStudent()
|
||||||
|
val semester = semesterRepository.getCurrentSemester(student, true)
|
||||||
|
student to semester
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
return@withContext getResultFromErrors(listOf(e))
|
||||||
|
}
|
||||||
|
|
||||||
val exceptions = works.mapNotNull { work ->
|
val exceptions = works.mapNotNull { work ->
|
||||||
try {
|
try {
|
||||||
Timber.i("${work::class.java.simpleName} is starting")
|
Timber.i("${work::class.java.simpleName} is starting")
|
||||||
work.doWork(student, semester)
|
work.doWork(student, semester, isNotificationsEnabled())
|
||||||
Timber.i("${work::class.java.simpleName} result: Success")
|
Timber.i("${work::class.java.simpleName} result: Success")
|
||||||
null
|
null
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
@ -62,20 +67,7 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = when {
|
val result = getResultFromErrors(exceptions)
|
||||||
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
|
||||||
Result.failure(
|
|
||||||
Data.Builder()
|
|
||||||
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
exceptions.isNotEmpty() -> Result.retry()
|
|
||||||
else -> {
|
|
||||||
preferencesRepository.lasSyncDate = LocalDateTime.now()
|
|
||||||
Result.success()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preferencesRepository.isDebugNotificationEnable) notify(result)
|
if (preferencesRepository.isDebugNotificationEnable) notify(result)
|
||||||
Timber.i("SyncWorker result: $result")
|
Timber.i("SyncWorker result: $result")
|
||||||
@ -83,6 +75,27 @@ class SyncWorker @AssistedInject constructor(
|
|||||||
return@withContext result
|
return@withContext result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isNotificationsEnabled(): Boolean {
|
||||||
|
val quiet = inputData.getBoolean("quiet", false)
|
||||||
|
return preferencesRepository.isNotificationsEnable && !quiet
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getResultFromErrors(errors: List<Throwable>): Result = when {
|
||||||
|
errors.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
||||||
|
Result.failure(
|
||||||
|
Data.Builder()
|
||||||
|
.putString("error_message", errors.joinToString { it.message.toString() })
|
||||||
|
.putString("error_stack", errors.map { it.stackTraceToString() }.toString())
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
errors.isNotEmpty() -> Result.retry()
|
||||||
|
else -> {
|
||||||
|
preferencesRepository.lasSyncDate = Instant.now()
|
||||||
|
Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun notify(result: Result) {
|
private fun notify(result: Result) {
|
||||||
notificationManager.notify(
|
notificationManager.notify(
|
||||||
Random.nextInt(Int.MAX_VALUE),
|
Random.nextInt(Int.MAX_VALUE),
|
||||||
|
@ -14,11 +14,12 @@ import io.github.wulkanowy.data.pojos.GroupNotificationData
|
|||||||
import io.github.wulkanowy.data.pojos.NotificationData
|
import io.github.wulkanowy.data.pojos.NotificationData
|
||||||
import io.github.wulkanowy.data.repositories.NotificationRepository
|
import io.github.wulkanowy.data.repositories.NotificationRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
|
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||||
import io.github.wulkanowy.utils.PendingIntentCompat
|
import io.github.wulkanowy.utils.PendingIntentCompat
|
||||||
import io.github.wulkanowy.utils.getCompatBitmap
|
import io.github.wulkanowy.utils.getCompatBitmap
|
||||||
import io.github.wulkanowy.utils.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
import io.github.wulkanowy.utils.nickOrName
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
Random.nextInt(),
|
Random.nextInt(),
|
||||||
notificationData.intentToStart,
|
SplashActivity.getStartIntent(context, notificationData.destination),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -57,7 +58,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
NotificationCompat.BigTextStyle()
|
NotificationCompat.BigTextStyle()
|
||||||
.bigText(notificationData.content)
|
.bigText(notificationData.content)
|
||||||
.also { builder ->
|
.also { builder ->
|
||||||
if (shouldShowStudentName()) {
|
if (!studentRepository.isOneUniqueStudent()) {
|
||||||
builder.setSummaryText(student.nickOrName)
|
builder.setSummaryText(student.nickOrName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
student: Student
|
student: Student
|
||||||
) {
|
) {
|
||||||
val notificationType = groupNotificationData.type
|
val notificationType = groupNotificationData.type
|
||||||
val groupType = notificationType.group ?: return
|
val groupType = notificationType.channel
|
||||||
val group = "${groupType}_${student.id}"
|
val group = "${groupType}_${student.id}"
|
||||||
|
|
||||||
sendSummaryNotification(groupNotificationData, group, student)
|
sendSummaryNotification(groupNotificationData, group, student)
|
||||||
@ -92,7 +93,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
Random.nextInt(),
|
Random.nextInt(),
|
||||||
notificationData.intentToStart,
|
SplashActivity.getStartIntent(context, notificationData.destination),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -102,7 +103,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
NotificationCompat.BigTextStyle()
|
NotificationCompat.BigTextStyle()
|
||||||
.bigText(notificationData.content)
|
.bigText(notificationData.content)
|
||||||
.also { builder ->
|
.also { builder ->
|
||||||
if (shouldShowStudentName()) {
|
if (!studentRepository.isOneUniqueStudent()) {
|
||||||
builder.setSummaryText(student.nickOrName)
|
builder.setSummaryText(student.nickOrName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +135,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
.setStyle(
|
.setStyle(
|
||||||
NotificationCompat.InboxStyle()
|
NotificationCompat.InboxStyle()
|
||||||
.also { builder ->
|
.also { builder ->
|
||||||
if (shouldShowStudentName()) {
|
if (!studentRepository.isOneUniqueStudent()) {
|
||||||
builder.setSummaryText(student.nickOrName)
|
builder.setSummaryText(student.nickOrName)
|
||||||
}
|
}
|
||||||
groupNotificationData.notificationDataList.forEach {
|
groupNotificationData.notificationDataList.forEach {
|
||||||
@ -146,7 +147,7 @@ class AppNotificationManager @Inject constructor(
|
|||||||
PendingIntent.getActivity(
|
PendingIntent.getActivity(
|
||||||
context,
|
context,
|
||||||
Random.nextInt(),
|
Random.nextInt(),
|
||||||
groupNotificationData.intentToStart,
|
SplashActivity.getStartIntent(context, groupNotificationData.destination),
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -168,13 +169,11 @@ class AppNotificationManager @Inject constructor(
|
|||||||
studentId = student.id,
|
studentId = student.id,
|
||||||
title = notificationData.title,
|
title = notificationData.title,
|
||||||
content = notificationData.content,
|
content = notificationData.content,
|
||||||
|
destination = notificationData.destination,
|
||||||
type = notificationType,
|
type = notificationType,
|
||||||
date = LocalDateTime.now()
|
date = Instant.now(),
|
||||||
)
|
)
|
||||||
|
|
||||||
notificationRepository.saveNotification(notificationEntity)
|
notificationRepository.saveNotification(notificationEntity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun shouldShowStudentName(): Boolean =
|
|
||||||
studentRepository.getSavedStudents(decryptPass = false).size > 1
|
|
||||||
}
|
}
|
||||||
|
@ -8,11 +8,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
|||||||
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
import io.github.wulkanowy.data.pojos.GroupNotificationData
|
||||||
import io.github.wulkanowy.data.pojos.NotificationData
|
import io.github.wulkanowy.data.pojos.NotificationData
|
||||||
import io.github.wulkanowy.ui.modules.Destination
|
import io.github.wulkanowy.ui.modules.Destination
|
||||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
|
||||||
import io.github.wulkanowy.utils.getPlural
|
import io.github.wulkanowy.utils.getPlural
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ChangeTimetableNotification @Inject constructor(
|
class ChangeTimetableNotification @Inject constructor(
|
||||||
@ -21,10 +20,11 @@ class ChangeTimetableNotification @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun notify(items: List<Timetable>, student: Student) {
|
suspend fun notify(items: List<Timetable>, student: Student) {
|
||||||
val currentTime = LocalDateTime.now()
|
val currentTime = Instant.now()
|
||||||
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
|
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
|
||||||
val notificationDataList = changedLessons.groupBy { it.date }
|
val lessonsByDate = changedLessons.groupBy { it.date }
|
||||||
.map { (date, lessons) ->
|
val notificationDataList = lessonsByDate
|
||||||
|
.flatMap { (date, lessons) ->
|
||||||
getNotificationContents(date, lessons).map {
|
getNotificationContents(date, lessons).map {
|
||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(
|
title = context.getPlural(
|
||||||
@ -32,14 +32,10 @@ class ChangeTimetableNotification @Inject constructor(
|
|||||||
1
|
1
|
||||||
),
|
),
|
||||||
content = it,
|
content = it,
|
||||||
intentToStart = SplashActivity.getStartIntent(
|
destination = Destination.Timetable(date)
|
||||||
context = context,
|
|
||||||
destination = Destination.Timetable(date)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flatten()
|
|
||||||
.ifEmpty { return }
|
.ifEmpty { return }
|
||||||
|
|
||||||
val groupNotificationData = GroupNotificationData(
|
val groupNotificationData = GroupNotificationData(
|
||||||
@ -53,7 +49,7 @@ class ChangeTimetableNotification @Inject constructor(
|
|||||||
changedLessons.size,
|
changedLessons.size,
|
||||||
changedLessons.size
|
changedLessons.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Timetable()),
|
destination = Destination.Timetable(lessonsByDate.toSortedMap().firstKey()),
|
||||||
type = NotificationType.CHANGE_TIMETABLE
|
type = NotificationType.CHANGE_TIMETABLE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class NewAttendanceNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1),
|
title = context.getPlural(R.plurals.attendance_notify_new_items_title, 1),
|
||||||
content = it,
|
content = it,
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Attendance)
|
destination = Destination.Attendance
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class NewAttendanceNotification @Inject constructor(
|
|||||||
notificationDataList.size,
|
notificationDataList.size,
|
||||||
notificationDataList.size
|
notificationDataList.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Attendance),
|
destination = Destination.Attendance,
|
||||||
type = NotificationType.NEW_ATTENDANCE
|
type = NotificationType.NEW_ATTENDANCE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import io.github.wulkanowy.ui.modules.Destination
|
|||||||
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
import io.github.wulkanowy.ui.modules.splash.SplashActivity
|
||||||
import io.github.wulkanowy.utils.getPlural
|
import io.github.wulkanowy.utils.getPlural
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.LocalDateTime
|
import java.time.Instant
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NewConferenceNotification @Inject constructor(
|
class NewConferenceNotification @Inject constructor(
|
||||||
@ -20,7 +20,7 @@ class NewConferenceNotification @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun notify(items: List<Conference>, student: Student) {
|
suspend fun notify(items: List<Conference>, student: Student) {
|
||||||
val today = LocalDateTime.now()
|
val today = Instant.now()
|
||||||
val lines = items.filter { !it.date.isBefore(today) }
|
val lines = items.filter { !it.date.isBefore(today) }
|
||||||
.map {
|
.map {
|
||||||
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"
|
||||||
@ -31,7 +31,7 @@ class NewConferenceNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.conference_notify_new_item_title, 1),
|
title = context.getPlural(R.plurals.conference_notify_new_item_title, 1),
|
||||||
content = it,
|
content = it,
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Conference)
|
destination = Destination.Conference
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class NewConferenceNotification @Inject constructor(
|
|||||||
lines.size,
|
lines.size,
|
||||||
lines.size
|
lines.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Conference),
|
destination = Destination.Conference,
|
||||||
type = NotificationType.NEW_CONFERENCE
|
type = NotificationType.NEW_CONFERENCE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class NewExamNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.exam_notify_new_item_title, 1),
|
title = context.getPlural(R.plurals.exam_notify_new_item_title, 1),
|
||||||
content = it,
|
content = it,
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Exam),
|
destination = Destination.Exam,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class NewExamNotification @Inject constructor(
|
|||||||
lines.size,
|
lines.size,
|
||||||
lines.size
|
lines.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Exam),
|
destination = Destination.Exam,
|
||||||
type = NotificationType.NEW_EXAM
|
type = NotificationType.NEW_EXAM
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,8 +22,11 @@ class NewGradeNotification @Inject constructor(
|
|||||||
val notificationDataList = items.map {
|
val notificationDataList = items.map {
|
||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.grade_new_items, 1),
|
title = context.getPlural(R.plurals.grade_new_items, 1),
|
||||||
content = "${it.subject}: ${it.entry}",
|
content = buildString {
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
append("${it.subject}: ${it.entry}")
|
||||||
|
if (it.comment.isNotBlank()) append(" (${it.comment})")
|
||||||
|
},
|
||||||
|
destination = Destination.Grade,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
notificationDataList = notificationDataList,
|
notificationDataList = notificationDataList,
|
||||||
title = context.getPlural(R.plurals.grade_new_items, items.size),
|
title = context.getPlural(R.plurals.grade_new_items, items.size),
|
||||||
content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size),
|
content = context.getPlural(R.plurals.grade_notify_new_items, items.size, items.size),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
destination = Destination.Grade,
|
||||||
type = NotificationType.NEW_GRADE_DETAILS
|
type = NotificationType.NEW_GRADE_DETAILS
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.grade_new_items_predicted, 1),
|
title = context.getPlural(R.plurals.grade_new_items_predicted, 1),
|
||||||
content = "${it.subject}: ${it.predictedGrade}",
|
content = "${it.subject}: ${it.predictedGrade}",
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
destination = Destination.Grade,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
items.size,
|
items.size,
|
||||||
items.size
|
items.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
destination = Destination.Grade,
|
||||||
type = NotificationType.NEW_GRADE_PREDICTED
|
type = NotificationType.NEW_GRADE_PREDICTED
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -67,7 +70,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.grade_new_items_final, 1),
|
title = context.getPlural(R.plurals.grade_new_items_final, 1),
|
||||||
content = "${it.subject}: ${it.finalGrade}",
|
content = "${it.subject}: ${it.finalGrade}",
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
destination = Destination.Grade,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +82,7 @@ class NewGradeNotification @Inject constructor(
|
|||||||
items.size,
|
items.size,
|
||||||
items.size
|
items.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Grade),
|
destination = Destination.Grade,
|
||||||
type = NotificationType.NEW_GRADE_FINAL
|
type = NotificationType.NEW_GRADE_FINAL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class NewHomeworkNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.homework_notify_new_item_title, 1),
|
title = context.getPlural(R.plurals.homework_notify_new_item_title, 1),
|
||||||
content = it,
|
content = it,
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Homework),
|
destination = Destination.Homework,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class NewHomeworkNotification @Inject constructor(
|
|||||||
lines.size,
|
lines.size,
|
||||||
lines.size
|
lines.size
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Homework),
|
destination = Destination.Homework,
|
||||||
type = NotificationType.NEW_HOMEWORK,
|
type = NotificationType.NEW_HOMEWORK,
|
||||||
notificationDataList = notificationDataList
|
notificationDataList = notificationDataList
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,7 @@ class NewLuckyNumberNotification @Inject constructor(
|
|||||||
R.string.lucky_number_notify_new_item,
|
R.string.lucky_number_notify_new_item,
|
||||||
item.luckyNumber.toString()
|
item.luckyNumber.toString()
|
||||||
),
|
),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.LuckyNumber)
|
destination = Destination.LuckyNumber
|
||||||
)
|
)
|
||||||
|
|
||||||
appNotificationManager.sendSingleNotification(
|
appNotificationManager.sendSingleNotification(
|
||||||
|
@ -22,7 +22,7 @@ class NewMessageNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(R.plurals.message_new_items, 1),
|
title = context.getPlural(R.plurals.message_new_items, 1),
|
||||||
content = "${it.sender}: ${it.subject}",
|
content = "${it.sender}: ${it.subject}",
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Message),
|
destination = Destination.Message,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ class NewMessageNotification @Inject constructor(
|
|||||||
notificationDataList = notificationDataList,
|
notificationDataList = notificationDataList,
|
||||||
title = context.getPlural(R.plurals.message_new_items, items.size),
|
title = context.getPlural(R.plurals.message_new_items, items.size),
|
||||||
content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size),
|
content = context.getPlural(R.plurals.message_notify_new_items, items.size, items.size),
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Message),
|
destination = Destination.Message,
|
||||||
type = NotificationType.NEW_MESSAGE
|
type = NotificationType.NEW_MESSAGE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,13 +29,13 @@ class NewNoteNotification @Inject constructor(
|
|||||||
NotificationData(
|
NotificationData(
|
||||||
title = context.getPlural(titleRes, 1),
|
title = context.getPlural(titleRes, 1),
|
||||||
content = "${it.teacher}: ${it.category}",
|
content = "${it.teacher}: ${it.category}",
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Note),
|
destination = Destination.Note,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val groupNotificationData = GroupNotificationData(
|
val groupNotificationData = GroupNotificationData(
|
||||||
notificationDataList = notificationDataList,
|
notificationDataList = notificationDataList,
|
||||||
intentToStart = SplashActivity.getStartIntent(context, Destination.Note),
|
destination = Destination.Note,
|
||||||
title = context.getPlural(R.plurals.note_new_items, items.size),
|
title = context.getPlural(R.plurals.note_new_items, items.size),
|
||||||
content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size),
|
content = context.getPlural(R.plurals.note_notify_new_items, items.size, items.size),
|
||||||
type = NotificationType.NEW_NOTE
|
type = NotificationType.NEW_NOTE
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.services.sync.notifications
|
package io.github.wulkanowy.services.sync.notifications
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.core.text.parseAsHtml
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
import io.github.wulkanowy.data.db.entities.SchoolAnnouncement
|
||||||
@ -20,23 +21,17 @@ class NewSchoolAnnouncementNotification @Inject constructor(
|
|||||||
suspend fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
suspend fun notify(items: List<SchoolAnnouncement>, student: Student) {
|
||||||
val notificationDataList = items.map {
|
val notificationDataList = items.map {
|
||||||
NotificationData(
|
NotificationData(
|
||||||
intentToStart = SplashActivity.getStartIntent(
|
destination = Destination.SchoolAnnouncement,
|
||||||
context = context,
|
|
||||||
destination = Destination.SchoolAnnouncement
|
|
||||||
),
|
|
||||||
title = context.getPlural(
|
title = context.getPlural(
|
||||||
R.plurals.school_announcement_notify_new_item_title,
|
R.plurals.school_announcement_notify_new_item_title,
|
||||||
1
|
1
|
||||||
),
|
),
|
||||||
content = "${it.subject}: ${it.content}"
|
content = "${it.subject}: ${it.content.parseAsHtml()}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val groupNotificationData = GroupNotificationData(
|
val groupNotificationData = GroupNotificationData(
|
||||||
type = NotificationType.NEW_ANNOUNCEMENT,
|
type = NotificationType.NEW_ANNOUNCEMENT,
|
||||||
intentToStart = SplashActivity.getStartIntent(
|
destination = Destination.SchoolAnnouncement,
|
||||||
context = context,
|
|
||||||
destination = Destination.SchoolAnnouncement
|
|
||||||
),
|
|
||||||
title = context.getPlural(
|
title = context.getPlural(
|
||||||
R.plurals.school_announcement_notify_new_item_title,
|
R.plurals.school_announcement_notify_new_item_title,
|
||||||
items.size
|
items.size
|
||||||
|
@ -14,72 +14,58 @@ import io.github.wulkanowy.services.sync.channels.PushChannel
|
|||||||
import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel
|
import io.github.wulkanowy.services.sync.channels.TimetableChangeChannel
|
||||||
|
|
||||||
enum class NotificationType(
|
enum class NotificationType(
|
||||||
val group: String?,
|
|
||||||
val channel: String,
|
val channel: String,
|
||||||
val icon: Int
|
val icon: Int
|
||||||
) {
|
) {
|
||||||
NEW_CONFERENCE(
|
NEW_CONFERENCE(
|
||||||
group = "new_conferences_group",
|
|
||||||
channel = NewConferencesChannel.CHANNEL_ID,
|
channel = NewConferencesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_more_conferences,
|
icon = R.drawable.ic_more_conferences,
|
||||||
),
|
),
|
||||||
NEW_EXAM(
|
NEW_EXAM(
|
||||||
group = "new_exam_group",
|
|
||||||
channel = NewExamChannel.CHANNEL_ID,
|
channel = NewExamChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_main_exam
|
icon = R.drawable.ic_main_exam
|
||||||
),
|
),
|
||||||
NEW_GRADE_DETAILS(
|
NEW_GRADE_DETAILS(
|
||||||
group = "new_grade_details_group",
|
|
||||||
channel = NewGradesChannel.CHANNEL_ID,
|
channel = NewGradesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
),
|
),
|
||||||
NEW_GRADE_PREDICTED(
|
NEW_GRADE_PREDICTED(
|
||||||
group = "new_grade_predicted_group",
|
|
||||||
channel = NewGradesChannel.CHANNEL_ID,
|
channel = NewGradesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
),
|
),
|
||||||
NEW_GRADE_FINAL(
|
NEW_GRADE_FINAL(
|
||||||
group = "new_grade_final_group",
|
|
||||||
channel = NewGradesChannel.CHANNEL_ID,
|
channel = NewGradesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_grade,
|
icon = R.drawable.ic_stat_grade,
|
||||||
),
|
),
|
||||||
NEW_HOMEWORK(
|
NEW_HOMEWORK(
|
||||||
group = "new_homework_group",
|
|
||||||
channel = NewHomeworkChannel.CHANNEL_ID,
|
channel = NewHomeworkChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_more_homework,
|
icon = R.drawable.ic_more_homework,
|
||||||
),
|
),
|
||||||
NEW_LUCKY_NUMBER(
|
NEW_LUCKY_NUMBER(
|
||||||
group = null,
|
|
||||||
channel = LuckyNumberChannel.CHANNEL_ID,
|
channel = LuckyNumberChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_luckynumber,
|
icon = R.drawable.ic_stat_luckynumber,
|
||||||
),
|
),
|
||||||
NEW_MESSAGE(
|
NEW_MESSAGE(
|
||||||
group = "new_message_group",
|
|
||||||
channel = NewMessagesChannel.CHANNEL_ID,
|
channel = NewMessagesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_message,
|
icon = R.drawable.ic_stat_message,
|
||||||
),
|
),
|
||||||
NEW_NOTE(
|
NEW_NOTE(
|
||||||
group = "new_notes_group",
|
|
||||||
channel = NewNotesChannel.CHANNEL_ID,
|
channel = NewNotesChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_note
|
icon = R.drawable.ic_stat_note
|
||||||
),
|
),
|
||||||
NEW_ANNOUNCEMENT(
|
NEW_ANNOUNCEMENT(
|
||||||
group = "new_school_announcements_group",
|
|
||||||
channel = NewSchoolAnnouncementsChannel.CHANNEL_ID,
|
channel = NewSchoolAnnouncementsChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_all_about
|
icon = R.drawable.ic_all_about
|
||||||
),
|
),
|
||||||
CHANGE_TIMETABLE(
|
CHANGE_TIMETABLE(
|
||||||
group = "change_timetable_group",
|
|
||||||
channel = TimetableChangeChannel.CHANNEL_ID,
|
channel = TimetableChangeChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_main_timetable
|
icon = R.drawable.ic_main_timetable
|
||||||
),
|
),
|
||||||
NEW_ATTENDANCE(
|
NEW_ATTENDANCE(
|
||||||
group = "new_attendance_group",
|
|
||||||
channel = NewAttendanceChannel.CHANNEL_ID,
|
channel = NewAttendanceChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_main_attendance
|
icon = R.drawable.ic_main_attendance
|
||||||
),
|
),
|
||||||
PUSH(
|
PUSH(
|
||||||
group = null,
|
|
||||||
channel = PushChannel.CHANNEL_ID,
|
channel = PushChannel.CHANNEL_ID,
|
||||||
icon = R.drawable.ic_stat_all
|
icon = R.drawable.ic_stat_all
|
||||||
)
|
)
|
||||||
|
@ -3,14 +3,19 @@ package io.github.wulkanowy.services.sync.works
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
|
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
|
||||||
import io.github.wulkanowy.utils.waitForResult
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AttendanceSummaryWork @Inject constructor(
|
class AttendanceSummaryWork @Inject constructor(
|
||||||
private val attendanceSummaryRepository: AttendanceSummaryRepository
|
private val attendanceSummaryRepository: AttendanceSummaryRepository
|
||||||
) : Work {
|
) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester) {
|
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||||
attendanceSummaryRepository.getAttendanceSummary(student, semester, -1, true).waitForResult()
|
attendanceSummaryRepository.getAttendanceSummary(
|
||||||
|
student = student,
|
||||||
|
semester = semester,
|
||||||
|
subjectId = -1,
|
||||||
|
forceRefresh = true,
|
||||||
|
).waitForResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,9 @@ package io.github.wulkanowy.services.sync.works
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
import io.github.wulkanowy.data.repositories.AttendanceRepository
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
|
import io.github.wulkanowy.services.sync.notifications.NewAttendanceNotification
|
||||||
import io.github.wulkanowy.utils.previousOrSameSchoolDay
|
import io.github.wulkanowy.utils.previousOrSameSchoolDay
|
||||||
import io.github.wulkanowy.utils.waitForResult
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.now
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -14,17 +13,16 @@ import javax.inject.Inject
|
|||||||
class AttendanceWork @Inject constructor(
|
class AttendanceWork @Inject constructor(
|
||||||
private val attendanceRepository: AttendanceRepository,
|
private val attendanceRepository: AttendanceRepository,
|
||||||
private val newAttendanceNotification: NewAttendanceNotification,
|
private val newAttendanceNotification: NewAttendanceNotification,
|
||||||
private val preferencesRepository: PreferencesRepository
|
|
||||||
) : Work {
|
) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester) {
|
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||||
attendanceRepository.getAttendance(
|
attendanceRepository.getAttendance(
|
||||||
student = student,
|
student = student,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
start = now().previousOrSameSchoolDay,
|
start = now().previousOrSameSchoolDay,
|
||||||
end = now().previousOrSameSchoolDay,
|
end = now().previousOrSameSchoolDay,
|
||||||
forceRefresh = true,
|
forceRefresh = true,
|
||||||
notify = preferencesRepository.isNotificationsEnable
|
notify = notify,
|
||||||
)
|
)
|
||||||
.waitForResult()
|
.waitForResult()
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user