Compare commits
214 Commits
Author | SHA1 | Date | |
---|---|---|---|
18dbbba328 | |||
d99c93ec05 | |||
f8431d7ad6 | |||
b195fda026 | |||
b1a5a77559 | |||
4be6663752 | |||
f7fa89638a | |||
4fedb74005 | |||
56d7e94946 | |||
b2af5ed57d | |||
b1d22843b5 | |||
623f0339e6 | |||
1f30cc1f90 | |||
de8b38dd9c | |||
e7054bb5b9 | |||
6978ad11eb | |||
327e61bbdd | |||
2c94347668 | |||
bce2c39ccc | |||
8752607433 | |||
c67d2d767d | |||
253e55f70e | |||
a7cf54897a | |||
cb914fe32b | |||
7aa65e98ce | |||
8d2d7922f9 | |||
bb7e927065 | |||
349307b6a3 | |||
9981f458d0 | |||
a1b9ae2826 | |||
9afb38d5e2 | |||
97a7b34b99 | |||
6398c9a097 | |||
597d1d763e | |||
2203956228 | |||
b3c6e2004b | |||
a2a31df98e | |||
060bab46e2 | |||
a350a167f3 | |||
cc2079f4c9 | |||
d778c99bbb | |||
c4672b8de9 | |||
1b40e339b7 | |||
ee5ac46493 | |||
ef398f7409 | |||
5331bf90cd | |||
9ce1ba75b2 | |||
fba86930fe | |||
a495fcbc5f | |||
f11354dd35 | |||
12a54278fc | |||
6ddaeb99da | |||
9ae2ffe7ae | |||
5b0e47b1c5 | |||
3e09a1dcee | |||
63bd5f95cb | |||
531c7592b2 | |||
3d28168749 | |||
0c2fd1d2db | |||
811f839949 | |||
ea26a6c1c9 | |||
ac446e4f91 | |||
78ae23df68 | |||
d21e4afad2 | |||
6f819bcb80 | |||
4d237d3672 | |||
af6d5c3063 | |||
87b8989dc8 | |||
2760318f3d | |||
6596f3226b | |||
22dd16d278 | |||
d1b198222d | |||
277c3c7f0b | |||
f7d12670e7 | |||
32d6b4a7a6 | |||
6df3f22c7d | |||
95cf521f63 | |||
ef9e4b7ad9 | |||
ac4a822930 | |||
89678c2276 | |||
e1bffabf10 | |||
af8bb53c17 | |||
0306e38130 | |||
84812fb048 | |||
2293e8c1e6 | |||
368028c6f4 | |||
13650b3e0d | |||
4bb1198735 | |||
3eb74da945 | |||
5161fdd543 | |||
377e0c3a0d | |||
1c9860091a | |||
a383f7409d | |||
9a8fb593c0 | |||
f4c6e0ad1b | |||
b30b7c3318 | |||
897eac050a | |||
83974b6550 | |||
7efd106658 | |||
9cedab979c | |||
510e2d5b88 | |||
63d6a0b325 | |||
da0943b319 | |||
67875b1a9a | |||
aa3d7e37fc | |||
ede5914d70 | |||
09c968f273 | |||
345b580601 | |||
61240777cf | |||
7588345b6d | |||
2fa26c37a9 | |||
c34c63c128 | |||
a26dadb224 | |||
277ffd22be | |||
f1479d489b | |||
fba4e85311 | |||
4a5991ade4 | |||
a735c378f1 | |||
217ebfc549 | |||
df5155f1c7 | |||
b93c0222a2 | |||
083ca34f1b | |||
5d5dfd4eb4 | |||
429fdfa4a0 | |||
302d723cfb | |||
8f50ee82b3 | |||
9dc1220496 | |||
ae39bd94e5 | |||
85ce23845f | |||
890d60811b | |||
49e68f5c8b | |||
1df4679db8 | |||
e03aae2d56 | |||
9c60ce688b | |||
fdce2cf477 | |||
650cbd5a10 | |||
b160367744 | |||
6c115fb915 | |||
7a408899df | |||
4bce35f810 | |||
2d83218f61 | |||
d3e276d6fc | |||
51a1097bb4 | |||
db4f172fb8 | |||
4d49e956b8 | |||
b8296ac02f | |||
d6385e8cdd | |||
885319a885 | |||
fded5007c1 | |||
66ff14f719 | |||
1257dc63d3 | |||
50b6d380b6 | |||
62b7d42a73 | |||
21fe209246 | |||
02cd4e4e06 | |||
86fe2b61cb | |||
4113bd9b53 | |||
d924902dac | |||
b269360ecb | |||
ffd5addadb | |||
c5e2b18695 | |||
515a3973b7 | |||
7bee10d5ce | |||
22a4f509dc | |||
3925a6261b | |||
49b383fbe5 | |||
4a484dc2ce | |||
a14c4b489b | |||
e91cd18804 | |||
4c24363599 | |||
e20c232f8f | |||
1f11eea9b5 | |||
42f9a00e8c | |||
ad487e680c | |||
3f431022a5 | |||
cd037f0ce0 | |||
37f7f21a03 | |||
c653039590 | |||
95a90a7a79 | |||
4dc80595ac | |||
8114a2376e | |||
a523850216 | |||
354f51dd70 | |||
b271c12ebc | |||
8ca41b5ba3 | |||
edbe45332a | |||
1bbd249275 | |||
5148ff291b | |||
a1dc00af42 | |||
f1db993fee | |||
4f0519552e | |||
3625c5c518 | |||
afbfb9761f | |||
a5c636853a | |||
d5d45ed1ba | |||
d3f869c6c2 | |||
46c29c438e | |||
73a7255d3a | |||
c7af85e0e1 | |||
afc16e3d17 | |||
59f6f5c212 | |||
86f8763e69 | |||
157becb017 | |||
83ca9a7060 | |||
1033be4503 | |||
e67066f3ae | |||
bc22808b0e | |||
e05abb3539 | |||
6153c7b97d | |||
e574e5e2ec | |||
e6571a1dfc | |||
d566de0282 | |||
558db061f5 | |||
190f40ede8 |
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@ -2,10 +2,12 @@ name: Tests
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, develop ]
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
- 'hotfix/**'
|
||||||
tags: [ '*' ]
|
tags: [ '*' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ master, develop ]
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -119,3 +119,4 @@ Thumbs.db
|
|||||||
app/src/release/agconnect-services.json
|
app/src/release/agconnect-services.json
|
||||||
app/src/release/agconnect-credentials.json
|
app/src/release/agconnect-credentials.json
|
||||||
.idea/deploymentTargetDropDown.xml
|
.idea/deploymentTargetDropDown.xml
|
||||||
|
.idea/kotlinc.xml
|
||||||
|
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 2022 Wulkanowy
|
Copyright 2023 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.
|
||||||
|
15
README.cs.md
15
README.cs.md
@ -1,18 +1,13 @@
|
|||||||
[English version of README](README.en.md)
|
Česká verze / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
[Deutsche Version von README](README.de.md)
|
|
||||||
|
|
||||||
[Polska wersja README](README.md)
|
|
||||||
|
|
||||||
[Slovenská verzia README](README.sk.md)
|
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
||||||
|
|
||||||
@ -39,7 +34,7 @@ Neoficiální klient deníku VULCAN UONET+ pro žáka a rodiče
|
|||||||
* podpora více účtů s možností přejmenování žáků
|
* podpora více účtů s možností přejmenování žáků
|
||||||
* tmavý a černý (AMOLED) motiv
|
* tmavý a černý (AMOLED) motiv
|
||||||
* offline režim
|
* offline režim
|
||||||
* žádné reklamy
|
* volitelné reklamy na podporu projektu
|
||||||
|
|
||||||
## Stáhnout
|
## Stáhnout
|
||||||
|
|
||||||
@ -57,7 +52,7 @@ Aktuální verzi si můžete stáhnout z Google Play, F-Droid nebo Huawei AppGal
|
|||||||
|
|
||||||
Můžete si také stáhnout [vývojovou verzi](https://wulkanowy.github.io/#download), která zahrnuje nové funkce připravované pro příští vydání
|
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
|
## Postaveno s pomocí
|
||||||
|
|
||||||
|
|
||||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
11
README.de.md
11
README.de.md
@ -1,14 +1,13 @@
|
|||||||
[Polska wersja README](README.md)
|
[Česká verze](README.cs.md) / Deutsche Version / [English version](README.en.md) / [Polska wersja](README.md) / [Slovenská verzia](README.sk.md)
|
||||||
|
|
||||||
[English version of README](README.en.md)
|
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre Eltern
|
||||||
|
|
||||||
@ -22,7 +21,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
|
|||||||
* Prozentsatz der Anwesenheit
|
* Prozentsatz der Anwesenheit
|
||||||
* Prüfungen
|
* Prüfungen
|
||||||
* Stundenplan
|
* Stundenplan
|
||||||
* Unterricht abgeschlossen
|
* abgeschlossene Unterrichtsstunden
|
||||||
* Nachrichten
|
* Nachrichten
|
||||||
* Hausaufgaben
|
* Hausaufgaben
|
||||||
* Anmerkungen
|
* Anmerkungen
|
||||||
@ -35,7 +34,7 @@ Inoffizieller Android VULCAN UONET+ Registrierungsclient für Schüler und ihre
|
|||||||
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
|
* Unterstützung für mehrere Konten mit der Möglichkeit, den Namen des Schülers zu ändern
|
||||||
* dunkles und schwarzes (AMOLED) Thema
|
* dunkles und schwarzes (AMOLED) Thema
|
||||||
* Offline-Modus
|
* Offline-Modus
|
||||||
* keine Werbung
|
* optionale Werbungen, die es uns ermöglichen das Projekt zu unterstützen
|
||||||
|
|
||||||
## Herunterladen
|
## Herunterladen
|
||||||
|
|
||||||
|
13
README.en.md
13
README.en.md
@ -1,18 +1,13 @@
|
|||||||
[Polska wersja README](README.md)
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / English version / [Polska wersja](README.md) / [Slovenská verzia](README.sk.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)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Unofficial android VULCAN UONET+ register client for both students and their parents
|
Unofficial android VULCAN UONET+ register client for both students and their parents
|
||||||
|
|
||||||
@ -39,7 +34,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
|
|||||||
* support for multiple accounts with the ability to rename students
|
* support for multiple accounts with the ability to rename students
|
||||||
* dark and black (AMOLED) theme
|
* dark and black (AMOLED) theme
|
||||||
* offline mode
|
* offline mode
|
||||||
* no ads
|
* optional ads which allow to support the project
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@ -1,18 +1,13 @@
|
|||||||
[English version of README](README.en.md)
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / Polska wersja / [Slovenská verzia](README.sk.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)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
||||||
|
|
||||||
@ -39,7 +34,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
|
|||||||
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
* obsługa wielu kont wraz z możliwością zmiany nazwy ucznia
|
||||||
* ciemny i czarny (AMOLED) motyw
|
* ciemny i czarny (AMOLED) motyw
|
||||||
* tryb offline
|
* tryb offline
|
||||||
* brak reklam
|
* opcjonalne reklamy umożliwiające wsparcie projektu
|
||||||
|
|
||||||
## Pobierz
|
## Pobierz
|
||||||
|
|
||||||
|
15
README.sk.md
15
README.sk.md
@ -1,18 +1,13 @@
|
|||||||
[English version of README](README.en.md)
|
[Česká verze](README.cs.md) / [Deutsche Version](README.de.md) / [English version](README.en.md) / [Polska wersja](README.md) / Slovenská verzia
|
||||||
|
|
||||||
[Deutsche Version von README](README.de.md)
|
|
||||||
|
|
||||||
[Polska wersja README](README.md)
|
|
||||||
|
|
||||||
[Česká verze README](README.cs.md)
|
|
||||||
|
|
||||||
# Wulkanowy
|
# Wulkanowy
|
||||||
|
|
||||||
[](https://github.com/wulkanowy/wulkanowy/actions)
|
[](https://github.com/wulkanowy/wulkanowy/actions)
|
||||||
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
[](https://codecov.io/gh/wulkanowy/wulkanowy)
|
||||||
[](https://discord.gg/vccAQBr)
|
[](https://discord.gg/vccAQBr)
|
||||||
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
[](https://f-droid.org/packages/io.github.wulkanowy/)
|
||||||
[](https://github.com/wulkanowy/wulkanowy/releases)
|
[](https://github.com/wulkanowy/wulkanowy/releases)
|
||||||
|
[](https://translate.wulkanowy.net.pl)
|
||||||
|
|
||||||
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
||||||
|
|
||||||
@ -39,7 +34,7 @@ Neoficiálny klient denníka VULCAN UONET+ pre žiaka a rodičov
|
|||||||
* podpora viacerých účtov s možnosťou premenovania žiakov
|
* podpora viacerých účtov s možnosťou premenovania žiakov
|
||||||
* tmavý a čierny (AMOLED) motív
|
* tmavý a čierny (AMOLED) motív
|
||||||
* offline režim
|
* offline režim
|
||||||
* žiadne reklamy
|
* voliteľné reklamy na podporu projektu
|
||||||
|
|
||||||
## Stiahnuť
|
## Stiahnuť
|
||||||
|
|
||||||
@ -57,7 +52,7 @@ Aktuálnu verziu si môžete stiahnuť z Google Play, F-Droid alebo Huawei AppGa
|
|||||||
|
|
||||||
Môžete si tiež stiahnuť [vývojovú verziu](https://wulkanowy.github.io/#download), ktorá zahrňuje nové funkcie pripravované pre budúce vydanie
|
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
|
## Postavené s pomocou
|
||||||
|
|
||||||
|
|
||||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
|
@ -16,15 +16,15 @@ apply from: 'hooks.gradle'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'io.github.wulkanowy'
|
namespace 'io.github.wulkanowy'
|
||||||
compileSdkVersion 32
|
compileSdkVersion 33
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "io.github.wulkanowy"
|
applicationId "io.github.wulkanowy"
|
||||||
testApplicationId "io.github.tests.wulkanowy"
|
testApplicationId "io.github.tests.wulkanowy"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 32
|
targetSdkVersion 33
|
||||||
versionCode 111
|
versionCode 122
|
||||||
versionName "1.7.2"
|
versionName "2.0.0"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
resValue "string", "app_name", "Wulkanowy"
|
resValue "string", "app_name", "Wulkanowy"
|
||||||
@ -161,8 +161,8 @@ play {
|
|||||||
defaultToAppBundles = false
|
defaultToAppBundles = false
|
||||||
track = 'production'
|
track = 'production'
|
||||||
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
|
||||||
userFraction = 0.05d
|
userFraction = 0.50d
|
||||||
updatePriority = 5
|
updatePriority = 2
|
||||||
enabled.set(false)
|
enabled.set(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,44 +177,44 @@ huaweiPublish {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
work_manager = "2.7.1"
|
work_manager = "2.8.1"
|
||||||
android_hilt = "1.0.0"
|
android_hilt = "1.0.0"
|
||||||
room = "2.4.3"
|
room = "2.5.1"
|
||||||
chucker = "3.5.2"
|
chucker = "3.5.2"
|
||||||
mockk = "1.12.7"
|
mockk = "1.13.5"
|
||||||
coroutines = "1.6.4"
|
coroutines = "1.6.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:1.7.2"
|
implementation "io.github.wulkanowy:sdk:2.0.0"
|
||||||
|
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
|
||||||
|
|
||||||
implementation "androidx.core:core-ktx:1.8.0"
|
implementation "androidx.core:core-ktx:1.10.0"
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation "androidx.activity:activity-ktx:1.5.1"
|
implementation "androidx.activity:activity-ktx:1.7.1"
|
||||||
implementation "androidx.appcompat:appcompat:1.5.0"
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.5.2"
|
implementation "androidx.fragment:fragment-ktx:1.5.7"
|
||||||
implementation "androidx.annotation:annotation:1.4.0"
|
implementation "androidx.annotation:annotation:1.6.0"
|
||||||
|
|
||||||
implementation "androidx.preference:preference-ktx:1.2.0"
|
implementation "androidx.preference:preference-ktx:1.2.0"
|
||||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
implementation "androidx.recyclerview:recyclerview:1.3.0"
|
||||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
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.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
|
||||||
implementation "com.google.android.material:material:1.6.1"
|
implementation "com.google.android.material:material:1.8.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.3.0'
|
||||||
|
|
||||||
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.5.1"
|
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.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"
|
||||||
@ -229,28 +229,30 @@ dependencies {
|
|||||||
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
implementation "com.github.YarikSOffice:lingver:1.3.0"
|
||||||
|
|
||||||
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:1.0.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.10.0"
|
implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
|
||||||
|
|
||||||
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"
|
||||||
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
|
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
|
||||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
|
||||||
implementation "io.coil-kt:coil:2.2.0"
|
implementation "io.coil-kt:coil:2.3.0"
|
||||||
implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
|
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
|
||||||
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
|
||||||
implementation 'com.fredporciuncula:flow-preferences:1.8.0'
|
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
|
||||||
|
implementation 'org.apache.commons:commons-text:1.10.0'
|
||||||
|
|
||||||
playImplementation platform('com.google.firebase:firebase-bom:30.3.2')
|
playImplementation platform('com.google.firebase:firebase-bom:31.5.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.firebase:firebase-config-ktx'
|
||||||
playImplementation 'com.google.android.play:core:1.10.3'
|
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:21.1.0'
|
playImplementation 'com.google.android.gms:play-services-ads:22.0.0'
|
||||||
|
|
||||||
hmsImplementation 'com.huawei.hms:hianalytics:6.7.0.300'
|
hmsImplementation 'com.huawei.hms:hianalytics:6.9.1.200'
|
||||||
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.7.1.300'
|
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.0.300'
|
||||||
|
|
||||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
|
||||||
|
|
||||||
@ -263,17 +265,17 @@ dependencies {
|
|||||||
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.8.2'
|
testImplementation 'org.robolectric:robolectric:4.10'
|
||||||
testImplementation "androidx.test:runner:1.4.0"
|
testImplementation "androidx.test:runner:1.5.2"
|
||||||
testImplementation "androidx.test.ext:junit:1.1.3"
|
testImplementation "androidx.test.ext:junit:1.1.5"
|
||||||
testImplementation "androidx.test:core:1.4.0"
|
testImplementation "androidx.test:core:1.5.0"
|
||||||
testImplementation "androidx.room:room-testing:$room"
|
testImplementation "androidx.room:room-testing:$room"
|
||||||
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
testImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
|
||||||
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
kaptTest "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||||
|
|
||||||
androidTestImplementation "androidx.test:core:1.4.0"
|
androidTestImplementation "androidx.test:core:1.5.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.4.0"
|
androidTestImplementation "androidx.test:runner:1.5.2"
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
androidTestImplementation "androidx.test.ext:junit:1.1.5"
|
||||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
}
|
}
|
||||||
|
2421
app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json
Normal file
2421
app/schemas/io.github.wulkanowy.data.db.AppDatabase/52.json
Normal file
File diff suppressed because it is too large
Load Diff
2439
app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json
Normal file
2439
app/schemas/io.github.wulkanowy.data.db.AppDatabase/53.json
Normal file
File diff suppressed because it is too large
Load Diff
2439
app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json
Normal file
2439
app/schemas/io.github.wulkanowy.data.db.AppDatabase/54.json
Normal file
File diff suppressed because it is too large
Load Diff
2435
app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json
Normal file
2435
app/schemas/io.github.wulkanowy.data.db.AppDatabase/55.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<background android:drawable="@color/colorPrimary" />
|
<background android:drawable="@color/colorIcon" />
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
||||||
</adaptive-icon>
|
<monochrome android:drawable="@drawable/ic_launcher_foreground_dev_mono" />
|
||||||
|
</adaptive-icon>
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/colorPrimary" />
|
|
||||||
<foreground android:drawable="@drawable/ic_launcher_foreground_dev" />
|
|
||||||
</adaptive-icon>
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
@ -8,15 +8,7 @@ import javax.inject.Singleton
|
|||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
class AnalyticsHelper @Inject constructor() {
|
class AnalyticsHelper @Inject constructor() {
|
||||||
|
|
||||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
fun logEvent(name: String, vararg params: Pair<String, Any?>) = Unit
|
||||||
// do nothing
|
fun setCurrentScreen(activity: Activity, name: String?) = Unit
|
||||||
}
|
fun popCurrentScreen(name: String?) = Unit
|
||||||
|
|
||||||
fun setCurrentScreen(activity: Activity, name: String?) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
fun popCurrentScreen(name: String?) {
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
@ -3,26 +3,38 @@ package io.github.wulkanowy.utils
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import com.huawei.agconnect.crash.AGConnectCrash
|
||||||
import com.huawei.hms.analytics.HiAnalytics
|
import com.huawei.hms.analytics.HiAnalytics
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class AnalyticsHelper @Inject constructor(
|
class AnalyticsHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context,
|
||||||
|
preferencesRepository: PreferencesRepository,
|
||||||
|
appInfo: AppInfo,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val analytics by lazy { HiAnalytics.getInstance(context) }
|
private val analytics by lazy { HiAnalytics.getInstance(context) }
|
||||||
|
|
||||||
|
private val connectCrash by lazy { AGConnectCrash.getInstance() }
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!appInfo.isDebug) {
|
||||||
|
connectCrash.setUserId(preferencesRepository.installationId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
|
||||||
Bundle().apply {
|
Bundle().apply {
|
||||||
params.forEach {
|
params.forEach { (key, value) ->
|
||||||
if (it.second == null) return@forEach
|
if (value == null) return@forEach
|
||||||
when (it.second) {
|
when (value) {
|
||||||
is String, is String? -> putString(it.first, it.second as String)
|
is String -> putString(key, value)
|
||||||
is Int, is Int? -> putInt(it.first, it.second as Int)
|
is Int -> putInt(key, value)
|
||||||
is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
|
is Boolean -> putBoolean(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
analytics.onEvent(name, this)
|
analytics.onEvent(name, this)
|
||||||
|
@ -3,6 +3,7 @@ package io.github.wulkanowy.utils
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.huawei.agconnect.crash.AGConnectCrash
|
import com.huawei.agconnect.crash.AGConnectCrash
|
||||||
import fr.bipi.tressence.base.FormatterPriorityTree
|
import fr.bipi.tressence.base.FormatterPriorityTree
|
||||||
|
import fr.bipi.tressence.common.StackTraceRecorder
|
||||||
|
|
||||||
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
|
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
|
||||||
|
|
||||||
@ -22,16 +23,10 @@ class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR, ExceptionFilter)
|
|||||||
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
|
||||||
if (skipLog(priority, tag, message, t)) return
|
if (skipLog(priority, tag, message, t)) return
|
||||||
|
|
||||||
// Disabled due to a bug in the Huawei library
|
|
||||||
|
|
||||||
/*connectCrash.setCustomKey("priority", priority)
|
|
||||||
connectCrash.setCustomKey("tag", tag.orEmpty())
|
|
||||||
connectCrash.setCustomKey("message", message)
|
|
||||||
|
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
connectCrash.recordException(t)
|
connectCrash.recordException(t)
|
||||||
} else {
|
} else {
|
||||||
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
|
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class RemoteConfigHelper @Inject constructor() : BaseRemoteConfigHelper()
|
@ -8,7 +8,8 @@
|
|||||||
<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"/>
|
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
@ -36,13 +37,14 @@
|
|||||||
<application
|
<application
|
||||||
android:name=".WulkanowyApp"
|
android:name=".WulkanowyApp"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
|
||||||
android:supportsRtl="false"
|
android:supportsRtl="false"
|
||||||
android:theme="@style/WulkanowyTheme"
|
android:theme="@style/WulkanowyTheme"
|
||||||
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
|
tools:ignore="DataExtractionRules,UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.splash.SplashActivity"
|
android:name=".ui.modules.splash.SplashActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -70,7 +72,7 @@
|
|||||||
android:name=".ui.modules.message.send.SendMessageActivity"
|
android:name=".ui.modules.message.send.SendMessageActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:label="@string/send_message_title"
|
android:label="@string/send_message_title"
|
||||||
android:theme="@style/WulkanowyTheme.MessageSend"
|
android:theme="@style/WulkanowyTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustResize" />
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
android:name=".ui.modules.timetablewidget.TimetableWidgetConfigureActivity"
|
||||||
|
@ -34,11 +34,15 @@ class WulkanowyApp : Application(), Configuration.Provider {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var adsHelper: AdsHelper
|
lateinit var adsHelper: AdsHelper
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var remoteConfigHelper: RemoteConfigHelper
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
initializeAppLanguage()
|
initializeAppLanguage()
|
||||||
themeManager.applyDefaultTheme()
|
themeManager.applyDefaultTheme()
|
||||||
adsHelper.initialize()
|
adsHelper.initialize()
|
||||||
|
remoteConfigHelper.initialize()
|
||||||
initLogging()
|
initLogging()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
|
|||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
import io.github.wulkanowy.utils.AppInfo
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
import io.github.wulkanowy.utils.RemoteConfigHelper
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
@ -36,10 +36,11 @@ internal class DataModule {
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideSdk(chuckerInterceptor: ChuckerInterceptor) =
|
fun provideSdk(chuckerInterceptor: ChuckerInterceptor, remoteConfig: RemoteConfigHelper) =
|
||||||
Sdk().apply {
|
Sdk().apply {
|
||||||
androidVersion = android.os.Build.VERSION.RELEASE
|
androidVersion = android.os.Build.VERSION.RELEASE
|
||||||
buildTag = android.os.Build.MODEL
|
buildTag = android.os.Build.MODEL
|
||||||
|
userAgentTemplate = remoteConfig.userAgentTemplate
|
||||||
setSimpleHttpLogger { Timber.d(it) }
|
setSimpleHttpLogger { Timber.d(it) }
|
||||||
|
|
||||||
// for debug only
|
// for debug only
|
||||||
@ -79,7 +80,6 @@ internal class DataModule {
|
|||||||
.readTimeout(30, TimeUnit.SECONDS)
|
.readTimeout(30, TimeUnit.SECONDS)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Provides
|
@Provides
|
||||||
fun provideRetrofit(
|
fun provideRetrofit(
|
||||||
|
@ -49,8 +49,8 @@ fun <T, U> Resource<T>.mapData(block: (T) -> U) = when (this) {
|
|||||||
|
|
||||||
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
fun <T> Flow<Resource<T>>.logResourceStatus(name: String, showData: Boolean = false) = onEach {
|
||||||
val description = when (it) {
|
val description = when (it) {
|
||||||
is Resource.Loading -> "started"
|
|
||||||
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
is Resource.Intermediate -> "intermediate data received" + if (showData) " (data: `${it.data}`)" else ""
|
||||||
|
is Resource.Loading -> "started"
|
||||||
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
is Resource.Success -> "success" + if (showData) " (data: `${it.data}`)" else ""
|
||||||
is Resource.Error -> "exception occurred: ${it.error}"
|
is Resource.Error -> "exception occurred: ${it.error}"
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,8 @@ import javax.inject.Singleton
|
|||||||
AutoMigration(from = 44, to = 45),
|
AutoMigration(from = 44, to = 45),
|
||||||
AutoMigration(from = 46, to = 47),
|
AutoMigration(from = 46, to = 47),
|
||||||
AutoMigration(from = 47, to = 48),
|
AutoMigration(from = 47, to = 48),
|
||||||
|
AutoMigration(from = 51, to = 52),
|
||||||
|
AutoMigration(from = 54, to = 55, spec = Migration55::class),
|
||||||
],
|
],
|
||||||
version = AppDatabase.VERSION_SCHEMA,
|
version = AppDatabase.VERSION_SCHEMA,
|
||||||
exportSchema = true
|
exportSchema = true
|
||||||
@ -55,7 +57,7 @@ import javax.inject.Singleton
|
|||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val VERSION_SCHEMA = 51
|
const val VERSION_SCHEMA = 55
|
||||||
|
|
||||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||||
Migration2(),
|
Migration2(),
|
||||||
@ -105,6 +107,8 @@ abstract class AppDatabase : RoomDatabase() {
|
|||||||
Migration49(),
|
Migration49(),
|
||||||
Migration50(),
|
Migration50(),
|
||||||
Migration51(),
|
Migration51(),
|
||||||
|
Migration53(),
|
||||||
|
Migration54(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
|
@ -3,12 +3,16 @@ package io.github.wulkanowy.data.db.dao
|
|||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Dao
|
@Dao
|
||||||
interface MailboxDao : BaseDao<Mailbox> {
|
interface MailboxDao : BaseDao<Mailbox> {
|
||||||
|
|
||||||
@Query("SELECT * FROM Mailboxes WHERE userLoginId = :userLoginId ")
|
@Query("SELECT * FROM Mailboxes WHERE email = :email")
|
||||||
suspend fun loadAll(userLoginId: Int): List<Mailbox>
|
suspend fun loadAll(email: String): List<Mailbox>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Mailboxes WHERE email = :email AND symbol = :symbol AND schoolId = :schoolId")
|
||||||
|
fun loadAll(email: String, symbol: String, schoolId: String): Flow<List<Mailbox>>
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,7 @@ interface MessagesDao : BaseDao<Message> {
|
|||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE mailbox_key = :mailboxKey AND folder_id = :folder ORDER BY date DESC")
|
||||||
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
fun loadAll(mailboxKey: String, folder: Int): Flow<List<Message>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Messages WHERE email = :email AND folder_id = :folder ORDER BY date DESC")
|
||||||
|
fun loadAll(folder: Int, email: String): Flow<List<Message>>
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.db.dao
|
package io.github.wulkanowy.data.db.dao
|
||||||
|
|
||||||
import androidx.room.*
|
import androidx.room.*
|
||||||
import androidx.room.OnConflictStrategy.ABORT
|
|
||||||
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
|
||||||
@ -11,7 +10,7 @@ import javax.inject.Singleton
|
|||||||
@Dao
|
@Dao
|
||||||
abstract class StudentDao {
|
abstract class StudentDao {
|
||||||
|
|
||||||
@Insert(onConflict = ABORT)
|
@Insert(onConflict = OnConflictStrategy.ABORT)
|
||||||
abstract suspend fun insertAll(student: List<Student>): List<Long>
|
abstract suspend fun insertAll(student: List<Student>): List<Long>
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
|
@ -13,4 +13,7 @@ interface TimetableDao : BaseDao<Timetable> {
|
|||||||
|
|
||||||
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
|
||||||
|
fun load(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): List<Timetable>
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ data class Exam(
|
|||||||
|
|
||||||
val subject: String,
|
val subject: String,
|
||||||
|
|
||||||
|
@Deprecated("not available anymore")
|
||||||
val group: String,
|
val group: String,
|
||||||
|
|
||||||
val type: String,
|
val type: String,
|
||||||
|
@ -1,20 +1,27 @@
|
|||||||
package io.github.wulkanowy.data.db.entities
|
package io.github.wulkanowy.data.db.entities
|
||||||
|
|
||||||
|
import android.os.Parcelable
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
@Entity(tableName = "Mailboxes")
|
@Entity(tableName = "Mailboxes")
|
||||||
data class Mailbox(
|
data class Mailbox(
|
||||||
|
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
val globalKey: String,
|
val globalKey: String,
|
||||||
|
|
||||||
|
val email: String,
|
||||||
|
val symbol: String,
|
||||||
|
val schoolId: String,
|
||||||
|
|
||||||
val fullName: String,
|
val fullName: String,
|
||||||
val userName: String,
|
val userName: String,
|
||||||
val userLoginId: Int,
|
|
||||||
val studentName: String,
|
val studentName: String,
|
||||||
val schoolNameShort: String,
|
val schoolNameShort: String,
|
||||||
val type: MailboxType,
|
val type: MailboxType,
|
||||||
)
|
) : java.io.Serializable, Parcelable
|
||||||
|
|
||||||
enum class MailboxType {
|
enum class MailboxType {
|
||||||
STUDENT,
|
STUDENT,
|
||||||
|
@ -9,6 +9,9 @@ import java.time.Instant
|
|||||||
@Entity(tableName = "Messages")
|
@Entity(tableName = "Messages")
|
||||||
data class Message(
|
data class Message(
|
||||||
|
|
||||||
|
@ColumnInfo(name = "email")
|
||||||
|
val email: String,
|
||||||
|
|
||||||
@ColumnInfo(name = "message_global_key")
|
@ColumnInfo(name = "message_global_key")
|
||||||
val messageGlobalKey: String,
|
val messageGlobalKey: String,
|
||||||
|
|
||||||
@ -29,6 +32,12 @@ data class Message(
|
|||||||
|
|
||||||
var unread: Boolean,
|
var unread: Boolean,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "read_by")
|
||||||
|
val readBy: Int?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "unread_by")
|
||||||
|
val unreadBy: Int?,
|
||||||
|
|
||||||
@ColumnInfo(name = "has_attachments")
|
@ColumnInfo(name = "has_attachments")
|
||||||
val hasAttachments: Boolean
|
val hasAttachments: Boolean
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
@ -2,16 +2,14 @@ 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 java.io.Serializable
|
import java.io.Serializable
|
||||||
|
|
||||||
@Entity(tableName = "MessageAttachments")
|
@Entity(
|
||||||
|
tableName = "MessageAttachments",
|
||||||
|
primaryKeys = ["message_global_key", "url", "filename"],
|
||||||
|
)
|
||||||
data class MessageAttachment(
|
data class MessageAttachment(
|
||||||
|
|
||||||
@PrimaryKey
|
|
||||||
@ColumnInfo(name = "real_id")
|
|
||||||
val realId: Int,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "message_global_key")
|
@ColumnInfo(name = "message_global_key")
|
||||||
val messageGlobalKey: String,
|
val messageGlobalKey: String,
|
||||||
|
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration53 : Migration(52, 53) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
createMailboxTable(database)
|
||||||
|
recreateMessagesTable(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMailboxTable(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("DROP TABLE IF EXISTS Mailboxes")
|
||||||
|
database.execSQL(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS `Mailboxes` (
|
||||||
|
`globalKey` TEXT NOT NULL,
|
||||||
|
`email` TEXT NOT NULL,
|
||||||
|
`symbol` TEXT NOT NULL,
|
||||||
|
`schoolId` TEXT NOT NULL,
|
||||||
|
`fullName` TEXT NOT NULL,
|
||||||
|
`userName` TEXT NOT NULL,
|
||||||
|
`studentName` TEXT NOT NULL,
|
||||||
|
`schoolNameShort` TEXT NOT NULL,
|
||||||
|
`type` TEXT NOT NULL,
|
||||||
|
PRIMARY KEY(`globalKey`)
|
||||||
|
)""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("DROP TABLE IF EXISTS Messages")
|
||||||
|
database.execSQL(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS `Messages` (
|
||||||
|
`email` TEXT NOT NULL,
|
||||||
|
`message_global_key` TEXT NOT NULL,
|
||||||
|
`mailbox_key` TEXT NOT NULL,
|
||||||
|
`message_id` INTEGER NOT NULL,
|
||||||
|
`correspondents` TEXT NOT NULL,
|
||||||
|
`subject` TEXT NOT NULL,
|
||||||
|
`date` INTEGER NOT NULL,
|
||||||
|
`folder_id` INTEGER NOT NULL,
|
||||||
|
`unread` INTEGER NOT NULL,
|
||||||
|
`read_by` INTEGER,
|
||||||
|
`unread_by` INTEGER,
|
||||||
|
`has_attachments` INTEGER NOT NULL,
|
||||||
|
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
`is_notified` INTEGER NOT NULL,
|
||||||
|
`content` TEXT NOT NULL,
|
||||||
|
`sender` TEXT,
|
||||||
|
`recipients` TEXT
|
||||||
|
)""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.migration.Migration
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
class Migration54 : Migration(53, 54) {
|
||||||
|
|
||||||
|
override fun migrate(database: SupportSQLiteDatabase) {
|
||||||
|
migrateResman(database)
|
||||||
|
removeTomaszowMazowieckiStudents(database)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun migrateResman(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("""
|
||||||
|
UPDATE Students SET
|
||||||
|
scrapper_base_url = 'https://vulcan.net.pl',
|
||||||
|
login_type = 'ADFSLightScoped',
|
||||||
|
symbol = 'rzeszowprojekt'
|
||||||
|
WHERE scrapper_base_url = 'https://resman.pl'
|
||||||
|
""".trimIndent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun removeTomaszowMazowieckiStudents(database: SupportSQLiteDatabase) {
|
||||||
|
database.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package io.github.wulkanowy.data.db.migrations
|
||||||
|
|
||||||
|
import androidx.room.DeleteColumn
|
||||||
|
import androidx.room.migration.AutoMigrationSpec
|
||||||
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
|
|
||||||
|
@DeleteColumn(
|
||||||
|
tableName = "MessageAttachments",
|
||||||
|
columnName = "real_id",
|
||||||
|
)
|
||||||
|
class Migration55 : AutoMigrationSpec {
|
||||||
|
|
||||||
|
override fun onPostMigrate(db: SupportSQLiteDatabase) {
|
||||||
|
db.execSQL("DELETE FROM Messages")
|
||||||
|
db.execSQL("DELETE FROM MessageAttachments")
|
||||||
|
}
|
||||||
|
}
|
@ -3,17 +3,22 @@ package io.github.wulkanowy.data.mappers
|
|||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
import io.github.wulkanowy.sdk.pojo.Attendance as SdkAttendance
|
||||||
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
|
import io.github.wulkanowy.sdk.pojo.AttendanceSummary as SdkAttendanceSummary
|
||||||
|
|
||||||
fun List<SdkAttendance>.mapToEntities(semester: Semester) = map {
|
fun List<SdkAttendance>.mapToEntities(semester: Semester, lessons: List<Timetable>) = map {
|
||||||
Attendance(
|
Attendance(
|
||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
date = it.date,
|
date = it.date,
|
||||||
timeId = it.timeId,
|
timeId = it.timeId,
|
||||||
number = it.number,
|
number = it.number,
|
||||||
subject = it.subject,
|
subject = it.subject.ifBlank {
|
||||||
|
lessons.find { lesson ->
|
||||||
|
lesson.date == it.date && lesson.number == it.number
|
||||||
|
}?.subject.orEmpty()
|
||||||
|
},
|
||||||
name = it.name,
|
name = it.name,
|
||||||
presence = it.presence,
|
presence = it.presence,
|
||||||
absence = it.absence,
|
absence = it.absence,
|
||||||
|
@ -10,9 +10,9 @@ 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.dateZoned.toInstant(),
|
date = it.date.toInstant(),
|
||||||
presentOnConference = it.presentOnConference,
|
presentOnConference = it.presentOnConference,
|
||||||
subject = it.subject,
|
subject = it.topic,
|
||||||
title = it.title
|
title = it.place,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ fun List<SdkExam>.mapToEntities(semester: Semester) = map {
|
|||||||
date = it.date,
|
date = it.date,
|
||||||
entryDate = it.entryDate,
|
entryDate = it.entryDate,
|
||||||
subject = it.subject,
|
subject = it.subject,
|
||||||
group = it.group,
|
group = "",
|
||||||
type = it.type,
|
type = it.type,
|
||||||
description = it.description,
|
description = it.description,
|
||||||
teacher = it.teacher,
|
teacher = it.teacher,
|
||||||
|
@ -10,9 +10,11 @@ fun List<SdkMailbox>.mapToEntities(student: Student) = map {
|
|||||||
globalKey = it.globalKey,
|
globalKey = it.globalKey,
|
||||||
fullName = it.fullName,
|
fullName = it.fullName,
|
||||||
userName = it.userName,
|
userName = it.userName,
|
||||||
userLoginId = student.userLoginId,
|
|
||||||
studentName = it.studentName,
|
studentName = it.studentName,
|
||||||
schoolNameShort = it.schoolNameShort,
|
schoolNameShort = it.schoolNameShort,
|
||||||
type = MailboxType.valueOf(it.type.name),
|
type = MailboxType.valueOf(it.type.name),
|
||||||
|
email = student.email,
|
||||||
|
symbol = student.symbol,
|
||||||
|
schoolId = student.schoolSymbol,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,36 @@ package io.github.wulkanowy.data.mappers
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.*
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
import io.github.wulkanowy.sdk.pojo.MailboxType
|
import io.github.wulkanowy.sdk.pojo.MailboxType
|
||||||
|
import timber.log.Timber
|
||||||
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
|
||||||
|
|
||||||
fun List<SdkMessage>.mapToEntities(mailbox: Mailbox) = map {
|
fun List<SdkMessage>.mapToEntities(
|
||||||
|
student: Student,
|
||||||
|
mailbox: Mailbox?,
|
||||||
|
allMailboxes: List<Mailbox>
|
||||||
|
): List<Message> = map {
|
||||||
Message(
|
Message(
|
||||||
messageGlobalKey = it.globalKey,
|
messageGlobalKey = it.globalKey,
|
||||||
mailboxKey = mailbox.globalKey,
|
mailboxKey = mailbox?.globalKey ?: allMailboxes.find { box ->
|
||||||
|
box.fullName == it.mailbox
|
||||||
|
}?.globalKey.let { mailboxKey ->
|
||||||
|
if (mailboxKey == null) {
|
||||||
|
Timber.e("Can't find ${it.mailbox} in $allMailboxes")
|
||||||
|
"unknown"
|
||||||
|
} else mailboxKey
|
||||||
|
},
|
||||||
|
email = student.email,
|
||||||
messageId = it.id,
|
messageId = it.id,
|
||||||
correspondents = it.correspondents,
|
correspondents = it.correspondents,
|
||||||
subject = it.subject.trim(),
|
subject = it.subject.trim(),
|
||||||
date = it.dateZoned.toInstant(),
|
date = it.date.toInstant(),
|
||||||
folderId = it.folderId,
|
folderId = it.folderId,
|
||||||
unread = it.unread,
|
unread = it.unread,
|
||||||
hasAttachments = it.hasAttachments
|
unreadBy = it.unreadBy,
|
||||||
|
readBy = it.readBy,
|
||||||
|
hasAttachments = it.hasAttachments,
|
||||||
).apply {
|
).apply {
|
||||||
content = it.content.orEmpty()
|
content = it.content.orEmpty()
|
||||||
}
|
}
|
||||||
@ -25,7 +40,6 @@ fun List<SdkMessage>.mapToEntities(mailbox: Mailbox) = map {
|
|||||||
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
fun List<SdkMessageAttachment>.mapToEntities(messageGlobalKey: String) = map {
|
||||||
MessageAttachment(
|
MessageAttachment(
|
||||||
messageGlobalKey = messageGlobalKey,
|
messageGlobalKey = messageGlobalKey,
|
||||||
realId = it.url.hashCode(),
|
|
||||||
url = it.url,
|
url = it.url,
|
||||||
filename = it.filename
|
filename = it.filename
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@ import io.github.wulkanowy.sdk.pojo.Token as SdkToken
|
|||||||
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
fun List<SdkDevice>.mapToEntities(student: Student) = map {
|
||||||
MobileDevice(
|
MobileDevice(
|
||||||
userLoginId = student.userLoginId,
|
userLoginId = student.userLoginId,
|
||||||
date = it.createDateZoned.toInstant(),
|
date = it.createDate.toInstant(),
|
||||||
deviceId = it.id,
|
deviceId = it.id,
|
||||||
name = it.name
|
name = it.name
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package io.github.wulkanowy.data.mappers
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
|
import io.github.wulkanowy.data.pojos.*
|
||||||
|
import java.time.Instant
|
||||||
|
import io.github.wulkanowy.sdk.pojo.RegisterStudent as SdkRegisterStudent
|
||||||
|
import io.github.wulkanowy.sdk.pojo.RegisterUser as SdkRegisterUser
|
||||||
|
|
||||||
|
fun SdkRegisterUser.mapToPojo(password: String?) = RegisterUser(
|
||||||
|
email = email,
|
||||||
|
login = login,
|
||||||
|
password = password,
|
||||||
|
scrapperBaseUrl = scrapperBaseUrl,
|
||||||
|
loginMode = loginMode,
|
||||||
|
loginType = loginType,
|
||||||
|
symbols = symbols.map { registerSymbol ->
|
||||||
|
RegisterSymbol(
|
||||||
|
symbol = registerSymbol.symbol,
|
||||||
|
error = registerSymbol.error,
|
||||||
|
hebeBaseUrl = registerSymbol.hebeBaseUrl,
|
||||||
|
keyId = registerSymbol.keyId,
|
||||||
|
privatePem = registerSymbol.privatePem,
|
||||||
|
userName = registerSymbol.userName,
|
||||||
|
schools = registerSymbol.schools.map {
|
||||||
|
RegisterUnit(
|
||||||
|
userLoginId = it.userLoginId,
|
||||||
|
schoolId = it.schoolId,
|
||||||
|
schoolName = it.schoolName,
|
||||||
|
schoolShortName = it.schoolShortName,
|
||||||
|
parentIds = it.parentIds,
|
||||||
|
studentIds = it.studentIds,
|
||||||
|
employeeIds = it.employeeIds,
|
||||||
|
error = it.error,
|
||||||
|
students = it.subjects
|
||||||
|
.filterIsInstance<SdkRegisterStudent>()
|
||||||
|
.map { registerSubject ->
|
||||||
|
RegisterStudent(
|
||||||
|
studentId = registerSubject.studentId,
|
||||||
|
studentName = registerSubject.studentName,
|
||||||
|
studentSecondName = registerSubject.studentSecondName,
|
||||||
|
studentSurname = registerSubject.studentSurname,
|
||||||
|
className = registerSubject.className,
|
||||||
|
classId = registerSubject.classId,
|
||||||
|
isParent = registerSubject.isParent,
|
||||||
|
semesters = registerSubject.semesters
|
||||||
|
.mapToEntities(registerSubject.studentId),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
fun RegisterStudent.mapToStudentWithSemesters(
|
||||||
|
user: RegisterUser,
|
||||||
|
symbol: RegisterSymbol,
|
||||||
|
unit: RegisterUnit,
|
||||||
|
colors: List<Long>,
|
||||||
|
): StudentWithSemesters = StudentWithSemesters(
|
||||||
|
semesters = semesters,
|
||||||
|
student = Student(
|
||||||
|
email = user.login, // for compatibility
|
||||||
|
userName = symbol.userName,
|
||||||
|
userLoginId = unit.userLoginId,
|
||||||
|
isParent = isParent,
|
||||||
|
className = className,
|
||||||
|
classId = classId,
|
||||||
|
studentId = studentId,
|
||||||
|
symbol = symbol.symbol,
|
||||||
|
loginType = user.loginType?.name.orEmpty(),
|
||||||
|
schoolName = unit.schoolName,
|
||||||
|
schoolShortName = unit.schoolShortName,
|
||||||
|
schoolSymbol = unit.schoolId,
|
||||||
|
studentName = "$studentName $studentSurname",
|
||||||
|
loginMode = user.loginMode.name,
|
||||||
|
scrapperBaseUrl = user.scrapperBaseUrl.orEmpty(),
|
||||||
|
mobileBaseUrl = symbol.hebeBaseUrl.orEmpty(),
|
||||||
|
certificateKey = symbol.keyId.orEmpty(),
|
||||||
|
privateKey = symbol.privatePem.orEmpty(),
|
||||||
|
password = user.password.orEmpty(),
|
||||||
|
isCurrent = false,
|
||||||
|
registrationDate = Instant.now(),
|
||||||
|
).apply {
|
||||||
|
avatarColor = colors.random()
|
||||||
|
},
|
||||||
|
)
|
@ -1,37 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.mappers
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
|
||||||
import java.time.Instant
|
|
||||||
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
|
|
||||||
|
|
||||||
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
|
|
||||||
StudentWithSemesters(
|
|
||||||
student = Student(
|
|
||||||
email = it.email,
|
|
||||||
password = password,
|
|
||||||
isParent = it.isParent,
|
|
||||||
symbol = it.symbol,
|
|
||||||
studentId = it.studentId,
|
|
||||||
userLoginId = it.userLoginId,
|
|
||||||
userName = it.userName,
|
|
||||||
studentName = it.studentName + " " + it.studentSurname,
|
|
||||||
schoolSymbol = it.schoolSymbol,
|
|
||||||
schoolShortName = it.schoolShortName,
|
|
||||||
schoolName = it.schoolName,
|
|
||||||
className = it.className,
|
|
||||||
classId = it.classId,
|
|
||||||
scrapperBaseUrl = it.scrapperBaseUrl,
|
|
||||||
loginType = it.loginType.name,
|
|
||||||
isCurrent = false,
|
|
||||||
registrationDate = Instant.now(),
|
|
||||||
mobileBaseUrl = it.mobileBaseUrl,
|
|
||||||
privateKey = it.privateKey,
|
|
||||||
certificateKey = it.certificateKey,
|
|
||||||
loginMode = it.loginMode.name,
|
|
||||||
).apply {
|
|
||||||
avatarColor = colors.random()
|
|
||||||
},
|
|
||||||
semesters = it.semesters.mapToEntities(it.studentId)
|
|
||||||
)
|
|
||||||
}
|
|
@ -5,10 +5,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
|
|||||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||||
import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull
|
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetableFull
|
||||||
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
|
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
|
||||||
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
|
import io.github.wulkanowy.sdk.pojo.Lesson as SdkLesson
|
||||||
import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional
|
import io.github.wulkanowy.sdk.pojo.LessonAdditional as SdkTimetableAdditional
|
||||||
|
|
||||||
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
||||||
lessons = lessons.mapToEntities(semester),
|
lessons = lessons.mapToEntities(semester),
|
||||||
@ -16,13 +16,13 @@ fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
|||||||
headers = headers.mapToEntities(semester)
|
headers = headers.mapToEntities(semester)
|
||||||
)
|
)
|
||||||
|
|
||||||
fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
|
fun List<SdkLesson>.mapToEntities(semester: Semester) = map {
|
||||||
Timetable(
|
Timetable(
|
||||||
studentId = semester.studentId,
|
studentId = semester.studentId,
|
||||||
diaryId = semester.diaryId,
|
diaryId = semester.diaryId,
|
||||||
number = it.number,
|
number = it.number,
|
||||||
start = it.startZoned.toInstant(),
|
start = it.start.toInstant(),
|
||||||
end = it.endZoned.toInstant(),
|
end = it.end.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.startZoned.toInstant(),
|
start = it.start.toInstant(),
|
||||||
end = it.endZoned.toInstant(),
|
end = it.end.toInstant(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package io.github.wulkanowy.data.pojos
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.Scrapper
|
||||||
|
|
||||||
|
data class RegisterUser(
|
||||||
|
val email: String,
|
||||||
|
val password: String?,
|
||||||
|
val login: String, // may be the same as email
|
||||||
|
val scrapperBaseUrl: String?,
|
||||||
|
val loginType: Scrapper.LoginType?,
|
||||||
|
val loginMode: Sdk.Mode,
|
||||||
|
val symbols: List<RegisterSymbol>,
|
||||||
|
) : java.io.Serializable
|
||||||
|
|
||||||
|
data class RegisterSymbol(
|
||||||
|
val symbol: String,
|
||||||
|
val error: Throwable?,
|
||||||
|
val hebeBaseUrl: String?,
|
||||||
|
val keyId: String?,
|
||||||
|
val privatePem: String?,
|
||||||
|
val userName: String,
|
||||||
|
val schools: List<RegisterUnit>,
|
||||||
|
) : java.io.Serializable
|
||||||
|
|
||||||
|
data class RegisterUnit(
|
||||||
|
val userLoginId: Int,
|
||||||
|
val schoolId: String,
|
||||||
|
val schoolName: String,
|
||||||
|
val schoolShortName: String,
|
||||||
|
val parentIds: List<Int>,
|
||||||
|
val studentIds: List<Int>,
|
||||||
|
val employeeIds: List<Int>,
|
||||||
|
val error: Throwable?,
|
||||||
|
val students: List<RegisterStudent>,
|
||||||
|
) : java.io.Serializable
|
||||||
|
|
||||||
|
data class RegisterStudent(
|
||||||
|
val studentId: Int,
|
||||||
|
val studentName: String,
|
||||||
|
val studentSecondName: String,
|
||||||
|
val studentSurname: String,
|
||||||
|
val className: String,
|
||||||
|
val classId: Int,
|
||||||
|
val isParent: Boolean,
|
||||||
|
val semesters: List<Semester>,
|
||||||
|
) : java.io.Serializable
|
@ -19,7 +19,6 @@ class AppCreatorRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
suspend fun getAppCreators() = withContext(dispatchers.io) {
|
suspend fun getAppCreators() = withContext(dispatchers.io) {
|
||||||
val inputStream = context.assets.open("contributors.json").buffered()
|
val inputStream = context.assets.open("contributors.json").buffered()
|
||||||
json.decodeFromStream<List<Contributor>>(inputStream)
|
json.decodeFromStream<List<Contributor>>(inputStream)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
package io.github.wulkanowy.data.repositories
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
import io.github.wulkanowy.data.db.dao.AttendanceDao
|
||||||
|
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.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
|
||||||
@ -9,8 +10,10 @@ 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.*
|
import io.github.wulkanowy.utils.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
@ -20,6 +23,7 @@ import javax.inject.Singleton
|
|||||||
@Singleton
|
@Singleton
|
||||||
class AttendanceRepository @Inject constructor(
|
class AttendanceRepository @Inject constructor(
|
||||||
private val attendanceDb: AttendanceDao,
|
private val attendanceDb: AttendanceDao,
|
||||||
|
private val timetableDb: TimetableDao,
|
||||||
private val sdk: Sdk,
|
private val sdk: Sdk,
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
) {
|
) {
|
||||||
@ -48,10 +52,15 @@ 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 = {
|
||||||
|
val lessons = withContext(Dispatchers.IO) {
|
||||||
|
timetableDb.load(
|
||||||
|
semester.diaryId, semester.studentId, start.monday, end.sunday
|
||||||
|
)
|
||||||
|
}
|
||||||
sdk.init(student)
|
sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getAttendance(start.monday, end.sunday, semester.semesterId)
|
.getAttendance(start.monday, end.sunday)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester, lessons)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
attendanceDb.deleteAll(old uniqueSubtract new)
|
attendanceDb.deleteAll(old uniqueSubtract new)
|
||||||
|
@ -52,7 +52,7 @@ class ExamRepository @Inject constructor(
|
|||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getExams(start.startExamsDay, start.endExamsDay, semester.semesterId)
|
.getExams(start.startExamsDay, start.endExamsDay)
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package io.github.wulkanowy.data.repositories
|
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.dao.MailboxDao
|
|
||||||
import io.github.wulkanowy.data.db.entities.Mailbox
|
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
|
||||||
import io.github.wulkanowy.utils.getRefreshKey
|
|
||||||
import io.github.wulkanowy.utils.init
|
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Singleton
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
class MailboxRepository @Inject constructor(
|
|
||||||
private val mailboxDao: MailboxDao,
|
|
||||||
private val sdk: Sdk,
|
|
||||||
private val refreshHelper: AutoRefreshHelper,
|
|
||||||
) {
|
|
||||||
private val cacheKey = "mailboxes"
|
|
||||||
|
|
||||||
suspend fun refreshMailboxes(student: Student) {
|
|
||||||
val new = sdk.init(student).getMailboxes().mapToEntities(student)
|
|
||||||
val old = mailboxDao.loadAll(student.userLoginId)
|
|
||||||
|
|
||||||
mailboxDao.deleteAll(old uniqueSubtract new)
|
|
||||||
mailboxDao.insertAll(new uniqueSubtract old)
|
|
||||||
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getMailbox(student: Student): Mailbox {
|
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
|
||||||
val mailboxes = mailboxDao.loadAll(student.userLoginId)
|
|
||||||
val mailbox = mailboxes.filterByStudent(student)
|
|
||||||
|
|
||||||
return if (isExpired || mailbox == null) {
|
|
||||||
refreshMailboxes(student)
|
|
||||||
val newMailbox = mailboxDao.loadAll(student.userLoginId).filterByStudent(student)
|
|
||||||
|
|
||||||
requireNotNull(newMailbox) {
|
|
||||||
"Mailbox for ${student.userName} - ${student.studentName} not found! Saved mailboxes: $mailboxes"
|
|
||||||
}
|
|
||||||
|
|
||||||
newMailbox
|
|
||||||
} else mailbox
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<Mailbox>.filterByStudent(student: Student): Mailbox? = find {
|
|
||||||
it.studentName.trim() == student.studentName.trim()
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,8 +3,9 @@ package io.github.wulkanowy.data.repositories
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
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.Resource
|
import io.github.wulkanowy.data.*
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
|
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
|
||||||
import io.github.wulkanowy.data.db.dao.MessagesDao
|
import io.github.wulkanowy.data.db.dao.MessagesDao
|
||||||
import io.github.wulkanowy.data.db.entities.*
|
import io.github.wulkanowy.data.db.entities.*
|
||||||
@ -13,8 +14,8 @@ import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
|||||||
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
|
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
|
||||||
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.domain.messages.GetMailboxByStudentUseCase
|
||||||
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
|
||||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||||
@ -40,16 +41,18 @@ class MessageRepository @Inject constructor(
|
|||||||
private val refreshHelper: AutoRefreshHelper,
|
private val refreshHelper: AutoRefreshHelper,
|
||||||
private val sharedPrefProvider: SharedPrefProvider,
|
private val sharedPrefProvider: SharedPrefProvider,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
|
private val mailboxDao: MailboxDao,
|
||||||
|
private val getMailboxByStudentUseCase: GetMailboxByStudentUseCase,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val saveFetchResultMutex = Mutex()
|
private val saveFetchResultMutex = Mutex()
|
||||||
|
|
||||||
private val cacheKey = "message"
|
private val messagesCacheKey = "message"
|
||||||
|
private val mailboxCacheKey = "mailboxes"
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
fun getMessages(
|
fun getMessages(
|
||||||
student: Student,
|
student: Student,
|
||||||
mailbox: Mailbox,
|
mailbox: Mailbox?,
|
||||||
folder: MessageFolder,
|
folder: MessageFolder,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
notify: Boolean = false,
|
notify: Boolean = false,
|
||||||
@ -58,13 +61,20 @@ class MessageRepository @Inject constructor(
|
|||||||
isResultEmpty = { it.isEmpty() },
|
isResultEmpty = { it.isEmpty() },
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
key = getRefreshKey(cacheKey, student, folder)
|
key = getRefreshKey(messagesCacheKey, mailbox, folder)
|
||||||
)
|
)
|
||||||
it.isEmpty() || forceRefresh || isExpired
|
it.isEmpty() || forceRefresh || isExpired
|
||||||
},
|
},
|
||||||
query = { messagesDb.loadAll(mailbox.globalKey, folder.id) },
|
query = {
|
||||||
|
if (mailbox == null) {
|
||||||
|
messagesDb.loadAll(folder.id, student.email)
|
||||||
|
} else messagesDb.loadAll(mailbox.globalKey, folder.id)
|
||||||
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getMessages(Folder.valueOf(folder.name)).mapToEntities(mailbox)
|
sdk.init(student).getMessages(
|
||||||
|
folder = Folder.valueOf(folder.name),
|
||||||
|
mailboxKey = mailbox?.globalKey,
|
||||||
|
).mapToEntities(student, mailbox, mailboxDao.loadAll(student.email))
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
messagesDb.deleteAll(old uniqueSubtract new)
|
messagesDb.deleteAll(old uniqueSubtract new)
|
||||||
@ -72,7 +82,9 @@ class MessageRepository @Inject constructor(
|
|||||||
it.isNotified = !notify
|
it.isNotified = !notify
|
||||||
})
|
})
|
||||||
|
|
||||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
|
refreshHelper.updateLastRefreshTimestamp(
|
||||||
|
getRefreshKey(messagesCacheKey, mailbox, folder)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -87,9 +99,14 @@ class MessageRepository @Inject constructor(
|
|||||||
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
|
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
|
||||||
it.message.unread || it.message.content.isBlank()
|
it.message.unread || it.message.content.isBlank()
|
||||||
},
|
},
|
||||||
query = { messagesDb.loadMessageWithAttachment(message.messageGlobalKey) },
|
query = {
|
||||||
|
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
|
||||||
|
},
|
||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student).getMessageDetails(it!!.message.messageGlobalKey)
|
sdk.init(student).getMessageDetails(
|
||||||
|
messageKey = it!!.message.messageGlobalKey,
|
||||||
|
markAsRead = message.unread && markAsRead,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
checkNotNull(old) { "Fetched message no longer exist!" }
|
checkNotNull(old) { "Fetched message no longer exist!" }
|
||||||
@ -98,7 +115,7 @@ class MessageRepository @Inject constructor(
|
|||||||
id = message.id
|
id = message.id
|
||||||
unread = !markAsRead
|
unread = !markAsRead
|
||||||
sender = new.sender
|
sender = new.sender
|
||||||
recipients = new.recipients.firstOrNull() ?: "Wielu adresoatów"
|
recipients = new.recipients.singleOrNull() ?: "Wielu adresatów"
|
||||||
content = content.ifBlank { new.content }
|
content = content.ifBlank { new.content }
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -110,8 +127,10 @@ class MessageRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getMessagesFromDatabase(mailbox: Mailbox): Flow<List<Message>> {
|
fun getMessagesFromDatabase(student: Student, mailbox: Mailbox?): Flow<List<Message>> {
|
||||||
return messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
|
return if (mailbox == null) {
|
||||||
|
messagesDb.loadAll(RECEIVED.id, student.email)
|
||||||
|
} else messagesDb.loadAll(mailbox.globalKey, RECEIVED.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateMessages(messages: List<Message>) {
|
suspend fun updateMessages(messages: List<Message>) {
|
||||||
@ -133,7 +152,7 @@ class MessageRepository @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteMessages(student: Student, mailbox: Mailbox, messages: List<Message>) {
|
suspend fun deleteMessages(student: Student, mailbox: Mailbox?, messages: List<Message>) {
|
||||||
val firstMessage = messages.first()
|
val firstMessage = messages.first()
|
||||||
sdk.init(student).deleteMessages(
|
sdk.init(student).deleteMessages(
|
||||||
messages = messages.map { it.messageGlobalKey },
|
messages = messages.map { it.messageGlobalKey },
|
||||||
@ -162,10 +181,44 @@ class MessageRepository @Inject constructor(
|
|||||||
).first()
|
).first()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun deleteMessage(student: Student, mailbox: Mailbox, message: Message) {
|
suspend fun deleteMessage(student: Student, mailbox: Mailbox?, message: Message) {
|
||||||
deleteMessages(student, mailbox, listOf(message))
|
deleteMessages(student, mailbox, listOf(message))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getMailboxes(student: Student, forceRefresh: Boolean) = networkBoundResource(
|
||||||
|
mutex = saveFetchResultMutex,
|
||||||
|
isResultEmpty = { it.isEmpty() },
|
||||||
|
shouldFetch = {
|
||||||
|
val isExpired = refreshHelper.shouldBeRefreshed(
|
||||||
|
key = getRefreshKey(mailboxCacheKey, student),
|
||||||
|
)
|
||||||
|
it.isEmpty() || isExpired || forceRefresh
|
||||||
|
},
|
||||||
|
query = { mailboxDao.loadAll(student.email, student.symbol, student.schoolSymbol) },
|
||||||
|
fetch = {
|
||||||
|
sdk.init(student).getMailboxes().mapToEntities(student)
|
||||||
|
},
|
||||||
|
saveFetchResult = { old, new ->
|
||||||
|
mailboxDao.deleteAll(old uniqueSubtract new)
|
||||||
|
mailboxDao.insertAll(new uniqueSubtract old)
|
||||||
|
|
||||||
|
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(mailboxCacheKey, student))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
suspend fun getMailboxByStudent(student: Student): Mailbox? {
|
||||||
|
val mailbox = getMailboxByStudentUseCase(student)
|
||||||
|
|
||||||
|
return if (mailbox == null) {
|
||||||
|
getMailboxes(student, forceRefresh = true)
|
||||||
|
.onResourceError { throw it }
|
||||||
|
.onResourceSuccess { Timber.i("Found ${it.size} new mailboxes") }
|
||||||
|
.waitForResult()
|
||||||
|
|
||||||
|
getMailboxByStudentUseCase(student)
|
||||||
|
} else mailbox
|
||||||
|
}
|
||||||
|
|
||||||
var draftMessage: MessageDraft?
|
var draftMessage: MessageDraft?
|
||||||
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft))
|
get() = sharedPrefProvider.getString(context.getString(R.string.pref_key_message_draft))
|
||||||
?.let { json.decodeFromString(it) }
|
?.let { json.decodeFromString(it) }
|
||||||
|
@ -42,7 +42,7 @@ class NoteRepository @Inject constructor(
|
|||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getNotes(semester.semesterId)
|
.getNotes()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -2,25 +2,27 @@ package io.github.wulkanowy.data.repositories
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import androidx.annotation.StringRes
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
|
||||||
import com.fredporciuncula.flow.preferences.Preference
|
import com.fredporciuncula.flow.preferences.Preference
|
||||||
|
import com.fredporciuncula.flow.preferences.Serializer
|
||||||
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.enums.*
|
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 kotlinx.coroutines.ExperimentalCoroutinesApi
|
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
|
||||||
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.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class PreferencesRepository @Inject constructor(
|
class PreferencesRepository @Inject constructor(
|
||||||
@ApplicationContext val context: Context,
|
@ApplicationContext val context: Context,
|
||||||
@ -29,29 +31,35 @@ class PreferencesRepository @Inject constructor(
|
|||||||
private val json: Json,
|
private val json: Json,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val startMenuIndex: Int
|
|
||||||
get() = getString(R.string.pref_key_start_menu, R.string.pref_default_startup).toInt()
|
|
||||||
|
|
||||||
val isShowPresent: Boolean
|
val isShowPresent: Boolean
|
||||||
get() = getBoolean(
|
get() = getBoolean(
|
||||||
R.string.pref_key_attendance_present,
|
R.string.pref_key_attendance_present,
|
||||||
R.bool.pref_default_attendance_present
|
R.bool.pref_default_attendance_present
|
||||||
)
|
)
|
||||||
|
|
||||||
val gradeAverageMode: GradeAverageMode
|
private val gradeAverageModePref: Preference<GradeAverageMode>
|
||||||
get() = GradeAverageMode.getByValue(
|
get() = getObjectFlow(
|
||||||
getString(
|
R.string.pref_key_grade_average_mode,
|
||||||
R.string.pref_key_grade_average_mode,
|
R.string.pref_default_grade_average_mode,
|
||||||
R.string.pref_default_grade_average_mode
|
object : Serializer<GradeAverageMode> {
|
||||||
)
|
override fun serialize(value: GradeAverageMode) = value.value
|
||||||
|
override fun deserialize(serialized: String) =
|
||||||
|
GradeAverageMode.getByValue(serialized)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
val gradeAverageForceCalc: Boolean
|
val gradeAverageModeFlow: Flow<GradeAverageMode>
|
||||||
get() = getBoolean(
|
get() = gradeAverageModePref.asFlow()
|
||||||
R.string.pref_key_grade_average_force_calc,
|
|
||||||
R.bool.pref_default_grade_average_force_calc
|
private val gradeAverageForceCalcPref: Preference<Boolean>
|
||||||
|
get() = flowSharedPref.getBoolean(
|
||||||
|
context.getString(R.string.pref_key_grade_average_force_calc),
|
||||||
|
context.resources.getBoolean(R.bool.pref_default_grade_average_force_calc)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val gradeAverageForceCalcFlow: Flow<Boolean>
|
||||||
|
get() = gradeAverageForceCalcPref.asFlow()
|
||||||
|
|
||||||
val gradeExpandMode: GradeExpandMode
|
val gradeExpandMode: GradeExpandMode
|
||||||
get() = GradeExpandMode.getByValue(
|
get() = GradeExpandMode.getByValue(
|
||||||
getString(
|
getString(
|
||||||
@ -141,12 +149,24 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.string.pref_default_grade_modifier_plus
|
R.string.pref_default_grade_modifier_plus
|
||||||
).toDouble()
|
).toDouble()
|
||||||
|
|
||||||
|
val gradePlusModifierFlow: Flow<Double>
|
||||||
|
get() = getStringFlow(
|
||||||
|
R.string.pref_key_grade_modifier_plus,
|
||||||
|
R.string.pref_default_grade_modifier_plus
|
||||||
|
).asFlow().map { it.toDouble() }
|
||||||
|
|
||||||
val gradeMinusModifier: Double
|
val gradeMinusModifier: Double
|
||||||
get() = getString(
|
get() = getString(
|
||||||
R.string.pref_key_grade_modifier_minus,
|
R.string.pref_key_grade_modifier_minus,
|
||||||
R.string.pref_default_grade_modifier_minus
|
R.string.pref_default_grade_modifier_minus
|
||||||
).toDouble()
|
).toDouble()
|
||||||
|
|
||||||
|
val gradeMinusModifierFlow: Flow<Double>
|
||||||
|
get() = getStringFlow(
|
||||||
|
R.string.pref_key_grade_modifier_minus,
|
||||||
|
R.string.pref_default_grade_modifier_minus
|
||||||
|
).asFlow().map { it.toDouble() }
|
||||||
|
|
||||||
val fillMessageContent: Boolean
|
val fillMessageContent: Boolean
|
||||||
get() = getBoolean(
|
get() = getBoolean(
|
||||||
R.string.pref_key_fill_message_content,
|
R.string.pref_key_fill_message_content,
|
||||||
@ -181,24 +201,17 @@ class PreferencesRepository @Inject constructor(
|
|||||||
R.bool.pref_default_timetable_show_timers
|
R.bool.pref_default_timetable_show_timers
|
||||||
)
|
)
|
||||||
|
|
||||||
var isHomeworkFullscreen: Boolean
|
|
||||||
get() = getBoolean(
|
|
||||||
R.string.pref_key_homework_fullscreen,
|
|
||||||
R.bool.pref_default_homework_fullscreen
|
|
||||||
)
|
|
||||||
set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
|
|
||||||
|
|
||||||
val showSubjectsWithoutGrades: Boolean
|
val showSubjectsWithoutGrades: Boolean
|
||||||
get() = getBoolean(
|
get() = getBoolean(
|
||||||
R.string.pref_key_subjects_without_grades,
|
R.string.pref_key_subjects_without_grades,
|
||||||
R.bool.pref_default_subjects_without_grades
|
R.bool.pref_default_subjects_without_grades
|
||||||
)
|
)
|
||||||
|
|
||||||
val isOptionalArithmeticAverage: Boolean
|
val isOptionalArithmeticAverageFlow: Flow<Boolean>
|
||||||
get() = getBoolean(
|
get() = flowSharedPref.getBoolean(
|
||||||
R.string.pref_key_optional_arithmetic_average,
|
context.getString(R.string.pref_key_optional_arithmetic_average),
|
||||||
R.bool.pref_default_optional_arithmetic_average
|
context.resources.getBoolean(R.bool.pref_default_optional_arithmetic_average)
|
||||||
)
|
).asFlow()
|
||||||
|
|
||||||
var lasSyncDate: Instant?
|
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)
|
||||||
@ -316,11 +329,50 @@ class PreferencesRepository @Inject constructor(
|
|||||||
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
|
putBoolean(context.getString(R.string.pref_key_ads_enabled), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var appMenuItemOrder: List<AppMenuItem>
|
||||||
|
get() {
|
||||||
|
val value = sharedPref.getString(PREF_KEY_APP_MENU_ITEM_ORDER, null)
|
||||||
|
?: return AppMenuItem.defaultAppMenuItemList
|
||||||
|
|
||||||
|
return json.decodeFromString(value)
|
||||||
|
}
|
||||||
|
set(value) = sharedPref.edit {
|
||||||
|
putString(
|
||||||
|
PREF_KEY_APP_MENU_ITEM_ORDER,
|
||||||
|
json.encodeToString(value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var installationId: String
|
||||||
|
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
|
||||||
|
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (installationId.isEmpty()) {
|
||||||
|
installationId = UUID.randomUUID().toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
|
private fun getLong(id: Int, default: Int) = getLong(context.getString(id), default)
|
||||||
|
|
||||||
private fun getLong(id: String, default: Int) =
|
private fun getLong(id: String, default: Int) =
|
||||||
sharedPref.getLong(id, context.resources.getString(default).toLong())
|
sharedPref.getLong(id, context.resources.getString(default).toLong())
|
||||||
|
|
||||||
|
private fun getStringFlow(id: Int, default: Int) =
|
||||||
|
flowSharedPref.getString(context.getString(id), context.getString(default))
|
||||||
|
|
||||||
|
private fun <T : Any> getObjectFlow(
|
||||||
|
@StringRes id: Int,
|
||||||
|
@StringRes default: Int,
|
||||||
|
serializer: Serializer<T>
|
||||||
|
): Preference<T> = flowSharedPref.getObject(
|
||||||
|
key = context.getString(id),
|
||||||
|
serializer = serializer,
|
||||||
|
defaultValue = serializer.deserialize(
|
||||||
|
flowSharedPref.getString(context.getString(default)).get()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||||
|
|
||||||
private fun getString(id: String, default: Int) =
|
private fun getString(id: String, default: Int) =
|
||||||
@ -331,23 +383,15 @@ class PreferencesRepository @Inject constructor(
|
|||||||
private fun getBoolean(id: String, default: Int) =
|
private fun getBoolean(id: String, default: Int) =
|
||||||
sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
sharedPref.getBoolean(id, context.resources.getBoolean(default))
|
||||||
|
|
||||||
private fun getBoolean(id: Int, default: Boolean) =
|
|
||||||
sharedPref.getBoolean(context.getString(id), default)
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
private const val PREF_KEY_APP_MENU_ITEM_ORDER = "app_menu_item_order"
|
||||||
|
private const val PREF_KEY_INSTALLATION_ID = "installation_id"
|
||||||
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
|
private const val PREF_KEY_DASHBOARD_ITEMS_POSITION = "dashboard_items_position"
|
||||||
|
|
||||||
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
|
private const val PREF_KEY_IN_APP_REVIEW_COUNT = "in_app_review_count"
|
||||||
|
|
||||||
private const val PREF_KEY_IN_APP_REVIEW_DATE = "in_app_review_date"
|
private const val PREF_KEY_IN_APP_REVIEW_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_APP_SUPPORT_SHOWN = "app_support_shown"
|
private const val PREF_KEY_APP_SUPPORT_SHOWN = "app_support_shown"
|
||||||
|
|
||||||
private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled"
|
private const val PREF_KEY_PERSONALIZED_ADS_ENABLED = "personalized_ads_enabled"
|
||||||
|
|
||||||
private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
|
private const val PREF_KEY_ADMIN_DISMISSED_MESSAGE_IDS = "admin_message_dismissed_ids"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,11 @@ class RecipientRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun getRecipients(
|
suspend fun getRecipients(
|
||||||
student: Student,
|
student: Student,
|
||||||
mailbox: Mailbox,
|
mailbox: Mailbox?,
|
||||||
type: MailboxType
|
type: MailboxType,
|
||||||
): List<Recipient> {
|
): List<Recipient> {
|
||||||
|
mailbox ?: return emptyList()
|
||||||
|
|
||||||
val cached = recipientDb.loadAll(type, mailbox.globalKey)
|
val cached = recipientDb.loadAll(type, mailbox.globalKey)
|
||||||
|
|
||||||
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
val isExpired = refreshHelper.shouldBeRefreshed(getRefreshKey(cacheKey, student))
|
||||||
@ -47,11 +49,15 @@ class RecipientRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun getMessageSender(
|
suspend fun getMessageSender(
|
||||||
student: Student,
|
student: Student,
|
||||||
mailbox: Mailbox,
|
mailbox: Mailbox?,
|
||||||
message: Message
|
message: Message,
|
||||||
): List<Recipient> = sdk.init(student)
|
): List<Recipient> {
|
||||||
.getMessageReplayDetails(message.messageGlobalKey)
|
mailbox ?: return emptyList()
|
||||||
.sender
|
|
||||||
.let(::listOf)
|
return sdk.init(student)
|
||||||
.mapToEntities(mailbox.globalKey)
|
.getMessageReplayDetails(message.messageGlobalKey)
|
||||||
|
.sender
|
||||||
|
.let(::listOf)
|
||||||
|
.mapToEntities(mailbox.globalKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class SemesterRepository @Inject constructor(
|
|||||||
val isNoSemesters = semesters.isEmpty()
|
val isNoSemesters = semesters.isEmpty()
|
||||||
|
|
||||||
val isRefreshOnModeChangeRequired = when {
|
val isRefreshOnModeChangeRequired = when {
|
||||||
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> {
|
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> {
|
||||||
semesters.firstOrNull { it.isCurrent }?.let {
|
semesters.firstOrNull { it.isCurrent }?.let {
|
||||||
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
0 == it.diaryId && 0 == it.kindergartenDiaryId
|
||||||
} == true
|
} == true
|
||||||
|
@ -10,9 +10,9 @@ 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
|
||||||
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
import io.github.wulkanowy.data.exceptions.NoCurrentStudentException
|
||||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
import io.github.wulkanowy.data.mappers.mapToPojo
|
||||||
|
import io.github.wulkanowy.data.pojos.RegisterUser
|
||||||
import io.github.wulkanowy.sdk.Sdk
|
import io.github.wulkanowy.sdk.Sdk
|
||||||
import io.github.wulkanowy.utils.AppInfo
|
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import io.github.wulkanowy.utils.security.decrypt
|
import io.github.wulkanowy.utils.security.decrypt
|
||||||
import io.github.wulkanowy.utils.security.encrypt
|
import io.github.wulkanowy.utils.security.encrypt
|
||||||
@ -27,45 +27,51 @@ class StudentRepository @Inject constructor(
|
|||||||
private val studentDb: StudentDao,
|
private val studentDb: StudentDao,
|
||||||
private val semesterDb: SemesterDao,
|
private val semesterDb: SemesterDao,
|
||||||
private val sdk: Sdk,
|
private val sdk: Sdk,
|
||||||
private val appInfo: AppInfo,
|
|
||||||
private val appDatabase: AppDatabase
|
private val appDatabase: AppDatabase
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun isStudentSaved() = getSavedStudents(false).isNotEmpty()
|
|
||||||
|
|
||||||
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
|
suspend fun isCurrentStudentSet() = studentDb.loadCurrent()?.isCurrent ?: false
|
||||||
|
|
||||||
suspend fun getStudentsApi(
|
suspend fun getStudentsApi(
|
||||||
pin: String,
|
pin: String,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
token: String
|
token: String
|
||||||
): List<StudentWithSemesters> =
|
): RegisterUser = sdk
|
||||||
sdk.getStudentsFromMobileApi(token, pin, symbol, "")
|
.getStudentsFromHebe(token, pin, symbol, "")
|
||||||
.mapToEntities(colors = appInfo.defaultColorsForAvatar)
|
.mapToPojo(null)
|
||||||
|
|
||||||
suspend fun getStudentsScrapper(
|
suspend fun getStudentsScrapper(
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): List<StudentWithSemesters> =
|
): RegisterUser = sdk
|
||||||
sdk.getStudentsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
.mapToPojo(password)
|
||||||
|
|
||||||
|
suspend fun getUserSubjectsFromScrapper(
|
||||||
|
email: String,
|
||||||
|
password: String,
|
||||||
|
scrapperBaseUrl: String,
|
||||||
|
symbol: String
|
||||||
|
): RegisterUser = sdk
|
||||||
|
.getUserSubjectsFromScrapper(email, password, scrapperBaseUrl, symbol)
|
||||||
|
.mapToPojo(password)
|
||||||
|
|
||||||
suspend fun getStudentsHybrid(
|
suspend fun getStudentsHybrid(
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
scrapperBaseUrl: String,
|
scrapperBaseUrl: String,
|
||||||
symbol: String
|
symbol: String
|
||||||
): List<StudentWithSemesters> =
|
): RegisterUser = sdk
|
||||||
sdk.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
|
||||||
.mapToEntities(password, appInfo.defaultColorsForAvatar)
|
.mapToPojo(password)
|
||||||
|
|
||||||
suspend fun getSavedStudents(decryptPass: Boolean = true) =
|
suspend fun getSavedStudents(decryptPass: Boolean = true) =
|
||||||
studentDb.loadStudentsWithSemesters()
|
studentDb.loadStudentsWithSemesters()
|
||||||
.map {
|
.map {
|
||||||
it.apply {
|
it.apply {
|
||||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||||
student.password = withContext(dispatchers.io) {
|
student.password = withContext(dispatchers.io) {
|
||||||
decrypt(student.password)
|
decrypt(student.password)
|
||||||
}
|
}
|
||||||
@ -75,7 +81,7 @@ class StudentRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
|
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
|
||||||
studentDb.loadStudentWithSemestersById(id)?.apply {
|
studentDb.loadStudentWithSemestersById(id)?.apply {
|
||||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||||
student.password = withContext(dispatchers.io) {
|
student.password = withContext(dispatchers.io) {
|
||||||
decrypt(student.password)
|
decrypt(student.password)
|
||||||
}
|
}
|
||||||
@ -85,7 +91,7 @@ class StudentRepository @Inject constructor(
|
|||||||
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()
|
||||||
|
|
||||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||||
student.password = withContext(dispatchers.io) {
|
student.password = withContext(dispatchers.io) {
|
||||||
decrypt(student.password)
|
decrypt(student.password)
|
||||||
}
|
}
|
||||||
@ -96,7 +102,7 @@ class StudentRepository @Inject constructor(
|
|||||||
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
suspend fun getCurrentStudent(decryptPass: Boolean = true): Student {
|
||||||
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
val student = studentDb.loadCurrent() ?: throw NoCurrentStudentException()
|
||||||
|
|
||||||
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
|
||||||
student.password = withContext(dispatchers.io) {
|
student.password = withContext(dispatchers.io) {
|
||||||
decrypt(student.password)
|
decrypt(student.password)
|
||||||
}
|
}
|
||||||
@ -109,7 +115,7 @@ class StudentRepository @Inject constructor(
|
|||||||
val students = studentsWithSemesters.map { it.student }
|
val students = studentsWithSemesters.map { it.student }
|
||||||
.map {
|
.map {
|
||||||
it.apply {
|
it.apply {
|
||||||
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.API) {
|
if (Sdk.Mode.valueOf(it.loginMode) != Sdk.Mode.HEBE) {
|
||||||
password = withContext(dispatchers.io) {
|
password = withContext(dispatchers.io) {
|
||||||
encrypt(password, context)
|
encrypt(password, context)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class TeacherRepository @Inject constructor(
|
|||||||
fetch = {
|
fetch = {
|
||||||
sdk.init(student)
|
sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getTeachers(semester.semesterId)
|
.getTeachers()
|
||||||
.mapToEntities(semester)
|
.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
|
@ -13,6 +13,7 @@ import io.github.wulkanowy.utils.*
|
|||||||
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
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -65,7 +66,7 @@ class TimetableRepository @Inject constructor(
|
|||||||
fetch = {
|
fetch = {
|
||||||
val timetableFull = sdk.init(student)
|
val timetableFull = sdk.init(student)
|
||||||
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
.switchDiary(semester.diaryId, semester.kindergartenDiaryId, semester.schoolYear)
|
||||||
.getTimetableFull(start.monday, end.sunday)
|
.getTimetable(start.monday, end.sunday)
|
||||||
|
|
||||||
timetableFull.mapToEntities(semester)
|
timetableFull.mapToEntities(semester)
|
||||||
},
|
},
|
||||||
@ -164,6 +165,11 @@ class TimetableRepository @Inject constructor(
|
|||||||
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLastRefreshTimestamp(semester: Semester, start: LocalDate, end: LocalDate): Instant {
|
||||||
|
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
|
||||||
|
return refreshHelper.getLastRefreshTimestamp(refreshKey)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun saveAdditionalList(additionalList: List<TimetableAdditional>) =
|
suspend fun saveAdditionalList(additionalList: List<TimetableAdditional>) =
|
||||||
timetableAdditionalDb.insertAll(additionalList)
|
timetableAdditionalDb.insertAll(additionalList)
|
||||||
|
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package io.github.wulkanowy.domain.messages
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.dao.MailboxDao
|
||||||
|
import io.github.wulkanowy.data.db.entities.Mailbox
|
||||||
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GetMailboxByStudentUseCase @Inject constructor(
|
||||||
|
private val mailboxDao: MailboxDao,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend operator fun invoke(student: Student): Mailbox? {
|
||||||
|
return mailboxDao.loadAll(student.email)
|
||||||
|
.filterByStudent(student)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Mailbox>.filterByStudent(student: Student): Mailbox? {
|
||||||
|
val normalizedStudentName = student.studentName.normalizeStudentName()
|
||||||
|
|
||||||
|
return singleOrNull {
|
||||||
|
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||||
|
} ?: singleOrNull {
|
||||||
|
it.studentName.normalizeStudentName() == normalizedStudentName
|
||||||
|
&& it.schoolNameShort == student.schoolShortName
|
||||||
|
} ?: singleOrNull {
|
||||||
|
it.studentName.getFirstAndLastPart() == normalizedStudentName.getFirstAndLastPart()
|
||||||
|
} ?: singleOrNull {
|
||||||
|
it.studentName.getReversedName() == normalizedStudentName
|
||||||
|
} ?: singleOrNull {
|
||||||
|
it.studentName.getUnauthorizedVersion() == normalizedStudentName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.normalizeStudentName(): String {
|
||||||
|
return trim().split(" ")
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.joinToString(" ") { part ->
|
||||||
|
part.lowercase().replaceFirstChar { it.uppercase() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.getFirstAndLastPart(): String {
|
||||||
|
val parts = normalizeStudentName().split(" ")
|
||||||
|
|
||||||
|
val endParts = parts.filterIndexed { i, _ ->
|
||||||
|
i == 0 || parts.size - 1 == i
|
||||||
|
}
|
||||||
|
return endParts.joinToString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.getReversedName(): String {
|
||||||
|
val parts = normalizeStudentName().split(" ")
|
||||||
|
|
||||||
|
return parts
|
||||||
|
.asReversed()
|
||||||
|
.joinToString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.getUnauthorizedVersion(): String {
|
||||||
|
return normalizeStudentName().split(" ")
|
||||||
|
.joinToString(" ") {
|
||||||
|
it.first() + "*".repeat(it.length - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,18 +4,12 @@ import android.os.Build.VERSION.SDK_INT
|
|||||||
import android.os.Build.VERSION_CODES.O
|
import android.os.Build.VERSION_CODES.O
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.lifecycle.asFlow
|
import androidx.lifecycle.asFlow
|
||||||
|
import androidx.work.*
|
||||||
import androidx.work.BackoffPolicy.EXPONENTIAL
|
import androidx.work.BackoffPolicy.EXPONENTIAL
|
||||||
import androidx.work.Constraints
|
|
||||||
import androidx.work.Data
|
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy.REPLACE
|
import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
|
||||||
import androidx.work.ExistingWorkPolicy
|
|
||||||
import androidx.work.NetworkType.CONNECTED
|
import androidx.work.NetworkType.CONNECTED
|
||||||
import androidx.work.NetworkType.UNMETERED
|
import androidx.work.NetworkType.UNMETERED
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
|
||||||
import androidx.work.PeriodicWorkRequestBuilder
|
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
import io.github.wulkanowy.data.db.SharedPrefProvider.Companion.APP_VERSION_CODE_KEY
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
@ -60,7 +54,7 @@ class SyncManager @Inject constructor(
|
|||||||
val serviceInterval = preferencesRepository.servicesInterval
|
val serviceInterval = preferencesRepository.servicesInterval
|
||||||
|
|
||||||
workManager.enqueueUniquePeriodicWork(
|
workManager.enqueueUniquePeriodicWork(
|
||||||
SyncWorker::class.java.simpleName, if (restart) REPLACE else KEEP,
|
SyncWorker::class.java.simpleName, if (restart) UPDATE else KEEP,
|
||||||
PeriodicWorkRequestBuilder<SyncWorker>(serviceInterval, MINUTES)
|
PeriodicWorkRequestBuilder<SyncWorker>(serviceInterval, MINUTES)
|
||||||
.setInitialDelay(10, MINUTES)
|
.setInitialDelay(10, MINUTES)
|
||||||
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
|
.setBackoffCriteria(EXPONENTIAL, 30, MINUTES)
|
||||||
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||||||
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.descriptionRes
|
import io.github.wulkanowy.utils.descriptionRes
|
||||||
import io.github.wulkanowy.utils.getPlural
|
import io.github.wulkanowy.utils.getPlural
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
@ -22,8 +21,9 @@ class NewAttendanceNotification @Inject constructor(
|
|||||||
suspend fun notify(items: List<Attendance>, student: Student) {
|
suspend fun notify(items: List<Attendance>, student: Student) {
|
||||||
val lines = items.filterNot { it.presence || it.name == "UNKNOWN" }
|
val lines = items.filterNot { it.presence || it.name == "UNKNOWN" }
|
||||||
.map {
|
.map {
|
||||||
|
val lesson = it.subject.ifBlank { "Lekcja ${it.number}" }
|
||||||
val description = context.getString(it.descriptionRes)
|
val description = context.getString(it.descriptionRes)
|
||||||
"${it.date.toFormattedString("dd.MM")} - ${it.subject}: $description"
|
"${it.date.toFormattedString("dd.MM")} - $lesson: $description"
|
||||||
}
|
}
|
||||||
.ifEmpty { return }
|
.ifEmpty { return }
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ import io.github.wulkanowy.data.db.entities.Student
|
|||||||
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 javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ 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.enums.MessageFolder.RECEIVED
|
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
|
||||||
import io.github.wulkanowy.data.repositories.MailboxRepository
|
|
||||||
import io.github.wulkanowy.data.repositories.MessageRepository
|
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||||
import io.github.wulkanowy.data.waitForResult
|
import io.github.wulkanowy.data.waitForResult
|
||||||
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
|
import io.github.wulkanowy.services.sync.notifications.NewMessageNotification
|
||||||
@ -12,12 +11,11 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class MessageWork @Inject constructor(
|
class MessageWork @Inject constructor(
|
||||||
private val messageRepository: MessageRepository,
|
private val messageRepository: MessageRepository,
|
||||||
private val mailboxRepository: MailboxRepository,
|
|
||||||
private val newMessageNotification: NewMessageNotification,
|
private val newMessageNotification: NewMessageNotification,
|
||||||
) : Work {
|
) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||||
val mailbox = mailboxRepository.getMailbox(student)
|
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||||
messageRepository.getMessages(
|
messageRepository.getMessages(
|
||||||
student = student,
|
student = student,
|
||||||
mailbox = mailbox,
|
mailbox = mailbox,
|
||||||
@ -26,7 +24,7 @@ class MessageWork @Inject constructor(
|
|||||||
notify = notify
|
notify = notify
|
||||||
).waitForResult()
|
).waitForResult()
|
||||||
|
|
||||||
messageRepository.getMessagesFromDatabase(mailbox).first()
|
messageRepository.getMessagesFromDatabase(student, mailbox).first()
|
||||||
.filter { !it.isNotified && it.unread }.let {
|
.filter { !it.isNotified && it.unread }.let {
|
||||||
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
|
if (it.isNotEmpty()) newMessageNotification.notify(it, student)
|
||||||
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
|
messageRepository.updateMessages(it.onEach { message -> message.isNotified = true })
|
||||||
|
@ -1,22 +1,23 @@
|
|||||||
package io.github.wulkanowy.services.sync.works
|
package io.github.wulkanowy.services.sync.works
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.dataOrNull
|
||||||
import io.github.wulkanowy.data.db.entities.MailboxType
|
import io.github.wulkanowy.data.db.entities.MailboxType
|
||||||
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.MailboxRepository
|
import io.github.wulkanowy.data.repositories.MessageRepository
|
||||||
import io.github.wulkanowy.data.repositories.RecipientRepository
|
import io.github.wulkanowy.data.repositories.RecipientRepository
|
||||||
|
import io.github.wulkanowy.data.toFirstResult
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class RecipientWork @Inject constructor(
|
class RecipientWork @Inject constructor(
|
||||||
private val mailboxRepository: MailboxRepository,
|
private val messageRepository: MessageRepository,
|
||||||
private val recipientRepository: RecipientRepository
|
private val recipientRepository: RecipientRepository
|
||||||
) : Work {
|
) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
override suspend fun doWork(student: Student, semester: Semester, notify: Boolean) {
|
||||||
mailboxRepository.refreshMailboxes(student)
|
val mailboxes = messageRepository.getMailboxes(student, forceRefresh = true).toFirstResult()
|
||||||
|
mailboxes.dataOrNull?.forEach {
|
||||||
val mailbox = mailboxRepository.getMailbox(student)
|
recipientRepository.refreshRecipients(student, it, MailboxType.EMPLOYEE)
|
||||||
|
}
|
||||||
recipientRepository.refreshRecipients(student, mailbox, MailboxType.EMPLOYEE)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import android.content.Intent
|
|||||||
import android.widget.RemoteViewsService
|
import android.widget.RemoteViewsService
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.data.db.SharedPrefProvider
|
import io.github.wulkanowy.data.db.SharedPrefProvider
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
|
||||||
import io.github.wulkanowy.data.repositories.SemesterRepository
|
import io.github.wulkanowy.data.repositories.SemesterRepository
|
||||||
import io.github.wulkanowy.data.repositories.StudentRepository
|
import io.github.wulkanowy.data.repositories.StudentRepository
|
||||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||||
@ -24,14 +23,13 @@ class TimetableWidgetService : RemoteViewsService() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var semesterRepo: SemesterRepository
|
lateinit var semesterRepo: SemesterRepository
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var prefRepository: PreferencesRepository
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var sharedPref: SharedPrefProvider
|
lateinit var sharedPref: SharedPrefProvider
|
||||||
|
|
||||||
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
|
||||||
Timber.d("TimetableWidgetFactory created")
|
Timber.d("TimetableWidgetFactory created")
|
||||||
return TimetableWidgetFactory(timetableRepo, studentRepo, semesterRepo, prefRepository, sharedPref, applicationContext, intent)
|
return TimetableWidgetFactory(
|
||||||
|
timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@ import android.app.ActivityManager
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
@ -30,6 +30,8 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
|
|
||||||
protected var messageContainer: View? = null
|
protected var messageContainer: View? = null
|
||||||
|
|
||||||
|
protected var messageAnchor: View? = null
|
||||||
|
|
||||||
abstract var presenter: T
|
abstract var presenter: T
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -48,6 +50,7 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
if (messageContainer != null) {
|
if (messageContainer != null) {
|
||||||
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
|
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
|
||||||
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
|
.setAction(R.string.all_details) { showErrorDetailsDialog(error) }
|
||||||
|
.apply { messageAnchor?.let { anchorView = it } }
|
||||||
.show()
|
.show()
|
||||||
} else showMessage(text)
|
} else showMessage(text)
|
||||||
}
|
}
|
||||||
@ -57,12 +60,15 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun showMessage(text: String) {
|
override fun showMessage(text: String) {
|
||||||
if (messageContainer != null) Snackbar.make(messageContainer!!, text, LENGTH_LONG).show()
|
if (messageContainer != null) {
|
||||||
else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
Snackbar.make(messageContainer!!, text, LENGTH_LONG)
|
||||||
|
.apply { messageAnchor?.let { anchorView = it } }
|
||||||
|
.show()
|
||||||
|
} else Toast.makeText(this, text, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showExpiredDialog() {
|
override fun showExpiredDialog() {
|
||||||
AlertDialog.Builder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.main_session_expired)
|
.setTitle(R.string.main_session_expired)
|
||||||
.setMessage(R.string.main_session_relogin)
|
.setMessage(R.string.main_session_relogin)
|
||||||
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() }
|
.setPositiveButton(R.string.main_log_in) { _, _ -> presenter.onExpiredLoginSelected() }
|
||||||
@ -74,6 +80,7 @@ abstract class BaseActivity<T : BasePresenter<out BaseView>, VB : ViewBinding> :
|
|||||||
messageContainer?.let {
|
messageContainer?.let {
|
||||||
Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG)
|
Snackbar.make(it, R.string.error_password_change_required, LENGTH_LONG)
|
||||||
.setAction(R.string.all_change) { openInternetBrowser(redirectUrl) }
|
.setAction(R.string.all_change) { openInternetBrowser(redirectUrl) }
|
||||||
|
.apply { messageAnchor?.let { anchorView = it } }
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.annotation.CallSuper
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.AnalyticsHelper
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -38,6 +44,19 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
|
|||||||
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
|
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
view.setBackgroundColor(SurfaceColors.SURFACE_3.getColor(requireContext()))
|
||||||
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
) = binding.root
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
|
analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
|
||||||
|
@ -4,27 +4,32 @@ import android.app.Dialog
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.View
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import android.widget.Toast.LENGTH_LONG
|
import android.widget.Toast.LENGTH_LONG
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.core.os.bundleOf
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.fragment.app.DialogFragment
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.databinding.DialogErrorBinding
|
import io.github.wulkanowy.databinding.DialogErrorBinding
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ErrorDialog : DialogFragment() {
|
class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var appInfo: AppInfo
|
lateinit var appInfo: AppInfo
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var preferencesRepository: PreferencesRepository
|
||||||
|
|
||||||
|
private lateinit var error: Throwable
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARGUMENT_KEY = "error"
|
private const val ARGUMENT_KEY = "error"
|
||||||
|
|
||||||
@ -33,32 +38,31 @@ class ErrorDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
val error = requireArguments().getSerializable(ARGUMENT_KEY) as Throwable
|
super.onCreate(savedInstanceState)
|
||||||
|
error = requireArguments().serializable(ARGUMENT_KEY)
|
||||||
val binding = DialogErrorBinding.inflate(LayoutInflater.from(context))
|
|
||||||
binding.bindErrorDetails(error)
|
|
||||||
|
|
||||||
return getAlertDialog(binding, error).apply {
|
|
||||||
enableReportButtonIfErrorIsReportable(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAlertDialog(binding: DialogErrorBinding, error: Throwable): AlertDialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
return MaterialAlertDialogBuilder(requireContext()).apply {
|
return MaterialAlertDialogBuilder(requireContext()).apply {
|
||||||
val errorStacktrace = error.stackTraceToString()
|
val errorStacktrace = error.stackTraceToString()
|
||||||
setTitle(R.string.all_details)
|
setTitle(R.string.all_details)
|
||||||
setView(binding.root)
|
setView(DialogErrorBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||||
setNeutralButton(R.string.about_feedback) { _, _ ->
|
setNeutralButton(R.string.about_feedback) { _, _ ->
|
||||||
openConfirmDialog { openEmailClient(errorStacktrace) }
|
openConfirmDialog { openEmailClient(errorStacktrace) }
|
||||||
}
|
}
|
||||||
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
|
setPositiveButton(android.R.string.copy) { _, _ -> copyErrorToClipboard(errorStacktrace) }
|
||||||
}.create()
|
}.create().apply {
|
||||||
|
setOnShowListener {
|
||||||
|
getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun DialogErrorBinding.bindErrorDetails(error: Throwable) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
return with(this) {
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
with(binding) {
|
||||||
errorDialogHumanizedMessage.text = resources.getErrorString(error)
|
errorDialogHumanizedMessage.text = resources.getErrorString(error)
|
||||||
errorDialogErrorMessage.text = error.localizedMessage
|
errorDialogErrorMessage.text = error.localizedMessage
|
||||||
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
|
errorDialogErrorMessage.isGone = error.localizedMessage.isNullOrBlank()
|
||||||
@ -67,12 +71,6 @@ class ErrorDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AlertDialog.enableReportButtonIfErrorIsReportable(error: Throwable) {
|
|
||||||
setOnShowListener {
|
|
||||||
getButton(AlertDialog.BUTTON_NEUTRAL).isEnabled = error.isShouldBeReported()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun copyErrorToClipboard(errorStacktrace: String) {
|
private fun copyErrorToClipboard(errorStacktrace: String) {
|
||||||
val clip = ClipData.newPlainText("Error details", errorStacktrace)
|
val clip = ClipData.newPlainText("Error details", errorStacktrace)
|
||||||
requireActivity().getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
|
requireActivity().getSystemService<ClipboardManager>()?.setPrimaryClip(clip)
|
||||||
@ -80,7 +78,7 @@ class ErrorDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openConfirmDialog(callback: () -> Unit) {
|
private fun openConfirmDialog(callback: () -> Unit) {
|
||||||
AlertDialog.Builder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.dialog_error_check_update)
|
.setTitle(R.string.dialog_error_check_update)
|
||||||
.setMessage(R.string.dialog_error_check_update_message)
|
.setMessage(R.string.dialog_error_check_update_message)
|
||||||
.setNeutralButton(R.string.about_feedback) { _, _ -> callback() }
|
.setNeutralButton(R.string.about_feedback) { _, _ -> callback() }
|
||||||
@ -99,7 +97,8 @@ class ErrorDialog : DialogFragment() {
|
|||||||
R.string.about_feedback_template,
|
R.string.about_feedback_template,
|
||||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||||
appInfo.systemVersion.toString(),
|
appInfo.systemVersion.toString(),
|
||||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||||
|
preferencesRepository.installationId,
|
||||||
) + "\n" + content,
|
) + "\n" + content,
|
||||||
onActivityNotFound = {
|
onActivityNotFound = {
|
||||||
requireContext().openInternetBrowser(
|
requireContext().openInternetBrowser(
|
||||||
@ -109,8 +108,4 @@ class ErrorDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showMessage(text: String) {
|
|
||||||
Toast.makeText(requireContext(), text, LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
package io.github.wulkanowy.ui.base
|
package io.github.wulkanowy.ui.base
|
||||||
|
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.GET_ACTIVITIES
|
import android.content.pm.PackageManager.GET_ACTIVITIES
|
||||||
|
import android.os.Build
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
import com.google.android.material.color.DynamicColors
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
|
|
||||||
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
|
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.enums.AppTheme
|
import io.github.wulkanowy.data.enums.AppTheme
|
||||||
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
import io.github.wulkanowy.ui.modules.login.LoginActivity
|
||||||
|
import io.github.wulkanowy.ui.modules.luckynumberwidget.LuckyNumberWidgetConfigureActivity
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
import io.github.wulkanowy.ui.modules.message.send.SendMessageActivity
|
import io.github.wulkanowy.ui.modules.timetablewidget.TimetableWidgetConfigureActivity
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -25,31 +27,40 @@ class ThemeManager @Inject constructor(private val preferencesRepository: Prefer
|
|||||||
when (activity) {
|
when (activity) {
|
||||||
is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
|
is MainActivity -> activity.setTheme(R.style.WulkanowyTheme_Black)
|
||||||
is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
|
is LoginActivity -> activity.setTheme(R.style.WulkanowyTheme_Login_Black)
|
||||||
is SendMessageActivity -> activity.setTheme(R.style.WulkanowyTheme_MessageSend_Black)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (activity is TimetableWidgetConfigureActivity || activity is LuckyNumberWidgetConfigureActivity) {
|
||||||
|
DynamicColors.applyToActivityIfAvailable(activity)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyDefaultTheme() {
|
fun applyDefaultTheme() {
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
when (preferencesRepository.appTheme) {
|
when (preferencesRepository.appTheme) {
|
||||||
AppTheme.LIGHT -> MODE_NIGHT_NO
|
AppTheme.LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||||
AppTheme.DARK, AppTheme.BLACK -> MODE_NIGHT_YES
|
AppTheme.DARK, AppTheme.BLACK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||||
AppTheme.SYSTEM -> MODE_NIGHT_FOLLOW_SYSTEM
|
AppTheme.SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isThemeApplicable(activity: AppCompatActivity) =
|
private fun isThemeApplicable(activity: AppCompatActivity): Boolean =
|
||||||
activity.packageManager
|
getPackageInfo(activity)
|
||||||
.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
|
||||||
.activities
|
.activities
|
||||||
.singleOrNull { it.name == activity::class.java.canonicalName }
|
.singleOrNull { it.name == activity::class.java.canonicalName }
|
||||||
?.theme
|
?.theme
|
||||||
.let {
|
.let {
|
||||||
it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
|
it == R.style.WulkanowyTheme_Black || it == R.style.WulkanowyTheme_NoActionBar
|
||||||
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
|
|| it == R.style.WulkanowyTheme_Login || it == R.style.WulkanowyTheme_Login_Black
|
||||||
|| it == R.style.WulkanowyTheme_MessageSend || it == R.style.WulkanowyTheme_MessageSend_Black
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun getPackageInfo(activity: AppCompatActivity): PackageInfo {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
activity.packageManager.getPackageInfo(
|
||||||
|
activity.packageName,
|
||||||
|
PackageManager.PackageInfoFlags.of(GET_ACTIVITIES.toLong())
|
||||||
|
)
|
||||||
|
} else activity.packageManager.getPackageInfo(activity.packageName, GET_ACTIVITIES)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,14 @@ import io.github.wulkanowy.ui.modules.exam.ExamFragment
|
|||||||
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
import io.github.wulkanowy.ui.modules.grade.GradeFragment
|
||||||
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
import io.github.wulkanowy.ui.modules.homework.HomeworkFragment
|
||||||
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
import io.github.wulkanowy.ui.modules.luckynumber.LuckyNumberFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.luckynumber.history.LuckyNumberHistoryFragment
|
||||||
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
import io.github.wulkanowy.ui.modules.message.MessageFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.mobiledevice.MobileDeviceFragment
|
||||||
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
import io.github.wulkanowy.ui.modules.more.MoreFragment
|
||||||
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
import io.github.wulkanowy.ui.modules.note.NoteFragment
|
||||||
import io.github.wulkanowy.ui.modules.schoolandteachers.school.SchoolFragment
|
import io.github.wulkanowy.ui.modules.schoolandteachers.SchoolAndTeachersFragment
|
||||||
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
import io.github.wulkanowy.ui.modules.schoolannouncement.SchoolAnnouncementFragment
|
||||||
|
import io.github.wulkanowy.ui.modules.settings.SettingsFragment
|
||||||
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@ -39,10 +42,13 @@ sealed class Destination {
|
|||||||
NOTE(Note),
|
NOTE(Note),
|
||||||
CONFERENCE(Conference),
|
CONFERENCE(Conference),
|
||||||
SCHOOL_ANNOUNCEMENT(SchoolAnnouncement),
|
SCHOOL_ANNOUNCEMENT(SchoolAnnouncement),
|
||||||
SCHOOL(School),
|
SCHOOL_AND_TEACHERS(SchoolAndTeachers),
|
||||||
LUCKY_NUMBER(More),
|
LUCKY_NUMBER(LuckyNumber),
|
||||||
|
LUCKY_NUMBER_HISTORY(LuckyNumberHistory),
|
||||||
MORE(More),
|
MORE(More),
|
||||||
MESSAGE(Message);
|
MESSAGE(Message),
|
||||||
|
MOBILE_DEVICE(MobileDevice),
|
||||||
|
SETTINGS(Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -103,9 +109,9 @@ sealed class Destination {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
object School : Destination() {
|
object SchoolAndTeachers : Destination() {
|
||||||
override val destinationType get() = Type.SCHOOL
|
override val destinationType get() = Type.SCHOOL_AND_TEACHERS
|
||||||
override val destinationFragment get() = SchoolFragment.newInstance()
|
override val destinationFragment get() = SchoolAndTeachersFragment.newInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@ -114,6 +120,12 @@ sealed class Destination {
|
|||||||
override val destinationFragment get() = LuckyNumberFragment.newInstance()
|
override val destinationFragment get() = LuckyNumberFragment.newInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object LuckyNumberHistory : Destination() {
|
||||||
|
override val destinationType get() = Type.LUCKY_NUMBER_HISTORY
|
||||||
|
override val destinationFragment get() = LuckyNumberHistoryFragment.newInstance()
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
object More : Destination() {
|
object More : Destination() {
|
||||||
override val destinationType get() = Type.MORE
|
override val destinationType get() = Type.MORE
|
||||||
@ -125,4 +137,16 @@ sealed class Destination {
|
|||||||
override val destinationType get() = Type.MESSAGE
|
override val destinationType get() = Type.MESSAGE
|
||||||
override val destinationFragment get() = MessageFragment.newInstance()
|
override val destinationFragment get() = MessageFragment.newInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object MobileDevice : Destination() {
|
||||||
|
override val destinationType get() = Type.MOBILE_DEVICE
|
||||||
|
override val destinationFragment get() = MobileDeviceFragment.newInstance()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object Settings : Destination() {
|
||||||
|
override val destinationType get() = Type.SETTINGS
|
||||||
|
override val destinationFragment get() = SettingsFragment.newInstance()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import android.view.View
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
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.repositories.PreferencesRepository
|
||||||
import io.github.wulkanowy.databinding.FragmentAboutBinding
|
import io.github.wulkanowy.databinding.FragmentAboutBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
import io.github.wulkanowy.ui.modules.about.contributor.ContributorFragment
|
||||||
@ -30,6 +31,9 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var appInfo: AppInfo
|
lateinit var appInfo: AppInfo
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var preferencesRepository: PreferencesRepository
|
||||||
|
|
||||||
override val versionRes: Triple<String, String, Drawable?>?
|
override val versionRes: Triple<String, String, Drawable?>?
|
||||||
get() = context?.run {
|
get() = context?.run {
|
||||||
val buildTimestamp =
|
val buildTimestamp =
|
||||||
@ -185,7 +189,8 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
|
|||||||
R.string.about_feedback_template,
|
R.string.about_feedback_template,
|
||||||
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
|
||||||
appInfo.systemVersion.toString(),
|
appInfo.systemVersion.toString(),
|
||||||
"${appInfo.versionName}-${appInfo.buildFlavor}"
|
"${appInfo.versionName}-${appInfo.buildFlavor}",
|
||||||
|
preferencesRepository.installationId,
|
||||||
),
|
),
|
||||||
onActivityNotFound = {
|
onActivityNotFound = {
|
||||||
requireContext().openInternetBrowser(
|
requireContext().openInternetBrowser(
|
||||||
|
@ -34,6 +34,7 @@ class AccountFragment : BaseFragment<FragmentAccountBinding>(R.layout.fragment_a
|
|||||||
|
|
||||||
override val titleStringId = R.string.account_title
|
override val titleStringId = R.string.account_title
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
@ -6,8 +6,10 @@ import android.view.MenuInflater
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.get
|
import androidx.core.view.get
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
@ -21,6 +23,7 @@ import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoFragment
|
|||||||
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
import io.github.wulkanowy.ui.modules.studentinfo.StudentInfoView
|
||||||
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
import io.github.wulkanowy.utils.createNameInitialsDrawable
|
||||||
import io.github.wulkanowy.utils.nickOrName
|
import io.github.wulkanowy.utils.nickOrName
|
||||||
|
import io.github.wulkanowy.utils.serializable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -37,12 +40,12 @@ class AccountDetailsFragment :
|
|||||||
|
|
||||||
private const val ARGUMENT_KEY = "Data"
|
private const val ARGUMENT_KEY = "Data"
|
||||||
|
|
||||||
fun newInstance(student: Student) =
|
fun newInstance(student: Student) = AccountDetailsFragment().apply {
|
||||||
AccountDetailsFragment().apply {
|
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, student) }
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -51,7 +54,7 @@ class AccountDetailsFragment :
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
binding = FragmentAccountDetailsBinding.bind(view)
|
binding = FragmentAccountDetailsBinding.bind(view)
|
||||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
@ -112,7 +115,7 @@ class AccountDetailsFragment :
|
|||||||
|
|
||||||
override fun showLogoutConfirmDialog() {
|
override fun showLogoutConfirmDialog() {
|
||||||
context?.let {
|
context?.let {
|
||||||
AlertDialog.Builder(it)
|
MaterialAlertDialogBuilder(it)
|
||||||
.setTitle(R.string.account_logout_student)
|
.setTitle(R.string.account_logout_student)
|
||||||
.setMessage(R.string.account_confirm)
|
.setMessage(R.string.account_confirm)
|
||||||
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
.setPositiveButton(R.string.account_logout) { _, _ -> presenter.onLogoutConfirm() }
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
package io.github.wulkanowy.ui.modules.account.accountedit
|
package io.github.wulkanowy.ui.modules.account.accountedit
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.databinding.DialogAccountEditBinding
|
import io.github.wulkanowy.databinding.DialogAccountEditBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
|
import io.github.wulkanowy.utils.serializable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -24,28 +26,21 @@ class AccountEditDialog : BaseDialogFragment<DialogAccountEditBinding>(), Accoun
|
|||||||
|
|
||||||
private const val ARGUMENT_KEY = "student_with_semesters"
|
private const val ARGUMENT_KEY = "student_with_semesters"
|
||||||
|
|
||||||
fun newInstance(student: Student) =
|
fun newInstance(student: Student) = AccountEditDialog().apply {
|
||||||
AccountEditDialog().apply {
|
arguments = bundleOf(ARGUMENT_KEY to student)
|
||||||
arguments = Bundle().apply {
|
}
|
||||||
putSerializable(ARGUMENT_KEY, student)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
inflater: LayoutInflater,
|
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||||
container: ViewGroup?,
|
.setView(DialogAccountEditBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||||
savedInstanceState: Bundle?
|
.create()
|
||||||
): View = DialogAccountEditBinding.inflate(inflater).apply { binding = this }.root
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
presenter.onAttachView(this, requireArguments()[ARGUMENT_KEY] as Student)
|
presenter.onAttachView(this, requireArguments().serializable(ARGUMENT_KEY))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package io.github.wulkanowy.ui.modules.account.accountquick
|
package io.github.wulkanowy.ui.modules.account.accountquick
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import androidx.core.os.bundleOf
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
|
||||||
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
|
import io.github.wulkanowy.databinding.DialogAccountQuickBinding
|
||||||
@ -13,6 +14,7 @@ import io.github.wulkanowy.ui.modules.account.AccountAdapter
|
|||||||
import io.github.wulkanowy.ui.modules.account.AccountFragment
|
import io.github.wulkanowy.ui.modules.account.AccountFragment
|
||||||
import io.github.wulkanowy.ui.modules.account.AccountItem
|
import io.github.wulkanowy.ui.modules.account.AccountItem
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
|
import io.github.wulkanowy.utils.serializable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@ -30,27 +32,23 @@ class AccountQuickDialog : BaseDialogFragment<DialogAccountQuickBinding>(), Acco
|
|||||||
|
|
||||||
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
fun newInstance(studentsWithSemesters: List<StudentWithSemesters>) =
|
||||||
AccountQuickDialog().apply {
|
AccountQuickDialog().apply {
|
||||||
arguments = Bundle().apply {
|
arguments = bundleOf(STUDENTS_ARGUMENT_KEY to studentsWithSemesters.toTypedArray())
|
||||||
putSerializable(STUDENTS_ARGUMENT_KEY, studentsWithSemesters.toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
super.onCreate(savedInstanceState)
|
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
.setView(
|
||||||
|
DialogAccountQuickBinding.inflate(layoutInflater)
|
||||||
|
.apply { binding = this }.root
|
||||||
|
)
|
||||||
|
.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
) = DialogAccountQuickBinding.inflate(inflater).apply { binding = this }.root
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
val studentsWithSemesters =
|
super.onViewCreated(view, savedInstanceState)
|
||||||
(requireArguments()[STUDENTS_ARGUMENT_KEY] as Array<StudentWithSemesters>).toList()
|
val studentsWithSemesters = requireArguments()
|
||||||
|
.serializable<Array<StudentWithSemesters>>(STUDENTS_ARGUMENT_KEY).toList()
|
||||||
|
|
||||||
presenter.onAttachView(this, studentsWithSemesters)
|
presenter.onAttachView(this, studentsWithSemesters)
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package io.github.wulkanowy.ui.modules.attendance
|
package io.github.wulkanowy.ui.modules.attendance
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
||||||
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
import io.github.wulkanowy.utils.descriptionRes
|
import io.github.wulkanowy.utils.descriptionRes
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.serializable
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
|
||||||
class AttendanceDialog : DialogFragment() {
|
@AndroidEntryPoint
|
||||||
|
class AttendanceDialog : BaseDialogFragment<DialogAttendanceBinding>() {
|
||||||
private var binding: DialogAttendanceBinding by lifecycleAwareVariable()
|
|
||||||
|
|
||||||
private lateinit var attendance: Attendance
|
private lateinit var attendance: Attendance
|
||||||
|
|
||||||
@ -22,23 +23,20 @@ class AttendanceDialog : DialogFragment() {
|
|||||||
private const val ARGUMENT_KEY = "Item"
|
private const val ARGUMENT_KEY = "Item"
|
||||||
|
|
||||||
fun newInstance(exam: Attendance) = AttendanceDialog().apply {
|
fun newInstance(exam: Attendance) = AttendanceDialog().apply {
|
||||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
attendance = requireArguments().serializable(ARGUMENT_KEY)
|
||||||
arguments?.run {
|
|
||||||
attendance = getSerializable(ARGUMENT_KEY) as Attendance
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
inflater: LayoutInflater,
|
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||||
container: ViewGroup?,
|
.setView(DialogAttendanceBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||||
savedInstanceState: Bundle?
|
.create()
|
||||||
) = DialogAttendanceBinding.inflate(inflater).apply { binding = this }.root
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -4,10 +4,10 @@ import android.content.DialogInterface.BUTTON_POSITIVE
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.View.*
|
import android.view.View.*
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.appcompat.view.ActionMode
|
import androidx.appcompat.view.ActionMode
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
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.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
@ -84,6 +84,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -123,7 +124,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
|
|
||||||
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
|
attendanceExcuseButton.setOnClickListener { presenter.onExcuseButtonClick() }
|
||||||
|
|
||||||
attendanceNavContainer.elevation = requireContext().dpToPx(8f)
|
attendanceNavContainer.elevation = requireContext().dpToPx(3f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +228,7 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
|
|||||||
|
|
||||||
override fun showExcuseDialog() {
|
override fun showExcuseDialog() {
|
||||||
val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context))
|
val dialogBinding = DialogExcuseBinding.inflate(LayoutInflater.from(context))
|
||||||
AlertDialog.Builder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.attendance_excuse_title)
|
.setTitle(R.string.attendance_excuse_title)
|
||||||
.setView(dialogBinding.root)
|
.setView(dialogBinding.root)
|
||||||
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
.setNegativeButton(android.R.string.cancel) { _, _ -> }
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package io.github.wulkanowy.ui.modules.conference
|
package io.github.wulkanowy.ui.modules.conference
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import androidx.core.os.bundleOf
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.DialogFragment
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.data.db.entities.Conference
|
import io.github.wulkanowy.data.db.entities.Conference
|
||||||
import io.github.wulkanowy.databinding.DialogConferenceBinding
|
import io.github.wulkanowy.databinding.DialogConferenceBinding
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
|
import io.github.wulkanowy.utils.serializable
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
|
||||||
class ConferenceDialog : DialogFragment() {
|
@AndroidEntryPoint
|
||||||
|
class ConferenceDialog : BaseDialogFragment<DialogConferenceBinding>() {
|
||||||
private var binding: DialogConferenceBinding by lifecycleAwareVariable()
|
|
||||||
|
|
||||||
private lateinit var conference: Conference
|
private lateinit var conference: Conference
|
||||||
|
|
||||||
@ -22,23 +23,20 @@ class ConferenceDialog : DialogFragment() {
|
|||||||
private const val ARGUMENT_KEY = "item"
|
private const val ARGUMENT_KEY = "item"
|
||||||
|
|
||||||
fun newInstance(conference: Conference) = ConferenceDialog().apply {
|
fun newInstance(conference: Conference) = ConferenceDialog().apply {
|
||||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, conference) }
|
arguments = bundleOf(ARGUMENT_KEY to conference)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
conference = requireArguments().serializable(ARGUMENT_KEY)
|
||||||
arguments?.let {
|
|
||||||
conference = it.getSerializable(ARGUMENT_KEY) as Conference
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
inflater: LayoutInflater,
|
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||||
container: ViewGroup?,
|
.setView(DialogConferenceBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||||
savedInstanceState: Bundle?
|
.create()
|
||||||
) = DialogConferenceBinding.inflate(inflater).also { binding = it }.root
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
@ -57,4 +55,4 @@ class ConferenceDialog : DialogFragment() {
|
|||||||
conferenceDialogAgendaTitle.isVisible = conference.agenda.isNotBlank()
|
conferenceDialogAgendaTitle.isVisible = conference.agenda.isNotBlank()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.fragment_conference),
|
class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.fragment_conference),
|
||||||
ConferenceView, MainView.TitledView {
|
ConferenceView, MainView.TitledView, MainView.MainChildView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: ConferencePresenter
|
lateinit var presenter: ConferencePresenter
|
||||||
@ -109,6 +109,14 @@ class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.frag
|
|||||||
(activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference))
|
(activity as? MainActivity)?.showDialogFragment(ConferenceDialog.newInstance(conference))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFragmentReselected() {
|
||||||
|
if (::presenter.isInitialized) presenter.onFragmentReselected()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetView() {
|
||||||
|
binding.conferenceRecycler.smoothScrollToPosition(0)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
@ -96,4 +96,11 @@ class ConferencePresenter @Inject constructor(
|
|||||||
.onResourceError(errorHandler::dispatch)
|
.onResourceError(errorHandler::dispatch)
|
||||||
.launch()
|
.launch()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onFragmentReselected() {
|
||||||
|
Timber.i("Conference is reselected")
|
||||||
|
if (view?.isViewEmpty == false) {
|
||||||
|
view?.resetView()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,4 +28,6 @@ interface ConferenceView : BaseView {
|
|||||||
fun showContent(show: Boolean)
|
fun showContent(show: Boolean)
|
||||||
|
|
||||||
fun openConferenceDialog(conference: Conference)
|
fun openConferenceDialog(conference: Conference)
|
||||||
|
|
||||||
|
fun resetView()
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import androidx.recyclerview.widget.DefaultItemAnimator
|
|||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.databinding.FragmentDashboardBinding
|
import io.github.wulkanowy.databinding.FragmentDashboardBinding
|
||||||
@ -61,6 +62,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
fun newInstance() = DashboardFragment()
|
fun newInstance() = DashboardFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -147,7 +149,7 @@ class DashboardFragment : BaseFragment<FragmentDashboardBinding>(R.layout.fragme
|
|||||||
val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values)
|
val values = requireContext().resources.getStringArray(R.array.dashboard_tile_values)
|
||||||
val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } }
|
val selectedItemsState = values.map { value -> selectedItems.any { it.name == value } }
|
||||||
|
|
||||||
AlertDialog.Builder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.pref_dashboard_appearance_tiles_title)
|
.setTitle(R.string.pref_dashboard_appearance_tiles_title)
|
||||||
.setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> }
|
.setMultiChoiceItems(entries, selectedItemsState.toBooleanArray()) { _, _, _ -> }
|
||||||
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
||||||
|
@ -33,18 +33,27 @@ sealed class DashboardItem(val type: Type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data class HorizontalGroup(
|
data class HorizontalGroup(
|
||||||
val unreadMessagesCount: Int? = null,
|
val unreadMessagesCount: Cell<Int?>? = null,
|
||||||
val attendancePercentage: Double? = null,
|
val attendancePercentage: Cell<Double>? = null,
|
||||||
val luckyNumber: Int? = null,
|
val luckyNumber: Cell<Int>? = null,
|
||||||
override val error: Throwable? = null,
|
override val error: Throwable? = null,
|
||||||
override val isLoading: Boolean = false
|
override val isLoading: Boolean = false
|
||||||
) : DashboardItem(Type.HORIZONTAL_GROUP) {
|
) : DashboardItem(Type.HORIZONTAL_GROUP) {
|
||||||
|
|
||||||
|
data class Cell<T>(
|
||||||
|
val data: T?,
|
||||||
|
val error: Boolean,
|
||||||
|
val isLoading: Boolean,
|
||||||
|
) {
|
||||||
|
val isHidden: Boolean
|
||||||
|
get() = data == null && !error && !isLoading
|
||||||
|
}
|
||||||
|
|
||||||
override val isDataLoaded
|
override val isDataLoaded
|
||||||
get() = unreadMessagesCount != null || attendancePercentage != null || luckyNumber != null
|
get() = unreadMessagesCount?.isLoading == false || attendancePercentage?.isLoading == false || luckyNumber?.isLoading == false
|
||||||
|
|
||||||
val isFullDataLoaded
|
val isFullDataLoaded
|
||||||
get() = luckyNumber != -1 && attendancePercentage != -1.0 && unreadMessagesCount != -1
|
get() = luckyNumber?.isLoading != true && attendancePercentage?.isLoading != true && unreadMessagesCount?.isLoading != true
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Grades(
|
data class Grades(
|
||||||
|
@ -25,7 +25,6 @@ class DashboardPresenter @Inject constructor(
|
|||||||
private val gradeRepository: GradeRepository,
|
private val gradeRepository: GradeRepository,
|
||||||
private val semesterRepository: SemesterRepository,
|
private val semesterRepository: SemesterRepository,
|
||||||
private val messageRepository: MessageRepository,
|
private val messageRepository: MessageRepository,
|
||||||
private val mailboxRepository: MailboxRepository,
|
|
||||||
private val attendanceSummaryRepository: AttendanceSummaryRepository,
|
private val attendanceSummaryRepository: AttendanceSummaryRepository,
|
||||||
private val timetableRepository: TimetableRepository,
|
private val timetableRepository: TimetableRepository,
|
||||||
private val homeworkRepository: HomeworkRepository,
|
private val homeworkRepository: HomeworkRepository,
|
||||||
@ -227,50 +226,71 @@ class DashboardPresenter @Inject constructor(
|
|||||||
|
|
||||||
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
|
private fun loadHorizontalGroup(student: Student, forceRefresh: Boolean) {
|
||||||
flow {
|
flow {
|
||||||
val semester = semesterRepository.getCurrentSemester(student)
|
|
||||||
val mailbox = mailboxRepository.getMailbox(student)
|
|
||||||
val selectedTiles = preferencesRepository.selectedDashboardTiles
|
val selectedTiles = preferencesRepository.selectedDashboardTiles
|
||||||
|
|
||||||
val flowSuccess = flowOf(Resource.Success(null))
|
val flowSuccess = flowOf(Resource.Success(null))
|
||||||
|
|
||||||
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
|
val luckyNumberFlow = luckyNumberRepository.getLuckyNumber(student, forceRefresh)
|
||||||
.mapResourceData {
|
.mapResourceData {
|
||||||
it ?: LuckyNumber(0, LocalDate.now(), 0)
|
it ?: LuckyNumber(0, LocalDate.now(), 0)
|
||||||
}
|
}
|
||||||
|
.onResourceError { errorHandler.dispatch(it) }
|
||||||
.takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess
|
.takeIf { DashboardItem.Tile.LUCKY_NUMBER in selectedTiles } ?: flowSuccess
|
||||||
|
|
||||||
val messageFLow = messageRepository.getMessages(
|
val messageFLow = flatResourceFlow {
|
||||||
student = student,
|
val mailbox = messageRepository.getMailboxByStudent(student)
|
||||||
mailbox = mailbox,
|
|
||||||
folder = MessageFolder.RECEIVED,
|
|
||||||
forceRefresh = forceRefresh
|
|
||||||
).takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
|
||||||
|
|
||||||
val attendanceFlow = attendanceSummaryRepository.getAttendanceSummary(
|
messageRepository.getMessages(
|
||||||
student = student,
|
student = student,
|
||||||
semester = semester,
|
mailbox = mailbox,
|
||||||
subjectId = -1,
|
folder = MessageFolder.RECEIVED,
|
||||||
forceRefresh = forceRefresh
|
forceRefresh = forceRefresh
|
||||||
).takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
|
)
|
||||||
|
}
|
||||||
|
.onResourceError { errorHandler.dispatch(it) }
|
||||||
|
.takeIf { DashboardItem.Tile.MESSAGES in selectedTiles } ?: flowSuccess
|
||||||
|
|
||||||
|
val attendanceFlow = flatResourceFlow {
|
||||||
|
val semester = semesterRepository.getCurrentSemester(student)
|
||||||
|
attendanceSummaryRepository.getAttendanceSummary(
|
||||||
|
student = student,
|
||||||
|
semester = semester,
|
||||||
|
subjectId = -1,
|
||||||
|
forceRefresh = forceRefresh
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onResourceError { errorHandler.dispatch(it) }
|
||||||
|
.takeIf { DashboardItem.Tile.ATTENDANCE in selectedTiles } ?: flowSuccess
|
||||||
|
|
||||||
emitAll(
|
emitAll(
|
||||||
combine(
|
combine(
|
||||||
luckyNumberFlow,
|
flow = luckyNumberFlow,
|
||||||
messageFLow,
|
flow2 = messageFLow,
|
||||||
attendanceFlow
|
flow3 = attendanceFlow,
|
||||||
) { luckyNumberResource, messageResource, attendanceResource ->
|
) { luckyNumberResource, messageResource, attendanceResource ->
|
||||||
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
|
val resList = listOf(luckyNumberResource, messageResource, attendanceResource)
|
||||||
resList.firstNotNullOfOrNull { it.errorOrNull }?.let { throw it }
|
|
||||||
val isLoading = resList.any { it is Resource.Loading }
|
|
||||||
|
|
||||||
val luckyNumber = luckyNumberResource.dataOrNull?.luckyNumber
|
|
||||||
val messageCount = messageResource.dataOrNull?.count { it.unread }
|
|
||||||
val attendancePercentage = attendanceResource.dataOrNull?.calculatePercentage()
|
|
||||||
|
|
||||||
DashboardItem.HorizontalGroup(
|
DashboardItem.HorizontalGroup(
|
||||||
isLoading = isLoading,
|
isLoading = resList.any { it is Resource.Loading },
|
||||||
attendancePercentage = if (attendancePercentage == 0.0 && isLoading) -1.0 else attendancePercentage,
|
error = resList.map { it.errorOrNull }.let { errors ->
|
||||||
unreadMessagesCount = if (messageCount == 0 && isLoading) -1 else messageCount,
|
if (errors.all { it != null }) {
|
||||||
luckyNumber = if (luckyNumber == 0 && isLoading) -1 else luckyNumber
|
errors.firstOrNull()
|
||||||
|
} else null
|
||||||
|
},
|
||||||
|
attendancePercentage = DashboardItem.HorizontalGroup.Cell(
|
||||||
|
data = attendanceResource.dataOrNull?.calculatePercentage(),
|
||||||
|
error = attendanceResource.errorOrNull != null,
|
||||||
|
isLoading = attendanceResource is Resource.Loading,
|
||||||
|
),
|
||||||
|
unreadMessagesCount = DashboardItem.HorizontalGroup.Cell(
|
||||||
|
data = messageResource.dataOrNull?.count { it.unread },
|
||||||
|
error = messageResource.errorOrNull != null,
|
||||||
|
isLoading = messageResource is Resource.Loading,
|
||||||
|
),
|
||||||
|
luckyNumber = DashboardItem.HorizontalGroup.Cell(
|
||||||
|
data = luckyNumberResource.dataOrNull?.luckyNumber,
|
||||||
|
error = luckyNumberResource.errorOrNull != null,
|
||||||
|
isLoading = luckyNumberResource is Resource.Loading,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -281,11 +301,8 @@ class DashboardPresenter @Inject constructor(
|
|||||||
|
|
||||||
if (it.isLoading) {
|
if (it.isLoading) {
|
||||||
Timber.i("Loading horizontal group data started")
|
Timber.i("Loading horizontal group data started")
|
||||||
|
|
||||||
if (it.isFullDataLoaded) {
|
|
||||||
firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
firstLoadedItemList += DashboardItem.Type.HORIZONTAL_GROUP
|
||||||
Timber.i("Loading horizontal group result: Success")
|
Timber.i("Loading horizontal group result: Success")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,7 +606,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
is Resource.Error -> {
|
is Resource.Error -> {
|
||||||
Timber.i("Loading dashboard admin message result: An exception occurred")
|
Timber.i("Loading dashboard admin message result: An exception occurred")
|
||||||
errorHandler.dispatch(it.error)
|
Timber.e(it.error)
|
||||||
updateData(
|
updateData(
|
||||||
dashboardItem = DashboardItem.AdminMessages(
|
dashboardItem = DashboardItem.AdminMessages(
|
||||||
adminMessage = null,
|
adminMessage = null,
|
||||||
@ -731,7 +748,7 @@ class DashboardPresenter @Inject constructor(
|
|||||||
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
|
itemsLoadedList.find { it.type == DashboardItem.Type.ACCOUNT }?.error != null
|
||||||
val isGeneralError =
|
val isGeneralError =
|
||||||
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
|
filteredItems.none { it.error == null } && filteredItems.isNotEmpty() || isAccountItemError
|
||||||
val firstError = itemsLoadedList.mapNotNull { it.error }.firstOrNull()
|
val firstError = itemsLoadedList.firstNotNullOfOrNull { it.error }
|
||||||
|
|
||||||
val filteredOriginalLoadedList =
|
val filteredOriginalLoadedList =
|
||||||
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
|
dashboardItemLoadedList.filterNot { it.type == DashboardItem.Type.ACCOUNT }
|
||||||
|
@ -171,81 +171,105 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
position: Int
|
position: Int
|
||||||
) {
|
) {
|
||||||
val item = items[position] as DashboardItem.HorizontalGroup
|
val item = items[position] as DashboardItem.HorizontalGroup
|
||||||
val unreadMessagesCount = item.unreadMessagesCount
|
|
||||||
val attendancePercentage = item.attendancePercentage
|
|
||||||
val luckyNumber = item.luckyNumber
|
|
||||||
val error = item.error
|
|
||||||
val isLoading = item.isLoading
|
|
||||||
val binding = horizontalGroupViewHolder.binding
|
|
||||||
val context = binding.root.context
|
|
||||||
val isLoadingVisible =
|
val isLoadingVisible =
|
||||||
(isLoading && !item.isDataLoaded) || (isLoading && !item.isFullDataLoaded)
|
(item.isLoading && !item.isDataLoaded) || (item.isLoading && !item.isFullDataLoaded)
|
||||||
|
val isWideErrorShow = isLoadingVisible || item.error != null
|
||||||
|
|
||||||
|
with(horizontalGroupViewHolder.binding) {
|
||||||
|
dashboardHorizontalGroupItemInfoContainer.isVisible = isWideErrorShow
|
||||||
|
dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible
|
||||||
|
dashboardHorizontalGroupItemInfoErrorText.isVisible = item.error != null
|
||||||
|
|
||||||
|
bindLuckyNumber(item, isWideErrorShow)
|
||||||
|
bindMessages(item, isWideErrorShow)
|
||||||
|
bindAttendance(item, isWideErrorShow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ItemDashboardHorizontalGroupBinding.bindLuckyNumber(
|
||||||
|
item: DashboardItem.HorizontalGroup,
|
||||||
|
isWideErrorShow: Boolean
|
||||||
|
) {
|
||||||
|
with(dashboardHorizontalGroupItemLuckyValue) {
|
||||||
|
isVisible = item.luckyNumber?.error != true
|
||||||
|
text = if (item.luckyNumber?.data == 0) {
|
||||||
|
context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||||
|
} else item.luckyNumber?.data?.toString()
|
||||||
|
}
|
||||||
|
dashboardHorizontalGroupItemLuckyError.isVisible = item.luckyNumber?.error == true
|
||||||
|
with(dashboardHorizontalGroupItemLuckyContainer) {
|
||||||
|
isVisible = item.luckyNumber?.isHidden == false && !isWideErrorShow
|
||||||
|
setOnClickListener { onLuckyNumberTileClickListener() }
|
||||||
|
|
||||||
|
val isAttendanceHidden = item.attendancePercentage?.isHidden == true
|
||||||
|
val isMessagesHidden = item.unreadMessagesCount?.isHidden == true
|
||||||
|
val isLuckyNumberHidden = item.luckyNumber?.isHidden == true
|
||||||
|
|
||||||
|
updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
|
updateMarginsRelative(
|
||||||
|
end = if (isAttendanceHidden && isMessagesHidden && !isLuckyNumberHidden) {
|
||||||
|
0
|
||||||
|
} else context.dpToPx(8f).toInt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ItemDashboardHorizontalGroupBinding.bindMessages(
|
||||||
|
item: DashboardItem.HorizontalGroup,
|
||||||
|
isWideErrorShow: Boolean
|
||||||
|
) {
|
||||||
|
dashboardHorizontalGroupItemMessageError.isVisible = item.unreadMessagesCount?.error == true
|
||||||
|
with(dashboardHorizontalGroupItemMessageValue) {
|
||||||
|
isVisible = item.unreadMessagesCount?.error != true
|
||||||
|
text = item.unreadMessagesCount?.data.toString()
|
||||||
|
}
|
||||||
|
with(dashboardHorizontalGroupItemMessageContainer) {
|
||||||
|
isVisible = item.unreadMessagesCount?.isHidden == false && !isWideErrorShow
|
||||||
|
setOnClickListener { onMessageTileClickListener() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ItemDashboardHorizontalGroupBinding.bindAttendance(
|
||||||
|
item: DashboardItem.HorizontalGroup,
|
||||||
|
isWideErrorShow: Boolean
|
||||||
|
) {
|
||||||
|
val attendancePercentage = item.attendancePercentage?.data
|
||||||
val attendanceColor = when {
|
val attendanceColor = when {
|
||||||
attendancePercentage == null || attendancePercentage == .0 -> {
|
attendancePercentage == null || attendancePercentage == .0 -> {
|
||||||
context.getThemeAttrColor(R.attr.colorOnSurface)
|
root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||||
}
|
}
|
||||||
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
|
attendancePercentage <= ATTENDANCE_SECOND_WARNING_THRESHOLD -> {
|
||||||
context.getThemeAttrColor(R.attr.colorPrimary)
|
root.context.getThemeAttrColor(R.attr.colorPrimary)
|
||||||
}
|
}
|
||||||
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
|
attendancePercentage <= ATTENDANCE_FIRST_WARNING_THRESHOLD -> {
|
||||||
context.getThemeAttrColor(R.attr.colorTimetableChange)
|
root.context.getThemeAttrColor(R.attr.colorTimetableChange)
|
||||||
}
|
}
|
||||||
else -> context.getThemeAttrColor(R.attr.colorOnSurface)
|
else -> root.context.getThemeAttrColor(R.attr.colorOnSurface)
|
||||||
}
|
}
|
||||||
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
|
val attendanceString = if (attendancePercentage == null || attendancePercentage == .0) {
|
||||||
context.getString(R.string.dashboard_horizontal_group_no_data)
|
root.context.getString(R.string.dashboard_horizontal_group_no_data)
|
||||||
} else {
|
} else {
|
||||||
"%.2f%%".format(attendancePercentage)
|
"%.2f%%".format(attendancePercentage)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(binding.dashboardHorizontalGroupItemAttendanceValue) {
|
dashboardHorizontalGroupItemAttendanceError.isVisible =
|
||||||
|
item.attendancePercentage?.error == true
|
||||||
|
with(dashboardHorizontalGroupItemAttendanceValue) {
|
||||||
|
isVisible = item.attendancePercentage?.error != true
|
||||||
text = attendanceString
|
text = attendanceString
|
||||||
setTextColor(attendanceColor)
|
setTextColor(attendanceColor)
|
||||||
}
|
}
|
||||||
|
with(dashboardHorizontalGroupItemAttendanceContainer) {
|
||||||
with(binding) {
|
isVisible = item.attendancePercentage?.isHidden == false && !isWideErrorShow
|
||||||
dashboardHorizontalGroupItemMessageValue.text = unreadMessagesCount.toString()
|
setOnClickListener { onAttendanceTileClickListener() }
|
||||||
dashboardHorizontalGroupItemLuckyValue.text = if (luckyNumber == 0) {
|
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||||
context.getString(R.string.dashboard_horizontal_group_no_data)
|
matchConstraintPercentWidth = when {
|
||||||
} else luckyNumber?.toString()
|
item.luckyNumber?.isHidden == true && item.unreadMessagesCount?.isHidden == true -> 1.0f
|
||||||
|
item.luckyNumber?.isHidden == true || item.unreadMessagesCount?.isHidden == true -> 0.5f
|
||||||
dashboardHorizontalGroupItemInfoContainer.isVisible = error != null || isLoadingVisible
|
else -> 0.4f
|
||||||
dashboardHorizontalGroupItemInfoProgress.isVisible = isLoadingVisible
|
|
||||||
dashboardHorizontalGroupItemInfoErrorText.isVisible = error != null
|
|
||||||
|
|
||||||
with(dashboardHorizontalGroupItemLuckyContainer) {
|
|
||||||
isVisible = luckyNumber != null && luckyNumber != -1 && !isLoadingVisible
|
|
||||||
setOnClickListener { onLuckyNumberTileClickListener() }
|
|
||||||
|
|
||||||
updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
|
||||||
updateMarginsRelative(
|
|
||||||
end = if (attendancePercentage == null && unreadMessagesCount == null && luckyNumber != null) {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
context.dpToPx(8f).toInt()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with(dashboardHorizontalGroupItemAttendanceContainer) {
|
|
||||||
isVisible =
|
|
||||||
attendancePercentage != null && attendancePercentage != -1.0 && !isLoadingVisible
|
|
||||||
updateLayoutParams<ConstraintLayout.LayoutParams> {
|
|
||||||
matchConstraintPercentWidth = when {
|
|
||||||
luckyNumber == null && unreadMessagesCount == null -> 1.0f
|
|
||||||
luckyNumber == null || unreadMessagesCount == null -> 0.5f
|
|
||||||
else -> 0.4f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setOnClickListener { onAttendanceTileClickListener() }
|
|
||||||
}
|
|
||||||
|
|
||||||
with(dashboardHorizontalGroupItemMessageContainer) {
|
|
||||||
isVisible =
|
|
||||||
unreadMessagesCount != null && unreadMessagesCount != -1 && !isLoadingVisible
|
|
||||||
setOnClickListener { onMessageTileClickListener() }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
package io.github.wulkanowy.ui.modules.dashboard.adapters
|
||||||
|
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
@ -8,6 +9,7 @@ import io.github.wulkanowy.data.enums.GradeColorTheme
|
|||||||
import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding
|
import io.github.wulkanowy.databinding.SubitemDashboardGradesBinding
|
||||||
import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding
|
import io.github.wulkanowy.databinding.SubitemDashboardSmallGradeBinding
|
||||||
import io.github.wulkanowy.utils.getBackgroundColor
|
import io.github.wulkanowy.utils.getBackgroundColor
|
||||||
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
|
|
||||||
class DashboardGradesAdapter : RecyclerView.Adapter<DashboardGradesAdapter.ViewHolder>() {
|
class DashboardGradesAdapter : RecyclerView.Adapter<DashboardGradesAdapter.ViewHolder>() {
|
||||||
|
|
||||||
@ -37,7 +39,9 @@ class DashboardGradesAdapter : RecyclerView.Adapter<DashboardGradesAdapter.ViewH
|
|||||||
|
|
||||||
with(subitemBinding.dashboardSmallGradeSubitemValue) {
|
with(subitemBinding.dashboardSmallGradeSubitemValue) {
|
||||||
text = it.entry
|
text = it.entry
|
||||||
setBackgroundResource(it.getBackgroundColor(gradeColorTheme))
|
backgroundTintList = ColorStateList.valueOf(
|
||||||
|
context.getCompatColor(it.getBackgroundColor(gradeColorTheme))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dashboardGradesSubitemGradeContainer.addView(subitemBinding.root)
|
dashboardGradesSubitemGradeContainer.addView(subitemBinding.root)
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package io.github.wulkanowy.ui.modules.debug.logviewer
|
package io.github.wulkanowy.ui.modules.debug.logviewer
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.EXTRA_EMAIL
|
import android.content.Intent.*
|
||||||
import android.content.Intent.EXTRA_STREAM
|
|
||||||
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
@ -36,6 +34,7 @@ class LogViewerFragment : BaseFragment<FragmentLogviewerBinding>(R.layout.fragme
|
|||||||
fun newInstance() = LogViewerFragment()
|
fun newInstance() = LogViewerFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
|
@ -19,9 +19,12 @@ val debugMessageItems = listOf(
|
|||||||
private fun generateMessage(sender: String, subject: String) = Message(
|
private fun generateMessage(sender: String, subject: String) = Message(
|
||||||
subject = subject,
|
subject = subject,
|
||||||
messageId = 123,
|
messageId = 123,
|
||||||
|
email = "",
|
||||||
date = Instant.now(),
|
date = Instant.now(),
|
||||||
folderId = 0,
|
folderId = 0,
|
||||||
unread = true,
|
unread = true,
|
||||||
|
readBy = 2,
|
||||||
|
unreadBy = 2,
|
||||||
hasAttachments = false,
|
hasAttachments = false,
|
||||||
messageGlobalKey = "",
|
messageGlobalKey = "",
|
||||||
correspondents = sender,
|
correspondents = sender,
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
package io.github.wulkanowy.ui.modules.exam
|
package io.github.wulkanowy.ui.modules.exam
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import androidx.core.os.bundleOf
|
||||||
import androidx.fragment.app.DialogFragment
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Exam
|
import io.github.wulkanowy.data.db.entities.Exam
|
||||||
import io.github.wulkanowy.databinding.DialogExamBinding
|
import io.github.wulkanowy.databinding.DialogExamBinding
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.ui.base.BaseDialogFragment
|
||||||
import io.github.wulkanowy.utils.openCalendarEventAdd
|
import io.github.wulkanowy.utils.openCalendarEventAdd
|
||||||
|
import io.github.wulkanowy.utils.serializable
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
|
|
||||||
class ExamDialog : DialogFragment() {
|
@AndroidEntryPoint
|
||||||
|
class ExamDialog : BaseDialogFragment<DialogExamBinding>() {
|
||||||
private var binding: DialogExamBinding by lifecycleAwareVariable()
|
|
||||||
|
|
||||||
private lateinit var exam: Exam
|
private lateinit var exam: Exam
|
||||||
|
|
||||||
@ -24,23 +25,20 @@ class ExamDialog : DialogFragment() {
|
|||||||
private const val ARGUMENT_KEY = "Item"
|
private const val ARGUMENT_KEY = "Item"
|
||||||
|
|
||||||
fun newInstance(exam: Exam) = ExamDialog().apply {
|
fun newInstance(exam: Exam) = ExamDialog().apply {
|
||||||
arguments = Bundle().apply { putSerializable(ARGUMENT_KEY, exam) }
|
arguments = bundleOf(ARGUMENT_KEY to exam)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setStyle(STYLE_NO_TITLE, 0)
|
exam = requireArguments().serializable(ARGUMENT_KEY)
|
||||||
arguments?.run {
|
|
||||||
exam = getSerializable(ARGUMENT_KEY) as Exam
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
inflater: LayoutInflater,
|
return MaterialAlertDialogBuilder(requireContext(), theme)
|
||||||
container: ViewGroup?,
|
.setView(DialogExamBinding.inflate(layoutInflater).apply { binding = this }.root)
|
||||||
savedInstanceState: Bundle?
|
.create()
|
||||||
) = DialogExamBinding.inflate(inflater).apply { binding = this }.root
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
@ -2,9 +2,7 @@ package io.github.wulkanowy.ui.modules.exam
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.GONE
|
import android.view.View.*
|
||||||
import android.view.View.INVISIBLE
|
|
||||||
import android.view.View.VISIBLE
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
@ -20,7 +18,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam), ExamView,
|
class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam), ExamView,
|
||||||
MainView.TitledView {
|
MainView.TitledView, MainView.MainChildView {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var presenter: ExamPresenter
|
lateinit var presenter: ExamPresenter
|
||||||
@ -64,7 +62,7 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||||||
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
|
examPreviousButton.setOnClickListener { presenter.onPreviousWeek() }
|
||||||
examNextButton.setOnClickListener { presenter.onNextWeek() }
|
examNextButton.setOnClickListener { presenter.onNextWeek() }
|
||||||
|
|
||||||
examNavContainer.elevation = requireContext().dpToPx(8f)
|
examNavContainer.elevation = requireContext().dpToPx(3f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +124,14 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
|
|||||||
(activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam))
|
(activity as? MainActivity)?.showDialogFragment(ExamDialog.newInstance(exam))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onFragmentReselected() {
|
||||||
|
if (::presenter.isInitialized) presenter.onViewReselected()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resetView() {
|
||||||
|
binding.examRecycler.smoothScrollToPosition(0)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
|
||||||
|
@ -175,4 +175,17 @@ class ExamPresenter @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onViewReselected() {
|
||||||
|
Timber.i("Exam view is reselected")
|
||||||
|
|
||||||
|
baseDate = now().nextOrSameSchoolDay
|
||||||
|
|
||||||
|
if (currentDate != baseDate) {
|
||||||
|
reloadView(baseDate)
|
||||||
|
loadData()
|
||||||
|
} else if (view?.isViewEmpty == false) {
|
||||||
|
view?.resetView()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,4 +34,6 @@ interface ExamView : BaseView {
|
|||||||
fun showPreButton(show: Boolean)
|
fun showPreButton(show: Boolean)
|
||||||
|
|
||||||
fun showExamDialog(exam: Exam)
|
fun showExamDialog(exam: Exam)
|
||||||
|
|
||||||
|
fun resetView()
|
||||||
}
|
}
|
||||||
|
@ -12,70 +12,92 @@ import io.github.wulkanowy.sdk.Sdk
|
|||||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
|
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode.*
|
||||||
import io.github.wulkanowy.utils.calcAverage
|
import io.github.wulkanowy.utils.calcAverage
|
||||||
import io.github.wulkanowy.utils.changeModifier
|
import io.github.wulkanowy.utils.changeModifier
|
||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class GradeAverageProvider @Inject constructor(
|
class GradeAverageProvider @Inject constructor(
|
||||||
private val semesterRepository: SemesterRepository,
|
private val semesterRepository: SemesterRepository,
|
||||||
private val gradeRepository: GradeRepository,
|
private val gradeRepository: GradeRepository,
|
||||||
private val preferencesRepository: PreferencesRepository
|
private val preferencesRepository: PreferencesRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val plusModifier get() = preferencesRepository.gradePlusModifier
|
private data class AverageCalcParams(
|
||||||
|
val gradeAverageMode: GradeAverageMode,
|
||||||
|
val forceAverageCalc: Boolean,
|
||||||
|
val isOptionalArithmeticAverage: Boolean,
|
||||||
|
val plusModifier: Double,
|
||||||
|
val minusModifier: Double,
|
||||||
|
)
|
||||||
|
|
||||||
private val minusModifier get() = preferencesRepository.gradeMinusModifier
|
fun getGradesDetailsWithAverage(
|
||||||
|
student: Student,
|
||||||
private val isOptionalArithmeticAverage get() = preferencesRepository.isOptionalArithmeticAverage
|
semesterId: Int,
|
||||||
|
forceRefresh: Boolean
|
||||||
fun getGradesDetailsWithAverage(student: Student, semesterId: Int, forceRefresh: Boolean) =
|
): Flow<Resource<List<GradeSubject>>> = combine(
|
||||||
|
flow = preferencesRepository.gradeAverageModeFlow,
|
||||||
|
flow2 = preferencesRepository.gradeAverageForceCalcFlow,
|
||||||
|
flow3 = preferencesRepository.isOptionalArithmeticAverageFlow,
|
||||||
|
flow4 = preferencesRepository.gradePlusModifierFlow,
|
||||||
|
flow5 = preferencesRepository.gradeMinusModifierFlow,
|
||||||
|
) { gradeAverageMode, forceAverageCalc, isOptionalArithmeticAverage, plusModifier, minusModifier ->
|
||||||
|
AverageCalcParams(
|
||||||
|
gradeAverageMode = gradeAverageMode,
|
||||||
|
forceAverageCalc = forceAverageCalc,
|
||||||
|
isOptionalArithmeticAverage = isOptionalArithmeticAverage,
|
||||||
|
plusModifier = plusModifier,
|
||||||
|
minusModifier = minusModifier,
|
||||||
|
)
|
||||||
|
}.flatMapLatest { params ->
|
||||||
flatResourceFlow {
|
flatResourceFlow {
|
||||||
val semesters = semesterRepository.getSemesters(student)
|
val semesters = semesterRepository.getSemesters(student)
|
||||||
|
when (params.gradeAverageMode) {
|
||||||
when (preferencesRepository.gradeAverageMode) {
|
|
||||||
ONE_SEMESTER -> getGradeSubjects(
|
ONE_SEMESTER -> getGradeSubjects(
|
||||||
student = student,
|
student = student,
|
||||||
semester = semesters.single { it.semesterId == semesterId },
|
semester = semesters.single { it.semesterId == semesterId },
|
||||||
forceRefresh = forceRefresh
|
forceRefresh = forceRefresh,
|
||||||
|
params = params,
|
||||||
)
|
)
|
||||||
BOTH_SEMESTERS -> calculateCombinedAverage(
|
BOTH_SEMESTERS -> calculateCombinedAverage(
|
||||||
student = student,
|
student = student,
|
||||||
semesters = semesters,
|
semesters = semesters,
|
||||||
semesterId = semesterId,
|
semesterId = semesterId,
|
||||||
forceRefresh = forceRefresh,
|
forceRefresh = forceRefresh,
|
||||||
averageMode = BOTH_SEMESTERS
|
config = params,
|
||||||
)
|
)
|
||||||
ALL_YEAR -> calculateCombinedAverage(
|
ALL_YEAR -> calculateCombinedAverage(
|
||||||
student = student,
|
student = student,
|
||||||
semesters = semesters,
|
semesters = semesters,
|
||||||
semesterId = semesterId,
|
semesterId = semesterId,
|
||||||
forceRefresh = forceRefresh,
|
forceRefresh = forceRefresh,
|
||||||
averageMode = ALL_YEAR
|
config = params,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.distinctUntilChanged()
|
}
|
||||||
|
}.distinctUntilChanged()
|
||||||
|
|
||||||
private fun calculateCombinedAverage(
|
private fun calculateCombinedAverage(
|
||||||
student: Student,
|
student: Student,
|
||||||
semesters: List<Semester>,
|
semesters: List<Semester>,
|
||||||
semesterId: Int,
|
semesterId: Int,
|
||||||
forceRefresh: Boolean,
|
forceRefresh: Boolean,
|
||||||
averageMode: GradeAverageMode
|
config: AverageCalcParams,
|
||||||
): Flow<Resource<List<GradeSubject>>> {
|
): Flow<Resource<List<GradeSubject>>> {
|
||||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
|
||||||
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
val selectedSemester = semesters.single { it.semesterId == semesterId }
|
||||||
val firstSemester =
|
val firstSemester =
|
||||||
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
semesters.single { it.diaryId == selectedSemester.diaryId && it.semesterName == 1 }
|
||||||
|
|
||||||
val selectedSemesterGradeSubjects =
|
val selectedSemesterGradeSubjects =
|
||||||
getGradeSubjects(student, selectedSemester, forceRefresh)
|
getGradeSubjects(student, selectedSemester, forceRefresh, config)
|
||||||
|
|
||||||
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
if (selectedSemester == firstSemester) return selectedSemesterGradeSubjects
|
||||||
|
|
||||||
val firstSemesterGradeSubjects = getGradeSubjects(student, firstSemester, forceRefresh)
|
val firstSemesterGradeSubjects =
|
||||||
|
getGradeSubjects(student, firstSemester, forceRefresh, config)
|
||||||
|
|
||||||
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
return selectedSemesterGradeSubjects.combine(firstSemesterGradeSubjects) { secondSemesterGradeSubject, firstSemesterGradeSubject ->
|
||||||
if (firstSemesterGradeSubject.errorOrNull != null) {
|
if (firstSemesterGradeSubject.errorOrNull != null) {
|
||||||
@ -91,21 +113,21 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
|
val firstSemesterSubject = firstSemesterGradeSubject.dataOrNull.orEmpty()
|
||||||
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
.singleOrNull { it.subject == secondSemesterSubject.subject }
|
||||||
|
|
||||||
val updatedAverage = if (averageMode == ALL_YEAR) {
|
val updatedAverage = if (config.gradeAverageMode == ALL_YEAR) {
|
||||||
calculateAllYearAverage(
|
calculateAllYearAverage(
|
||||||
student = student,
|
student = student,
|
||||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
|
||||||
secondSemesterSubject = secondSemesterSubject,
|
secondSemesterSubject = secondSemesterSubject,
|
||||||
firstSemesterSubject = firstSemesterSubject
|
firstSemesterSubject = firstSemesterSubject,
|
||||||
|
config = config,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
calculateBothSemestersAverage(
|
calculateBothSemestersAverage(
|
||||||
student = student,
|
student = student,
|
||||||
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
isAnyVulcanAverage = isAnyVulcanAverageInFirstSemester || isAnyVulcanAverageInSecondSemester,
|
||||||
isGradeAverageForceCalc = isGradeAverageForceCalc,
|
|
||||||
secondSemesterSubject = secondSemesterSubject,
|
secondSemesterSubject = secondSemesterSubject,
|
||||||
firstSemesterSubject = firstSemesterSubject
|
firstSemesterSubject = firstSemesterSubject,
|
||||||
|
config = config
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
secondSemesterSubject.copy(average = updatedAverage)
|
secondSemesterSubject.copy(average = updatedAverage)
|
||||||
@ -117,17 +139,17 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
private fun calculateAllYearAverage(
|
private fun calculateAllYearAverage(
|
||||||
student: Student,
|
student: Student,
|
||||||
isAnyVulcanAverage: Boolean,
|
isAnyVulcanAverage: Boolean,
|
||||||
isGradeAverageForceCalc: Boolean,
|
|
||||||
secondSemesterSubject: GradeSubject,
|
secondSemesterSubject: GradeSubject,
|
||||||
firstSemesterSubject: GradeSubject?
|
firstSemesterSubject: GradeSubject?,
|
||||||
) = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
config: AverageCalcParams,
|
||||||
val updatedSecondSemesterGrades =
|
) = if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||||
secondSemesterSubject.grades.updateModifiers(student)
|
val updatedSecondSemesterGrades = secondSemesterSubject.grades
|
||||||
val updatedFirstSemesterGrades =
|
.updateModifiers(student, config)
|
||||||
firstSemesterSubject?.grades?.updateModifiers(student).orEmpty()
|
val updatedFirstSemesterGrades = firstSemesterSubject?.grades
|
||||||
|
?.updateModifiers(student, config).orEmpty()
|
||||||
|
|
||||||
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
(updatedSecondSemesterGrades + updatedFirstSemesterGrades).calcAverage(
|
||||||
isOptionalArithmeticAverage
|
config.isOptionalArithmeticAverage
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
secondSemesterSubject.average
|
secondSemesterSubject.average
|
||||||
@ -136,32 +158,35 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
private fun calculateBothSemestersAverage(
|
private fun calculateBothSemestersAverage(
|
||||||
student: Student,
|
student: Student,
|
||||||
isAnyVulcanAverage: Boolean,
|
isAnyVulcanAverage: Boolean,
|
||||||
isGradeAverageForceCalc: Boolean,
|
|
||||||
secondSemesterSubject: GradeSubject,
|
secondSemesterSubject: GradeSubject,
|
||||||
firstSemesterSubject: GradeSubject?
|
firstSemesterSubject: GradeSubject?,
|
||||||
): Double = if (!isAnyVulcanAverage || isGradeAverageForceCalc) {
|
config: AverageCalcParams,
|
||||||
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
): Double {
|
||||||
|
return if (!isAnyVulcanAverage || config.forceAverageCalc) {
|
||||||
|
val divider = if (secondSemesterSubject.grades.any { it.weightValue > .0 }) 2 else 1
|
||||||
|
val secondSemesterAverage = secondSemesterSubject.grades
|
||||||
|
.updateModifiers(student, config)
|
||||||
|
.calcAverage(config.isOptionalArithmeticAverage)
|
||||||
|
val firstSemesterAverage = firstSemesterSubject?.grades
|
||||||
|
?.updateModifiers(student, config)
|
||||||
|
?.calcAverage(config.isOptionalArithmeticAverage) ?: secondSemesterAverage
|
||||||
|
|
||||||
val secondSemesterAverage = secondSemesterSubject.grades.updateModifiers(student)
|
(secondSemesterAverage + firstSemesterAverage) / divider
|
||||||
.calcAverage(isOptionalArithmeticAverage)
|
} else {
|
||||||
val firstSemesterAverage = firstSemesterSubject?.grades?.updateModifiers(student)
|
val divider = if (secondSemesterSubject.average > 0) 2 else 1
|
||||||
?.calcAverage(isOptionalArithmeticAverage) ?: secondSemesterAverage
|
|
||||||
|
|
||||||
(secondSemesterAverage + firstSemesterAverage) / divider
|
secondSemesterSubject.average.plus(
|
||||||
} else {
|
(firstSemesterSubject?.average ?: secondSemesterSubject.average)
|
||||||
val divider = if (secondSemesterSubject.average > 0) 2 else 1
|
) / divider
|
||||||
|
}
|
||||||
(secondSemesterSubject.average + (firstSemesterSubject?.average
|
|
||||||
?: secondSemesterSubject.average)) / divider
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getGradeSubjects(
|
private fun getGradeSubjects(
|
||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
forceRefresh: Boolean
|
forceRefresh: Boolean,
|
||||||
|
params: AverageCalcParams,
|
||||||
): Flow<Resource<List<GradeSubject>>> {
|
): Flow<Resource<List<GradeSubject>>> {
|
||||||
val isGradeAverageForceCalc = preferencesRepository.gradeAverageForceCalc
|
|
||||||
|
|
||||||
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
return gradeRepository.getGrades(student, semester, forceRefresh = forceRefresh)
|
||||||
.mapResourceData { res ->
|
.mapResourceData { res ->
|
||||||
val (details, summaries) = res
|
val (details, summaries) = res
|
||||||
@ -172,13 +197,15 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
student = student,
|
student = student,
|
||||||
semester = semester,
|
semester = semester,
|
||||||
grades = allGrades.toList(),
|
grades = allGrades.toList(),
|
||||||
calcAverage = isAnyAverage
|
calcAverage = isAnyAverage,
|
||||||
|
params = params,
|
||||||
).map { summary ->
|
).map { summary ->
|
||||||
val grades = allGrades[summary.subject].orEmpty()
|
val grades = allGrades[summary.subject].orEmpty()
|
||||||
GradeSubject(
|
GradeSubject(
|
||||||
subject = summary.subject,
|
subject = summary.subject,
|
||||||
average = if (!isAnyAverage || isGradeAverageForceCalc) {
|
average = if (!isAnyAverage || params.forceAverageCalc) {
|
||||||
grades.updateModifiers(student).calcAverage(isOptionalArithmeticAverage)
|
grades.updateModifiers(student, params)
|
||||||
|
.calcAverage(params.isOptionalArithmeticAverage)
|
||||||
} else summary.average,
|
} else summary.average,
|
||||||
points = summary.pointsSum,
|
points = summary.pointsSum,
|
||||||
summary = summary,
|
summary = summary,
|
||||||
@ -195,7 +222,8 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
student: Student,
|
student: Student,
|
||||||
semester: Semester,
|
semester: Semester,
|
||||||
grades: List<Pair<String, List<Grade>>>,
|
grades: List<Pair<String, List<Grade>>>,
|
||||||
calcAverage: Boolean
|
calcAverage: Boolean,
|
||||||
|
params: AverageCalcParams,
|
||||||
): List<GradeSummary> {
|
): List<GradeSummary> {
|
||||||
if (isNotEmpty() && size > grades.size) return this
|
if (isNotEmpty() && size > grades.size) return this
|
||||||
|
|
||||||
@ -211,15 +239,16 @@ class GradeAverageProvider @Inject constructor(
|
|||||||
proposedPoints = "",
|
proposedPoints = "",
|
||||||
finalPoints = "",
|
finalPoints = "",
|
||||||
pointsSum = "",
|
pointsSum = "",
|
||||||
average = if (calcAverage) details.updateModifiers(student)
|
average = if (calcAverage) details.updateModifiers(student, params)
|
||||||
.calcAverage(isOptionalArithmeticAverage) else .0
|
.calcAverage(params.isOptionalArithmeticAverage) else .0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<Grade>.updateModifiers(student: Student): List<Grade> {
|
private fun List<Grade>.updateModifiers(
|
||||||
return if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
student: Student,
|
||||||
map { it.changeModifier(plusModifier, minusModifier) }
|
params: AverageCalcParams,
|
||||||
} else this
|
): List<Grade> = if (student.loginMode == Sdk.Mode.SCRAPPER.name) {
|
||||||
}
|
map { it.changeModifier(params.plusModifier, params.minusModifier) }
|
||||||
|
} else this
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import android.view.View
|
|||||||
import android.view.View.INVISIBLE
|
import android.view.View.INVISIBLE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.tabs.TabLayoutMediator
|
import com.google.android.material.tabs.TabLayoutMediator
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
@ -51,6 +52,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
|
|
||||||
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
override val currentPageIndex get() = binding.gradeViewPager.currentItem
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
@ -140,7 +142,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
|
|||||||
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
|
val choices = semesters.map { getString(R.string.grade_semester, it.semesterName) }
|
||||||
.toTypedArray()
|
.toTypedArray()
|
||||||
|
|
||||||
AlertDialog.Builder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
|
.setSingleChoiceItems(choices, selectedIndex) { dialog, which ->
|
||||||
presenter.onSemesterSelected(which)
|
presenter.onSemesterSelected(which)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.details
|
package io.github.wulkanowy.ui.modules.grade.details
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -17,9 +18,10 @@ import io.github.wulkanowy.databinding.HeaderGradeDetailsBinding
|
|||||||
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
|
import io.github.wulkanowy.databinding.ItemGradeDetailsBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseExpandableAdapter
|
import io.github.wulkanowy.ui.base.BaseExpandableAdapter
|
||||||
import io.github.wulkanowy.utils.getBackgroundColor
|
import io.github.wulkanowy.utils.getBackgroundColor
|
||||||
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.BitSet
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() {
|
class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<RecyclerView.ViewHolder>() {
|
||||||
@ -203,7 +205,9 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
|
|||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
gradeItemValue.run {
|
gradeItemValue.run {
|
||||||
text = grade.entry
|
text = grade.entry
|
||||||
setBackgroundResource(grade.getBackgroundColor(gradeColorTheme))
|
backgroundTintList = ColorStateList.valueOf(
|
||||||
|
context.getCompatColor(grade.getBackgroundColor(gradeColorTheme))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
gradeItemDescription.text = when {
|
gradeItemDescription.text = when {
|
||||||
grade.description.isNotBlank() -> grade.description
|
grade.description.isNotBlank() -> grade.description
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user