1
0

Compare commits

...

92 Commits

Author SHA1 Message Date
3486d52a26 Merge branch 'release/0.23.1' 2020-12-16 21:19:52 +01:00
88c5c7d9dc Version 0.23.1 2020-12-16 21:19:47 +01:00
315e9b0595 Fix username label on registers other than default (#1050) 2020-12-16 20:48:01 +01:00
3eba89aeb9 Disable recordException in HMS (#1048) 2020-12-10 21:12:59 +01:00
1680ad233e New translations strings.xml (Czech) (#1049) 2020-12-10 21:12:14 +01:00
93bc4e92a9 Bump kotlin_version from 1.4.20 to 1.4.21 (#1047) 2020-12-09 15:38:06 +00:00
7536f98e6e Merge branch 'release/0.23.0' into develop 2020-12-06 22:21:42 +01:00
afee97a706 Merge branch 'release/0.23.0' 2020-12-06 22:21:36 +01:00
73a92497ed Version 0.23.0 2020-12-06 22:12:18 +01:00
67cef0f6d9 Add register variant auto-matching based on email (#1041) 2020-12-06 21:23:02 +01:00
6ca5e11371 Fix HMS analytics and crashlytics (#1042) 2020-12-06 19:31:35 +01:00
05a597313b Bump hilt_version from 2.29.1-alpha to 2.30.1-alpha (#1035) 2020-12-06 17:54:09 +00:00
33d540e1c9 Bump desugar_jdk_libs from 1.0.10 to 1.1.1 (#1039) 2020-12-06 15:04:43 +00:00
d115372c3b Bump coil from 1.0.0 to 1.1.0 (#1040) 2020-12-06 14:57:50 +00:00
e637896ad3 Bump firebase-crashlytics from 17.2.2 to 17.3.0 (#1038) 2020-12-06 14:50:57 +00:00
ce802cc737 Bump about_libraries from 8.6.2 to 8.6.3 (#1037) 2020-12-06 14:43:48 +00:00
bf342ed289 Bump kotlinx-coroutines-test from 1.4.0 to 1.4.2-native-mt (#1031) 2020-12-06 14:32:08 +00:00
40ec5bbe86 Bump kotlinx-coroutines-android from 1.4.0 to 1.4.2-native-mt (#1032) 2020-12-06 14:29:32 +00:00
41dbd2d25f New Crowdin updates (#1033) 2020-12-06 15:14:58 +01:00
f263b5534a Bump mockk from 1.10.2 to 1.10.3-jdk8 (#1034) 2020-12-06 14:08:26 +00:00
7ed4787496 Bump chucker from 3.3.0 to 3.4.0 (#1027) 2020-12-03 14:19:21 +00:00
1428887204 Bump agconnect-crash from 1.4.1.300 to 1.4.2.301 (#1030) 2020-12-03 13:57:37 +00:00
0fc828f006 Bump agcp from 1.4.1.300 to 1.4.2.301 (#1024) 2020-12-03 13:48:25 +00:00
13906a7d62 Bump about_libraries from 8.4.3 to 8.6.2 (#1025) 2020-12-03 13:47:26 +00:00
3c0dda9a82 Bump hianalytics from 5.0.4.301 to 5.0.5.301 (#1023) 2020-12-03 13:29:49 +00:00
c42333cd35 Bump firebase-crashlytics-gradle from 2.3.0 to 2.4.1 (#1029) 2020-12-03 13:28:10 +00:00
c675dc8b84 Bump kotlin_version from 1.4.10 to 1.4.20 (#1026) 2020-12-03 13:27:47 +00:00
5e9853b043 New Crowdin updates (#1022) 2020-11-21 15:50:18 +01:00
c7fdcc2bbd Add facebook link to about (#1021) 2020-11-17 16:58:24 +01:00
8a00ae95b8 Update contributor's username (#1020) 2020-11-13 23:59:45 +01:00
a1ebf6c6ad Add average in class grades statistics (#1017) 2020-11-11 16:03:52 +01:00
ada5854d10 New Crowdin updates (#1013) 2020-11-02 17:54:02 +01:00
fe191bb0df Bump firebase-analytics from 17.6.0 to 18.0.0 (#1009) 2020-11-01 18:36:55 +00:00
9eb091fbf4 Bump kotlinx-coroutines-android from 1.3.9 to 1.4.0 (#1005) 2020-11-01 18:16:21 +00:00
57e760844f Bump kotlinx-coroutines-test from 1.3.9 to 1.4.0 (#1012) 2020-11-01 18:15:52 +00:00
20644a7a67 Update english strings (#1014) 2020-11-01 19:05:05 +01:00
b3109aed0b Bump firebase-messaging from 20.3.0 to 21.0.0 (#1008) 2020-11-01 16:40:39 +00:00
9ba999feb0 Bump about_libraries from 8.4.2 to 8.4.3 (#1011) 2020-11-01 16:28:32 +00:00
7c9e85793b Bump firebase-inappmessaging-display-ktx from 19.1.1 to 19.1.2 (#1007) 2020-11-01 16:15:17 +00:00
6af8263952 Bump firebase-inappmessaging-ktx from 19.1.1 to 19.1.2 (#1010) 2020-11-01 15:55:51 +00:00
83d1d860a6 Bump coil from 1.0.0-rc3 to 1.0.0 (#1006) 2020-11-01 15:55:36 +00:00
8830240182 Add conferences (#1004) 2020-11-01 16:53:31 +01:00
c3061e75b5 Merge branch 'release/0.22.2' into develop 2020-10-30 12:31:58 +01:00
5f1bb7c1d0 Merge branch 'release/0.22.2' into master 2020-10-30 12:31:50 +01:00
c6f4c868b2 Version 0.22.2 2020-10-30 12:31:41 +01:00
c634c64e70 Update hianalytics to 5.0.4.301 (#1003) 2020-10-30 12:24:00 +01:00
da2b7dbf7e Merge branch 'release/0.22.1' into develop 2020-10-30 01:49:56 +01:00
26267507eb Merge branch 'release/0.22.1' into master 2020-10-30 01:49:42 +01:00
24d0c5057b Version 0.22.1 2020-10-30 01:49:30 +01:00
b05026a6e6 New Crowdin updates (#1002) 2020-10-30 00:54:35 +01:00
8036f3d7f7 Add HMS flavor (#998) 2020-10-29 13:58:56 +01:00
23e309d38e New Crowdin updates (#993) 2020-10-26 11:54:27 +01:00
bf92c6b2e9 Fix grade header item object comparision (#999) 2020-10-25 12:16:19 +01:00
c00b5edaf7 Make AppGallery badge background transparent. (#997) 2020-10-22 16:17:52 +02:00
cb09ca13dc Bump junit from 4.13 to 4.13.1 (#995) 2020-10-15 18:36:31 +00:00
3d68b8e629 Bump gradle from 4.0.2 to 4.1.0 (#996) 2020-10-15 18:36:13 +00:00
b3173581e5 Merge tag '0.22.0' into develop
Version 0.22.0
2020-10-15 17:54:27 +02:00
ddac1d0f98 Merge branch 'release/0.22.0' into master 2020-10-15 17:54:22 +02:00
db6a359bea Version 0.22.0 2020-10-15 17:54:14 +02:00
e7221e6a32 Add Vulcan and Scrapper Exceptions to known exceptions (#994) 2020-10-15 15:55:59 +02:00
db9c2640c7 Add in-app updates support (#914) 2020-10-15 01:00:41 +02:00
ca67e144e4 Add ConnectException and StreamResetException to known exceptions (#992) 2020-10-13 22:32:15 +02:00
da2346ed83 New Crowdin updates (#987) 2020-10-11 21:00:48 +02:00
a87818f3d0 Cancel scheduled lesson notifications which turned into canceled (#991) 2020-10-11 20:51:03 +02:00
5092f8c0bf Reload grade list on mark all as read (#990) 2020-10-11 20:33:18 +02:00
af0787c0b1 Add support for BIG font (#989)
Co-authored-by: Faierbel <RafalBO99@outlook.com>
2020-10-10 02:53:25 +02:00
721b4ac797 Add support for night mode in account switcher dialog (#988) 2020-10-09 21:24:58 +02:00
26a69092cc Add AppGallery badge (#986) 2020-10-07 11:19:34 +02:00
2bd0c75055 Bump about_libraries from 8.3.1 to 8.4.2 (#985) 2020-10-06 21:56:46 +00:00
d6f3c57293 Bump moshi from 1.10.0 to 1.11.0 (#984) 2020-10-06 21:45:38 +00:00
518387e7bb Bump gradle from 4.0.1 to 4.0.2 (#982) 2020-10-06 21:43:56 +00:00
ca6dfbf2d0 Bump core-ktx from 1.3.1 to 1.3.2 (#983) 2020-10-06 21:43:50 +00:00
da6d8a74fd New Crowdin updates (#981) 2020-10-06 23:34:27 +02:00
e35e4ef152 New Crowdin updates (#948) 2020-10-03 13:01:11 +02:00
40fc6ec2e0 Upgrade android sdk to 30 (#966) 2020-10-03 01:08:57 +02:00
b91973aec3 Bump chucker from 3.2.0 to 3.3.0 (#979) 2020-10-01 15:38:29 +00:00
047e70ad46 Bump firebase-crashlytics from 17.2.1 to 17.2.2 (#974) 2020-09-30 20:13:18 +00:00
112c1eb793 Bump moshi from 1.9.3 to 1.10.0 (#972) 2020-09-30 20:13:01 +00:00
c479b31670 Bump coil from 1.0.0-rc2 to 1.0.0-rc3 (#976) 2020-09-30 20:01:45 +00:00
43ed8c8fce Bump firebase-messaging from 20.2.4 to 20.3.0 (#973) 2020-09-30 19:52:02 +00:00
613fa44c27 Bump about_libraries from 8.3.0 to 8.3.1 (#975) 2020-09-30 19:34:53 +00:00
f21216286d Bump mockk from 1.10.0 to 1.10.2 (#977) 2020-09-30 19:34:13 +00:00
7298d0d75a Bump google-services from 4.3.3 to 4.3.4 (#978) 2020-09-30 19:33:22 +00:00
ee0fbcdfd6 Merge tag '0.21.2' into develop
Version 0.21.2
2020-09-29 21:10:17 +02:00
c362ad12c7 Merge branch 'release/0.21.2' into master 2020-09-29 21:10:11 +02:00
8a1a712d6d Version 0.21.2 2020-09-29 21:10:05 +02:00
1f0f6b3e51 Fix string pair list type converter (#971) 2020-09-29 21:02:49 +02:00
11487e77ca Merge tag '0.21.1' into develop
Version 0.21.1
2020-09-29 11:43:58 +02:00
fd0fd4df55 Merge branch 'release/0.21.1' into master 2020-09-29 11:43:54 +02:00
d95a33787b Version 0.21.1 2020-09-29 11:43:49 +02:00
e5661098d9 Fix string pair list type converter (#970) 2020-09-29 11:42:54 +02:00
d020b01794 Merge tag '0.21.0' into develop
Version 0.21.0
2020-09-27 22:12:31 +02:00
141 changed files with 6469 additions and 555 deletions

3
.gitignore vendored
View File

@ -113,3 +113,6 @@ Thumbs.db
!/gradle/wrapper/gradle-wrapper.jar !/gradle/wrapper/gradle-wrapper.jar
.idea/jarRepositories.xml .idea/jarRepositories.xml
app/src/release/agconnect-services.json

View File

@ -3,8 +3,8 @@ jdk: oraclejdk8
env: env:
global: global:
- ANDROID_API_LEVEL=29 - ANDROID_API_LEVEL=30
- ANDROID_BUILD_TOOLS_VERSION=29.0.3 - ANDROID_BUILD_TOOLS_VERSION=30.0.2
cache: cache:
directories: directories:
@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.21.0 - 0.23.1
android: android:
licenses: licenses:
@ -37,6 +37,10 @@ android:
- android-22 - android-22
- sys-img-armeabi-v7a-android-22 - sys-img-armeabi-v7a-android-22
before_install:
- yes | sdkmanager "platforms;android-30"
- yes | sdkmanager "build-tools;30.0.2"
before_script: before_script:
# Launch emulator before the execution # Launch emulator before the execution
- echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
@ -54,6 +58,7 @@ script:
- | - |
if [ $TRAVIS_TAG ]; then if [ $TRAVIS_TAG ]; then
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg; gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/google-services.json.gpg;
gpg --yes --batch --passphrase=$SERVICES_ENCRYPT_KEY ./app/src/release/agconnect-services.json.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/key.p12.gpg;
gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg; gpg --yes --batch --passphrase=$ENCRYPT_KEY ./app/upload-key.jks.gpg;
./gradlew publishPlayRelease -PenableFirebase --stacktrace; ./gradlew publishPlayRelease -PenableFirebase --stacktrace;

View File

@ -32,7 +32,7 @@ Unofficial android VULCAN UONET+ register client for both students and their par
## Download ## Download
You can download the current beta version from the Google Play or the F-Droid store You can download the current beta version from the Google Play, F-Droid or Huawei AppGallery store
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play" alt="Get it on Google Play"
@ -40,6 +40,9 @@ You can download the current beta version from the Google Play or the F-Droid st
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid" alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/io.github.wulkanowy/) height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
[<img src="appgallery_badge.png"
alt="Explore it on AppGallery"
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release You can also download a [development version](https://wulkanowy.github.io/#download) that includes new features being prepared for the next release

View File

@ -32,7 +32,7 @@ Nieoficjalny klient dziennika VULCAN UONET+ dla ucznia i rodzica
## Pobierz ## Pobierz
Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid Aktualną wersję beta możesz pobrać ze sklepu Google Play, F-Droid lub Huawei AppGallery
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" [<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Pobierz z Google Play" alt="Pobierz z Google Play"
@ -40,6 +40,9 @@ Aktualną wersję beta możesz pobrać ze sklepu Google Play lub F-Droid
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" [<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Pobierz z F-Droid" alt="Pobierz z F-Droid"
height="80">](https://f-droid.org/packages/io.github.wulkanowy/) height="80">](https://f-droid.org/packages/io.github.wulkanowy/)
[<img src="appgallery_badge.png"
alt="Odkrywaj w AppGallery"
height="80">](https://appgallery.cloud.huawei.com/ag/n/app/C101440411?channelId=Badge&id=1b3f7fbb700849a9be0dba6b520b2282&s=EB1D3BF9ED9D1564D869B7B94B18016D3CABFCA5AEFB8E29F675FA04E0DC131D&detailType=0&v=)
Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#download), która zawiera nowe funkcje przygotowywane do następnego wydania

View File

@ -10,16 +10,16 @@ apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle' apply from: 'hooks.gradle'
android { android {
compileSdkVersion 29 compileSdkVersion 30
buildToolsVersion '29.0.3' buildToolsVersion '30.0.2'
defaultConfig { defaultConfig {
applicationId "io.github.wulkanowy" applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy" testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 17 minSdkVersion 17
targetSdkVersion 29 targetSdkVersion 30
versionCode 70 versionCode 77
versionName "0.21.0" versionName "0.23.1"
multiDexEnabled true multiDexEnabled true
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -69,12 +69,26 @@ android {
flavorDimensions "platform" flavorDimensions "platform"
productFlavors { productFlavors {
hms {
dimension "platform"
minSdkVersion 19
manifestPlaceholders = [
install_channel: "AppGallery"
]
}
play { play {
dimension "platform" dimension "platform"
manifestPlaceholders = [
install_channel: "Google Play"
]
} }
fdroid { fdroid {
dimension "platform" dimension "platform"
manifestPlaceholders = [
install_channel: "F-Droid"
]
} }
} }
@ -112,14 +126,15 @@ play {
serviceAccountCredentials = file('key.p12') serviceAccountCredentials = file('key.p12')
defaultToAppBundles = false defaultToAppBundles = false
track = 'alpha' track = 'alpha'
updatePriority = 3
} }
ext { ext {
work_manager = "2.4.0" work_manager = "2.4.0"
room = "2.2.5" room = "2.2.5"
chucker = "3.2.0" chucker = "3.4.0"
mockk = "1.10.0" mockk = "1.10.3-jdk8"
moshi = "1.9.3" moshi = "1.11.0"
} }
configurations.all { configurations.all {
@ -127,14 +142,14 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.21.0" implementation "io.github.wulkanowy:sdk:0.23.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2'
implementation "androidx.core:core-ktx:1.3.1" implementation "androidx.core:core-ktx:1.3.2"
implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.appcompat:appcompat:1.2.0" implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.appcompat:appcompat-resources:1.2.0" implementation "androidx.appcompat:appcompat-resources:1.2.0"
@ -179,25 +194,29 @@ dependencies {
implementation "fr.bipi.treessence:treessence:0.3.2" implementation "fr.bipi.treessence:treessence:0.3.2"
implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
implementation 'com.wdullaer:materialdatetimepicker:4.2.3' implementation 'com.wdullaer:materialdatetimepicker:4.2.3'
implementation "io.coil-kt:coil:1.0.0-rc2" implementation "io.coil-kt:coil:1.1.0"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.3.1' implementation 'me.xdrop:fuzzywuzzy:1.3.1'
playImplementation 'com.google.firebase:firebase-analytics:17.5.0' playImplementation 'com.google.firebase:firebase-analytics:18.0.0'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.2'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1" playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.2"
playImplementation 'com.google.firebase:firebase-messaging:20.2.4' playImplementation 'com.google.firebase:firebase-messaging:21.0.0'
playImplementation 'com.google.firebase:firebase-crashlytics:17.2.1' playImplementation 'com.google.firebase:firebase-crashlytics:17.3.0'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' playImplementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
hmsImplementation 'com.huawei.hms:hianalytics:5.0.5.301'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.2.301'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker" releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker" debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
debugImplementation "com.amitshekhar.android:debug-db:1.0.6" debugImplementation "com.amitshekhar.android:debug-db:1.0.6"
testImplementation "junit:junit:4.13" testImplementation "junit:junit:4.13.1"
testImplementation "io.mockk:mockk:$mockk" testImplementation "io.mockk:mockk:$mockk"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.2'
androidTestImplementation "androidx.test:core:1.3.0" androidTestImplementation "androidx.test:core:1.3.0"
androidTestImplementation "androidx.test:runner:1.3.0" androidTestImplementation "androidx.test:runner:1.3.0"
@ -208,3 +227,4 @@ dependencies {
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
@ -27,7 +27,7 @@ class GradeStatisticsLocalTest {
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradeStatistics, testDb.gradePointsStatistics) gradeStatisticsLocal = GradeStatisticsLocal(testDb.gradePartialStatisticsDao, testDb.gradePointsStatisticsDao, testDb.gradeSemesterStatisticsDao)
} }
@After @After
@ -41,9 +41,9 @@ class GradeStatisticsLocalTest {
getGradeStatistics("Matematyka", 2, 1), getGradeStatistics("Matematyka", 2, 1),
getGradeStatistics("Fizyka", 1, 2) getGradeStatistics("Fizyka", 1, 2)
) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradePartialStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() } val stats = runBlocking { gradeStatisticsLocal.getGradePartialStatistics(getSemester()).first() }
assertEquals(1, stats.size) assertEquals(1, stats.size)
assertEquals(stats[0].subject, "Matematyka") assertEquals(stats[0].subject, "Matematyka")
} }
@ -55,12 +55,10 @@ class GradeStatisticsLocalTest {
getGradeStatistics("Chemia", 2, 1), getGradeStatistics("Chemia", 2, 1),
getGradeStatistics("Fizyka", 1, 2) getGradeStatistics("Fizyka", 1, 2)
) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradePartialStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() } val stats = runBlocking { gradeStatisticsLocal.getGradePartialStatistics(getSemester()).first() }
assertEquals(2, stats.size) assertEquals(2, stats.size)
// assertEquals(3, stats.size)
// assertEquals(stats[0].subject, "Wszystkie") // now in main repo
assertEquals(stats[0].subject, "Matematyka") assertEquals(stats[0].subject, "Matematyka")
assertEquals(stats[1].subject, "Chemia") assertEquals(stats[1].subject, "Chemia")
} }
@ -72,9 +70,9 @@ class GradeStatisticsLocalTest {
getGradePointsStatistics("Chemia", 2, 1), getGradePointsStatistics("Chemia", 2, 1),
getGradePointsStatistics("Fizyka", 1, 2) getGradePointsStatistics("Fizyka", 1, 2)
) )
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) } runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(list) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
with(stats[0]) { with(stats[0]) {
assertEquals(subject, "Matematyka") assertEquals(subject, "Matematyka")
assertEquals(others, 5.0) assertEquals(others, 5.0)
@ -84,17 +82,17 @@ class GradeStatisticsLocalTest {
@Test @Test
fun saveAndRead_subjectEmpty() { fun saveAndRead_subjectEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats) assertEquals(emptyList(), stats)
} }
@Test @Test
fun saveAndRead_allEmpty() { fun saveAndRead_allEmpty() {
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) } runBlocking { gradeStatisticsLocal.saveGradePointsStatistics(listOf()) }
val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() } val stats = runBlocking { gradeStatisticsLocal.getGradePointsStatistics(getSemester()).first() }
assertEquals(emptyList(), stats) assertEquals(emptyList(), stats)
} }
@ -102,8 +100,8 @@ class GradeStatisticsLocalTest {
return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1) return Semester(2, 2, "", 2019, 1, 2, now(), now(), 1, 1)
} }
private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradeStatistics { private fun getGradeStatistics(subject: String, studentId: Int, semesterId: Int): GradePartialStatistics {
return GradeStatistics(studentId, semesterId, subject, 5, 5, false) return GradePartialStatistics(studentId, semesterId, subject, "", "", listOf(5), listOf(5))
} }
private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics { private fun getGradePointsStatistics(subject: String, studentId: Int, semesterId: Int): GradePointsStatistics {

View File

@ -0,0 +1,33 @@
{
"agcgw":{
"backurl":"connect-dre.dbankcloud.cn",
"url":"connect-dre.hispace.hicloud.com"
},
"client":{
"cp_id":"890048000024105546",
"product_id":"",
"client_id":"",
"client_secret":"",
"app_id":"101440411",
"package_name":"io.github.wulkanowy.dev",
"api_key":""
},
"service":{
"analytics":{
"collector_url":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn",
"resource_id":"p1",
"channel_id":""
},
"search":{
"url":"https://search-dre.cloud.huawei.com"
},
"cloudstorage":{
"storage_url":"https://ops-dre.agcstorage.link"
},
"ml":{
"mlservice_url":"ml-api-dre.ai.dbankcloud.com,ml-api-dre.ai.dbankcloud.cn"
}
},
"region":"DE",
"configuration_version":"1.0"
}

View File

@ -6,7 +6,7 @@ import javax.inject.Singleton
@Singleton @Singleton
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
class FirebaseAnalyticsHelper @Inject constructor() { class AnalyticsHelper @Inject constructor() {
fun logEvent(name: String, vararg params: Pair<String, Any?>) { fun logEvent(name: String, vararg params: Pair<String, Any?>) {
// do nothing // do nothing
@ -15,4 +15,8 @@ class FirebaseAnalyticsHelper @Inject constructor() {
fun setCurrentScreen(activity: Activity, name: String?) { fun setCurrentScreen(activity: Activity, name: String?) {
// do nothing // do nothing
} }
fun popCurrentScreen(name: String?) {
// do nothing
}
} }

View File

@ -8,6 +8,6 @@ open class TimberTreeNoOp : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
} }
class CrashlyticsTree : TimberTreeNoOp() class CrashLogTree : TimberTreeNoOp()
class CrashlyticsExceptionTree : TimberTreeNoOp() class CrashLogExceptionTree : TimberTreeNoOp()

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.utils
import android.app.Activity
import android.view.View
import javax.inject.Inject
@Suppress("UNUSED_PARAMETER")
class UpdateHelper @Inject constructor() {
lateinit var messageContainer: View
fun checkAndInstallUpdates(activity: Activity) {}
fun onActivityResult(requestCode: Int, resultCode: Int) {}
fun onResume(activity: Activity) {}
}

View File

@ -0,0 +1,39 @@
package io.github.wulkanowy.utils
import android.app.Activity
import android.content.Context
import android.os.Bundle
import com.huawei.hms.analytics.HiAnalytics
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class AnalyticsHelper @Inject constructor(
@ApplicationContext private val context: Context
) {
private val analytics by lazy { HiAnalytics.getInstance(context) }
fun logEvent(name: String, vararg params: Pair<String, Any?>) {
Bundle().apply {
params.forEach {
if (it.second == null) return@forEach
when (it.second) {
is String, is String? -> putString(it.first, it.second as String)
is Int, is Int? -> putInt(it.first, it.second as Int)
is Boolean, is Boolean? -> putBoolean(it.first, it.second as Boolean)
}
}
analytics.onEvent(name, this)
}
}
fun setCurrentScreen(activity: Activity, name: String?) {
analytics.pageStart(name, activity::class.simpleName)
}
fun popCurrentScreen(name: String?) {
analytics.pageEnd(name)
}
}

View File

@ -0,0 +1,53 @@
package io.github.wulkanowy.utils
import android.util.Log
import com.huawei.agconnect.crash.AGConnectCrash
import fr.bipi.tressence.base.FormatterPriorityTree
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import java.io.InterruptedIOException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {
private val connectCrash by lazy { AGConnectCrash.getInstance() }
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (skipLog(priority, tag, message, t)) return
connectCrash.log(format(priority, tag, message))
}
}
class CrashLogExceptionTree : FormatterPriorityTree(Log.ERROR) {
private val connectCrash by lazy { AGConnectCrash.getInstance() }
override fun skipLog(priority: Int, tag: String?, message: String, t: Throwable?): Boolean {
return when (t) {
is FeatureDisabledException,
is FeatureNotAvailableException,
is UnknownHostException,
is SocketTimeoutException,
is InterruptedIOException -> true
else -> super.skipLog(priority, tag, message, t)
}
}
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (skipLog(priority, tag, message, t)) return
// Disabled due to a bug in the Huawei library
/*connectCrash.setCustomKey("priority", priority)
connectCrash.setCustomKey("tag", tag.orEmpty())
connectCrash.setCustomKey("message", message)
if (t != null) {
connectCrash.recordException(t)
} else {
connectCrash.recordException(StackTraceRecorder(format(priority, tag, message)))
}*/
}
}

View File

@ -0,0 +1,17 @@
package io.github.wulkanowy.utils
import android.app.Activity
import android.view.View
import javax.inject.Inject
@Suppress("UNUSED_PARAMETER")
class UpdateHelper @Inject constructor() {
lateinit var messageContainer: View
fun checkAndInstallUpdates(activity: Activity) {}
fun onActivityResult(requestCode: Int, resultCode: Int) {}
fun onResume(activity: Activity) {}
}

View File

@ -110,6 +110,11 @@
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<meta-data
android:name="install_channel"
android:value="${install_channel}">
</meta-data>
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false --> <!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html --> <!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
<provider <provider

View File

@ -33,6 +33,6 @@
}, },
{ {
"displayName": "Mateusz Idziejczak", "displayName": "Mateusz Idziejczak",
"githubUsername": "PanTajemnic" "githubUsername": "Luncenok"
} }
] ]

View File

@ -14,8 +14,8 @@ import fr.bipi.tressence.file.FileLoggerTree
import io.github.wulkanowy.ui.base.ThemeManager import io.github.wulkanowy.ui.base.ThemeManager
import io.github.wulkanowy.utils.ActivityLifecycleLogger import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashlyticsExceptionTree import io.github.wulkanowy.utils.CrashLogExceptionTree
import io.github.wulkanowy.utils.CrashlyticsTree import io.github.wulkanowy.utils.CrashLogTree
import io.github.wulkanowy.utils.DebugLogTree import io.github.wulkanowy.utils.DebugLogTree
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -56,8 +56,8 @@ class WulkanowyApp : Application(), Configuration.Provider {
.build() .build()
) )
} else { } else {
Timber.plant(CrashlyticsExceptionTree()) Timber.plant(CrashLogExceptionTree())
Timber.plant(CrashlyticsTree()) Timber.plant(CrashLogTree())
} }
registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
} }

View File

@ -11,8 +11,8 @@ import com.chuckerteam.chucker.api.RetentionManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
@ -21,7 +21,7 @@ import timber.log.Timber
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
@InstallIn(ApplicationComponent::class) @InstallIn(SingletonComponent::class)
internal class RepositoryModule { internal class RepositoryModule {
@Singleton @Singleton
@ -33,7 +33,11 @@ internal class RepositoryModule {
setSimpleHttpLogger { Timber.d(it) } setSimpleHttpLogger { Timber.d(it) }
// for debug only // for debug only
addInterceptor(ChuckerInterceptor(context, chuckerCollector), true) addInterceptor(ChuckerInterceptor(
context = context,
collector = chuckerCollector,
alwaysReadResponseBody = true
), true)
} }
} }
@ -81,11 +85,15 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideGradeStatisticsDao(database: AppDatabase) = database.gradeStatistics fun provideGradePartialStatisticsDao(database: AppDatabase) = database.gradePartialStatisticsDao
@Singleton @Singleton
@Provides @Provides
fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatistics fun provideGradeSemesterStatisticsDao(database: AppDatabase) = database.gradeSemesterStatisticsDao
@Singleton
@Provides
fun provideGradePointsStatisticsDao(database: AppDatabase) = database.gradePointsStatisticsDao
@Singleton @Singleton
@Provides @Provides
@ -150,4 +158,8 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao fun provideSchoolInfoDao(database: AppDatabase) = database.schoolDao
@Singleton
@Provides
fun provideConferenceDao(database: AppDatabase) = database.conferenceDao
} }

View File

@ -10,10 +10,12 @@ import androidx.room.migration.Migration
import io.github.wulkanowy.data.db.dao.AttendanceDao import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.dao.ConferenceDao
import io.github.wulkanowy.data.db.dao.ExamDao import io.github.wulkanowy.data.db.dao.ExamDao
import io.github.wulkanowy.data.db.dao.GradeDao import io.github.wulkanowy.data.db.dao.GradeDao
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeSummaryDao import io.github.wulkanowy.data.db.dao.GradeSummaryDao
import io.github.wulkanowy.data.db.dao.HomeworkDao import io.github.wulkanowy.data.db.dao.HomeworkDao
import io.github.wulkanowy.data.db.dao.LuckyNumberDao import io.github.wulkanowy.data.db.dao.LuckyNumberDao
@ -32,10 +34,12 @@ 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.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
@ -70,6 +74,8 @@ import io.github.wulkanowy.data.db.migrations.Migration24
import io.github.wulkanowy.data.db.migrations.Migration25 import io.github.wulkanowy.data.db.migrations.Migration25
import io.github.wulkanowy.data.db.migrations.Migration26 import io.github.wulkanowy.data.db.migrations.Migration26
import io.github.wulkanowy.data.db.migrations.Migration27 import io.github.wulkanowy.data.db.migrations.Migration27
import io.github.wulkanowy.data.db.migrations.Migration28
import io.github.wulkanowy.data.db.migrations.Migration29
import io.github.wulkanowy.data.db.migrations.Migration3 import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration4 import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration5 import io.github.wulkanowy.data.db.migrations.Migration5
@ -90,8 +96,9 @@ import javax.inject.Singleton
AttendanceSummary::class, AttendanceSummary::class,
Grade::class, Grade::class,
GradeSummary::class, GradeSummary::class,
GradeStatistics::class, GradePartialStatistics::class,
GradePointsStatistics::class, GradePointsStatistics::class,
GradeSemesterStatistics::class,
Message::class, Message::class,
MessageAttachment::class, MessageAttachment::class,
Note::class, Note::class,
@ -103,7 +110,8 @@ import javax.inject.Singleton
Recipient::class, Recipient::class,
MobileDevice::class, MobileDevice::class,
Teacher::class, Teacher::class,
School::class School::class,
Conference::class,
], ],
version = AppDatabase.VERSION_SCHEMA, version = AppDatabase.VERSION_SCHEMA,
exportSchema = true exportSchema = true
@ -112,7 +120,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 27 const val VERSION_SCHEMA = 29
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> { fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf( return arrayOf(
@ -142,6 +150,8 @@ abstract class AppDatabase : RoomDatabase() {
Migration25(), Migration25(),
Migration26(), Migration26(),
Migration27(), Migration27(),
Migration28(),
Migration29()
) )
} }
@ -171,9 +181,11 @@ abstract class AppDatabase : RoomDatabase() {
abstract val gradeSummaryDao: GradeSummaryDao abstract val gradeSummaryDao: GradeSummaryDao
abstract val gradeStatistics: GradeStatisticsDao abstract val gradePartialStatisticsDao: GradePartialStatisticsDao
abstract val gradePointsStatistics: GradePointsStatisticsDao abstract val gradePointsStatisticsDao: GradePointsStatisticsDao
abstract val gradeSemesterStatisticsDao: GradeSemesterStatisticsDao
abstract val messagesDao: MessagesDao abstract val messagesDao: MessagesDao
@ -198,4 +210,6 @@ abstract class AppDatabase : RoomDatabase() {
abstract val teacherDao: TeacherDao abstract val teacherDao: TeacherDao
abstract val schoolDao: SchoolDao abstract val schoolDao: SchoolDao
abstract val conferenceDao: ConferenceDao
} }

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import com.squareup.moshi.Types import com.squareup.moshi.Types
import io.github.wulkanowy.data.db.adapters.PairAdapterFactory
import java.time.Instant import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDateTime import java.time.LocalDateTime
@ -12,14 +13,14 @@ import java.util.Date
class Converters { class Converters {
private val moshi by lazy { Moshi.Builder().build() } private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() }
private val integerListAdapter by lazy { private val integerListAdapter by lazy {
moshi.adapter<List<Int>>(Types.newParameterizedType(List::class.java, Integer::class.java)) moshi.adapter<List<Int>>(Types.newParameterizedType(List::class.java, Integer::class.java))
} }
private val stringMapAdapter by lazy { private val stringListPairAdapter by lazy {
moshi.adapter<Map<String, String>>(Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java)) moshi.adapter<List<Pair<String, String>>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java))
} }
@TypeConverter @TypeConverter
@ -60,11 +61,11 @@ class Converters {
@TypeConverter @TypeConverter
fun stringPairListToJson(list: List<Pair<String, String>>): String { fun stringPairListToJson(list: List<Pair<String, String>>): String {
return stringMapAdapter.toJson(list.toMap()) return stringListPairAdapter.toJson(list)
} }
@TypeConverter @TypeConverter
fun jsonToStringPairList(value: String): List<Pair<String, String>> { fun jsonToStringPairList(value: String): List<Pair<String, String>> {
return stringMapAdapter.fromJson(value).orEmpty().toList() return stringListPairAdapter.fromJson(value).orEmpty()
} }
} }

View File

@ -0,0 +1,68 @@
package io.github.wulkanowy.data.db.adapters
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
object PairAdapterFactory : JsonAdapter.Factory {
override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
if (type !is ParameterizedType || List::class.java != type.rawType) return null
if (type.actualTypeArguments[0] != Pair::class.java) return null
val listType = Types.newParameterizedType(List::class.java, Map::class.java, String::class.java)
val listAdapter = moshi.adapter<List<Map<String, String>>>(listType)
val mapType = Types.newParameterizedType(MutableMap::class.java, String::class.java, String::class.java)
val mapAdapter = moshi.adapter<Map<String, String>>(mapType)
return PairAdapter(listAdapter, mapAdapter)
}
private class PairAdapter(
private val listAdapter: JsonAdapter<List<Map<String, String>>>,
private val mapAdapter: JsonAdapter<Map<String, String>>,
) : JsonAdapter<List<Pair<String, String>>>() {
override fun toJson(writer: JsonWriter, value: List<Pair<String, String>>?) {
writer.beginArray()
value?.forEach {
writer.beginObject()
writer.name("first").value(it.first)
writer.name("second").value(it.second)
writer.endObject()
}
writer.endArray()
}
override fun fromJson(reader: JsonReader): List<Pair<String, String>>? {
return if (reader.peek() == JsonReader.Token.BEGIN_OBJECT) deserializeMoshiMap(reader)
else deserializeGsonPair(reader)
}
// for compatibility with 0.21.0
private fun deserializeMoshiMap(reader: JsonReader): List<Pair<String, String>>? {
val map = mapAdapter.fromJson(reader) ?: return null
return map.entries.map {
it.key to it.value
}
}
private fun deserializeGsonPair(reader: JsonReader): List<Pair<String, String>>? {
val list = listAdapter.fromJson(reader) ?: return null
return list.map {
require(it.size == 2) {
"pair with more or less than two elements: $list"
}
it["first"].orEmpty() to it["second"].orEmpty()
}
}
}
}

View File

@ -0,0 +1,15 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Conference
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Dao
@Singleton
interface ConferenceDao : BaseDao<Conference> {
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId")
fun loadAll(diaryId: Int, studentId: Int): Flow<List<Conference>>
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import kotlinx.coroutines.flow.Flow
@Dao
interface GradePartialStatisticsDao : BaseDao<GradePartialStatistics> {
@Query("SELECT * FROM GradePartialStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradePartialStatistics>>
}

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import kotlinx.coroutines.flow.Flow
@Dao
interface GradeSemesterStatisticsDao : BaseDao<GradeSemesterStatistics> {
@Query("SELECT * FROM GradeSemesterStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeSemesterStatistics>>
}

View File

@ -1,18 +0,0 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics
import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton
@Singleton
@Dao
interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String, isSemester: Boolean): Flow<List<GradeStatistics>>
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow<List<GradeStatistics>>
}

View File

@ -0,0 +1,35 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
@Entity(tableName = "Conferences")
data class Conference(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "diary_id")
val diaryId: Int,
val title: String,
val subject: String,
val agenda: String,
@ColumnInfo(name = "present_on_conference")
val presentOnConference: String,
@ColumnInfo(name = "conference_id")
val conferenceId: Int,
val date: LocalDateTime
) : Serializable {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "GradePartialStatistics")
data class GradePartialStatistics(
@ColumnInfo(name = "student_id")
val studentId: Int,
@ColumnInfo(name = "semester_id")
val semesterId: Int,
val subject: String,
@ColumnInfo(name = "class_average")
val classAverage: String,
@ColumnInfo(name = "student_average")
val studentAverage: String,
@ColumnInfo(name = "class_amounts")
val classAmounts: List<Int>,
@ColumnInfo(name = "student_amounts")
val studentAmounts: List<Int>
) {
@PrimaryKey(autoGenerate = true)
var id: Long = 0
}

View File

@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "GradesStatistics") @Entity(tableName = "GradeSemesterStatistics")
data class GradeStatistics( data class GradeSemesterStatistics(
@ColumnInfo(name = "student_id") @ColumnInfo(name = "student_id")
val studentId: Int, val studentId: Int,
@ -15,13 +15,14 @@ data class GradeStatistics(
val subject: String, val subject: String,
val grade: Int, val amounts: List<Int>,
val amount: Int, @ColumnInfo(name = "student_grade")
val studentGrade: Int
@ColumnInfo(name = "is_semester")
val semester: Boolean
) { ) {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0
@Transient
var average: String = ""
} }

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration28 : Migration(27, 28) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS Conferences (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
diary_id INTEGER NOT NULL,
title TEXT NOT NULL,
subject TEXT NOT NULL,
agenda TEXT NOT NULL,
present_on_conference TEXT NOT NULL,
conference_id INTEGER NOT NULL,
date INTEGER NOT NULL
)
""")
}
}

View File

@ -0,0 +1,33 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration29 : Migration(28, 29) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS GradesStatistics")
database.execSQL("""
CREATE TABLE IF NOT EXISTS GradeSemesterStatistics (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
semester_id INTEGER NOT NULL,
subject TEXT NOT NULL,
amounts TEXT NOT NULL,
student_grade INTEGER NOT NULL
)
""")
database.execSQL("""
CREATE TABLE IF NOT EXISTS GradePartialStatistics (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
semester_id INTEGER NOT NULL,
subject TEXT NOT NULL,
class_average TEXT NOT NULL,
student_average TEXT NOT NULL,
class_amounts TEXT NOT NULL,
student_amounts TEXT NOT NULL
)
""")
}
}

View File

@ -1,14 +1,19 @@
package io.github.wulkanowy.data.pojos package io.github.wulkanowy.data.pojos
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
data class GradeStatisticsItem( data class GradeStatisticsItem(
val type: ViewType, val type: ViewType,
val partial: List<GradeStatistics>, val average: String,
val partial: GradePartialStatistics?,
val semester: GradeSemesterStatistics?,
val points: GradePointsStatistics? val points: GradePointsStatistics?
) )

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.repositories.conference
import io.github.wulkanowy.data.db.dao.ConferenceDao
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ConferenceLocal @Inject constructor(private val conferenceDb: ConferenceDao) {
fun getConferences(student: Student, semester: Semester): Flow<List<Conference>> {
return conferenceDb.loadAll(semester.diaryId, student.studentId)
}
suspend fun saveConferences(items: List<Conference>) {
conferenceDb.insertAll(items)
}
suspend fun deleteConferences(items: List<Conference>) {
conferenceDb.deleteAll(items)
}
}

View File

@ -0,0 +1,31 @@
package io.github.wulkanowy.data.repositories.conference
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.init
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ConferenceRemote @Inject constructor(private val sdk: Sdk) {
suspend fun getConferences(student: Student, semester: Semester): List<Conference> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getConferences()
.map {
it.agenda
Conference(
studentId = student.studentId,
diaryId = semester.diaryId,
agenda = it.agenda,
conferenceId = it.id,
date = it.date,
presentOnConference = it.presentOnConference,
subject = it.subject,
title = it.title
)
}
}
}

View File

@ -0,0 +1,25 @@
package io.github.wulkanowy.data.repositories.conference
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ConferenceRepository @Inject constructor(
private val local: ConferenceLocal,
private val remote: ConferenceRemote
) {
fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getConferences(student, semester) },
fetch = { remote.getConferences(student, semester) },
saveFetchResult = { old, new ->
local.deleteConferences(old uniqueSubtract new)
local.saveConferences(new uniqueSubtract old)
}
)
}

View File

@ -1,9 +1,11 @@
package io.github.wulkanowy.data.repositories.gradestatistics package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.dao.GradePartialStatisticsDao
import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao import io.github.wulkanowy.data.db.dao.GradePointsStatisticsDao
import io.github.wulkanowy.data.db.dao.GradeStatisticsDao import io.github.wulkanowy.data.db.dao.GradeSemesterStatisticsDao
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
@ -11,31 +13,47 @@ import javax.inject.Singleton
@Singleton @Singleton
class GradeStatisticsLocal @Inject constructor( class GradeStatisticsLocal @Inject constructor(
private val gradeStatisticsDb: GradeStatisticsDao, private val gradePartialStatisticsDb: GradePartialStatisticsDao,
private val gradePointsStatisticsDb: GradePointsStatisticsDao private val gradePointsStatisticsDb: GradePointsStatisticsDao,
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao
) { ) {
fun getGradesStatistics(semester: Semester, isSemester: Boolean): Flow<List<GradeStatistics>> { // partial
return gradeStatisticsDb.loadAll(semester.semesterId, semester.studentId, isSemester) fun getGradePartialStatistics(semester: Semester): Flow<List<GradePartialStatistics>> {
return gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId)
} }
fun getGradesPointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> { suspend fun saveGradePartialStatistics(items: List<GradePartialStatistics>) {
gradePartialStatisticsDb.insertAll(items)
}
suspend fun deleteGradePartialStatistics(items: List<GradePartialStatistics>) {
gradePartialStatisticsDb.deleteAll(items)
}
// points
fun getGradePointsStatistics(semester: Semester): Flow<List<GradePointsStatistics>> {
return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) return gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId)
} }
suspend fun saveGradesStatistics(gradesStatistics: List<GradeStatistics>) { suspend fun saveGradePointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
gradeStatisticsDb.insertAll(gradesStatistics)
}
suspend fun saveGradesPointsStatistics(gradePointsStatistics: List<GradePointsStatistics>) {
gradePointsStatisticsDb.insertAll(gradePointsStatistics) gradePointsStatisticsDb.insertAll(gradePointsStatistics)
} }
suspend fun deleteGradesStatistics(gradesStatistics: List<GradeStatistics>) { suspend fun deleteGradePointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
gradeStatisticsDb.deleteAll(gradesStatistics)
}
suspend fun deleteGradesPointsStatistics(gradesPointsStatistics: List<GradePointsStatistics>) {
gradePointsStatisticsDb.deleteAll(gradesPointsStatistics) gradePointsStatisticsDb.deleteAll(gradesPointsStatistics)
} }
// semester
fun getGradeSemesterStatistics(semester: Semester): Flow<List<GradeSemesterStatistics>> {
return gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId)
}
suspend fun saveGradeSemesterStatistics(items: List<GradeSemesterStatistics>) {
gradeSemesterStatisticsDb.insertAll(items)
}
suspend fun deleteGradeSemesterStatistics(items: List<GradeSemesterStatistics>) {
gradeSemesterStatisticsDb.deleteAll(items)
}
} }

View File

@ -1,7 +1,8 @@
package io.github.wulkanowy.data.repositories.gradestatistics package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
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.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
@ -12,18 +13,36 @@ import javax.inject.Singleton
@Singleton @Singleton
class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) { class GradeStatisticsRemote @Inject constructor(private val sdk: Sdk) {
suspend fun getGradeStatistics(student: Student, semester: Semester, isSemester: Boolean): List<GradeStatistics> { suspend fun getGradePartialStatistics(student: Student, semester: Semester): List<GradePartialStatistics> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).let { return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
if (isSemester) it.getGradesAnnualStatistics(semester.semesterId) .getGradesPartialStatistics(semester.semesterId)
else it.getGradesPartialStatistics(semester.semesterId) .map {
}.map { GradePartialStatistics(
GradeStatistics( semesterId = semester.semesterId,
studentId = student.studentId,
subject = it.subject,
classAverage = it.classAverage,
studentAverage = it.studentAverage,
classAmounts = it.classItems
.sortedBy { item -> item.grade }
.map { item -> item.amount },
studentAmounts = it.studentItems.map { item -> item.amount }
)
}
}
suspend fun getGradeSemesterStatistics(student: Student, semester: Semester): List<GradeSemesterStatistics> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getGradesSemesterStatistics(semester.semesterId)
.map {
GradeSemesterStatistics(
semesterId = semester.semesterId, semesterId = semester.semesterId,
studentId = semester.studentId, studentId = semester.studentId,
subject = it.subject, subject = it.subject,
grade = it.gradeValue, amounts = it.items
amount = it.amount, .sortedBy { item -> item.grade }
semester = isSemester .map { item -> item.amount },
studentGrade = it.items.singleOrNull { item -> item.isStudentHere }?.grade ?: 0
) )
} }
} }

View File

@ -1,13 +1,15 @@
package io.github.wulkanowy.data.repositories.gradestatistics package io.github.wulkanowy.data.repositories.gradestatistics
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
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.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.utils.networkBoundResource import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -17,55 +19,125 @@ class GradeStatisticsRepository @Inject constructor(
private val remote: GradeStatisticsRemote private val remote: GradeStatisticsRemote
) { ) {
fun getGradesStatistics(student: Student, semester: Semester, subjectName: String, isSemester: Boolean, forceRefresh: Boolean) = networkBoundResource( fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh }, shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesStatistics(semester, isSemester) }, query = { local.getGradePartialStatistics(semester) },
fetch = { remote.getGradeStatistics(student, semester, isSemester) }, fetch = { remote.getGradePartialStatistics(student, semester) },
saveFetchResult = { old, new -> saveFetchResult = { old, new ->
local.deleteGradesStatistics(old uniqueSubtract new) local.deleteGradePartialStatistics(old uniqueSubtract new)
local.saveGradesStatistics(new uniqueSubtract old) local.saveGradePartialStatistics(new uniqueSubtract old)
}, },
mapResult = { items -> mapResult = { items ->
when (subjectName) { when (subjectName) {
"Wszystkie" -> items.groupBy { it.grade }.map { "Wszystkie" -> {
GradeStatistics(semester.studentId, semester.semesterId, subjectName, it.key, val numerator = items.map {
it.value.fold(0) { acc, e -> acc + e.amount }, false) it.classAverage.replace(",", ".").toDoubleOrNull() ?: .0
} + items }.filterNot { it == .0 }
(items.reversed() + GradePartialStatistics(
studentId = semester.studentId,
semesterId = semester.semesterId,
subject = subjectName,
classAverage = if (numerator.isEmpty()) "" else numerator.average().let {
"%.2f".format(Locale.FRANCE, it)
},
studentAverage = "",
classAmounts = items.map { it.classAmounts }.sumGradeAmounts(),
studentAmounts = items.map { it.studentAmounts }.sumGradeAmounts()
)).reversed()
}
else -> items.filter { it.subject == subjectName } else -> items.filter { it.subject == subjectName }
}.mapToStatisticItems() }.mapPartialToStatisticItems()
}
)
fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradeSemesterStatistics(semester) },
fetch = { remote.getGradeSemesterStatistics(student, semester) },
saveFetchResult = { old, new ->
local.deleteGradeSemesterStatistics(old uniqueSubtract new)
local.saveGradeSemesterStatistics(new uniqueSubtract old)
},
mapResult = { items ->
val itemsWithAverage = items.map { item ->
item.copy().apply {
val denominator = item.amounts.sum()
average = if (denominator == 0) "" else (item.amounts.mapIndexed { gradeValue, amount ->
(gradeValue + 1) * amount
}.sum().toDouble() / denominator).let {
"%.2f".format(Locale.FRANCE, it)
}
}
}
when (subjectName) {
"Wszystkie" -> (itemsWithAverage.reversed() + GradeSemesterStatistics(
studentId = semester.studentId,
semesterId = semester.semesterId,
subject = subjectName,
amounts = itemsWithAverage.map { it.amounts }.sumGradeAmounts(),
studentGrade = 0
).apply {
average = itemsWithAverage.mapNotNull { it.average.replace(",", ".").toDoubleOrNull() }.average().let {
"%.2f".format(Locale.FRANCE, it)
}
}).reversed()
else -> itemsWithAverage.filter { it.subject == subjectName }
}.mapSemesterToStatisticItems()
} }
) )
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource( fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh }, shouldFetch = { it.isEmpty() || forceRefresh },
query = { local.getGradesPointsStatistics(semester) }, query = { local.getGradePointsStatistics(semester) },
fetch = { remote.getGradePointsStatistics(student, semester) }, fetch = { remote.getGradePointsStatistics(student, semester) },
saveFetchResult = { old, new -> saveFetchResult = { old, new ->
local.deleteGradesPointsStatistics(old uniqueSubtract new) local.deleteGradePointsStatistics(old uniqueSubtract new)
local.saveGradesPointsStatistics(new uniqueSubtract old) local.saveGradePointsStatistics(new uniqueSubtract old)
}, },
mapResult = { items -> mapResult = { items ->
when (subjectName) { when (subjectName) {
"Wszystkie" -> items "Wszystkie" -> items
else -> items.filter { it.subject == subjectName } else -> items.filter { it.subject == subjectName }
}.mapToStatisticsItem() }.mapPointsToStatisticsItems()
} }
) )
private fun List<GradeStatistics>.mapToStatisticItems() = groupBy { it.subject }.map { private fun List<List<Int>>.sumGradeAmounts(): List<Int> {
val result = mutableListOf(0, 0, 0, 0, 0, 0)
forEach {
it.forEachIndexed { grade, amount ->
result[grade] += amount
}
}
return result
}
private fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map {
GradeStatisticsItem( GradeStatisticsItem(
type = ViewType.PARTIAL, type = ViewType.PARTIAL,
partial = it.value average = it.classAverage,
.sortedByDescending { item -> item.grade } partial = it,
.filter { item -> item.amount != 0 }, points = null,
points = null semester = null
) )
} }
private fun List<GradePointsStatistics>.mapToStatisticsItem() = map { private fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
GradeStatisticsItem(
type = ViewType.SEMESTER,
partial = null,
points = null,
average = "",
semester = it
)
}
private fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map {
GradeStatisticsItem( GradeStatisticsItem(
type = ViewType.POINTS, type = ViewType.POINTS,
partial = emptyList(), partial = null,
semester = null,
average = "",
points = it points = it
) )
} }

View File

@ -14,7 +14,7 @@ class TimetableRemote @Inject constructor(private val sdk: Sdk) {
suspend fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Timetable> { suspend fun getTimetable(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Timetable> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getTimetable(startDate, endDate) .getTimetable(startDate, endDate).first
.map { .map {
Timetable( Timetable(
studentId = semester.studentId, studentId = semester.studentId,

View File

@ -6,13 +6,13 @@ import com.yariksoffice.lingver.Lingver
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.DispatchersProvider
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
@InstallIn(ApplicationComponent::class) @InstallIn(SingletonComponent::class)
internal class AppModule { internal class AppModule {
@Singleton @Singleton

View File

@ -9,8 +9,8 @@ import dagger.Binds
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.IntoSet import dagger.multibindings.IntoSet
import io.github.wulkanowy.services.sync.channels.Channel import io.github.wulkanowy.services.sync.channels.Channel
import io.github.wulkanowy.services.sync.channels.DebugChannel import io.github.wulkanowy.services.sync.channels.DebugChannel
@ -38,7 +38,7 @@ import javax.inject.Singleton
@Suppress("unused") @Suppress("unused")
@Module @Module
@InstallIn(ApplicationComponent::class) @InstallIn(SingletonComponent::class)
abstract class ServicesModule { abstract class ServicesModule {
companion object { companion object {

View File

@ -72,18 +72,22 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
withContext(dispatchersProvider.backgroundThread) { withContext(dispatchersProvider.backgroundThread) {
lessons.groupBy { it.date } lessons.groupBy { it.date }
.map { it.value.sortedBy { lesson -> lesson.start } } .map { it.value.sortedBy { lesson -> lesson.start } }
.map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } } .map { it.filter { lesson -> lesson.isStudentPlan } }
.map { day -> .map { day ->
day.forEachIndexed { index, lesson -> val canceled = day.filter { it.canceled }
val intent = createIntent(student, lesson, day.getOrNull(index + 1)) val active = day.filter { !it.canceled }
cancelScheduled(canceled)
active.forEachIndexed { index, lesson ->
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
if (lesson.start > now()) { if (lesson.start > now()) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson)) scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson))
} }
if (lesson.end > now()) { if (lesson.end > now()) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
if (day.lastIndex == index) { if (active.lastIndex == index) {
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
} }
} }

View File

@ -12,8 +12,8 @@ class GradeStatisticsWork @Inject constructor(
override suspend fun doWork(student: Student, semester: Semester) { override suspend fun doWork(student: Student, semester: Semester) {
with(gradeStatisticsRepository) { with(gradeStatisticsRepository) {
getGradesStatistics(student, semester, "Wszystkie", isSemester = true, forceRefresh = true).waitForResult() getGradesPartialStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
getGradesStatistics(student, semester, "Wszystkie", isSemester = false, forceRefresh = true).waitForResult() getGradesSemesterStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult() getGradesPointsStatistics(student, semester, "Wszystkie", forceRefresh = true).waitForResult()
} }
} }

View File

@ -3,10 +3,15 @@ package io.github.wulkanowy.ui.base
import android.widget.Toast import android.widget.Toast
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.lifecycleAwareVariable import io.github.wulkanowy.utils.lifecycleAwareVariable
import javax.inject.Inject
abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView { abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView {
@Inject
lateinit var analyticsHelper: AnalyticsHelper
protected var binding: VB by lifecycleAwareVariable() protected var binding: VB by lifecycleAwareVariable()
override fun showError(text: String, error: Throwable) { override fun showError(text: String, error: Throwable) {
@ -28,4 +33,14 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment(), BaseView
override fun showErrorDetailsDialog(error: Throwable) { override fun showErrorDetailsDialog(error: Throwable) {
ErrorDialog.newInstance(error).show(childFragmentManager, error.toString()) ErrorDialog.newInstance(error).show(childFragmentManager, error.toString())
} }
override fun onResume() {
super.onResume()
analyticsHelper.setCurrentScreen(requireActivity(), this::class.simpleName)
}
override fun onPause() {
super.onPause()
analyticsHelper.popCurrentScreen(this::class.simpleName)
}
} }

View File

@ -22,9 +22,11 @@ import io.github.wulkanowy.utils.getString
import io.github.wulkanowy.utils.openAppInMarket import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser import io.github.wulkanowy.utils.openInternetBrowser
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException import java.io.InterruptedIOException
import java.io.PrintWriter import java.io.PrintWriter
import java.io.StringWriter import java.io.StringWriter
import java.net.ConnectException
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.net.UnknownHostException import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
@ -85,6 +87,8 @@ class ErrorDialog : BaseDialogFragment<DialogErrorBinding>() {
errorDialogReport.isEnabled = when (error) { errorDialogReport.isEnabled = when (error) {
is UnknownHostException, is UnknownHostException,
is InterruptedIOException, is InterruptedIOException,
is ConnectException,
is StreamResetException,
is SocketTimeoutException, is SocketTimeoutException,
is ServiceUnavailableException, is ServiceUnavailableException,
is FeatureDisabledException, is FeatureDisabledException,

View File

@ -58,6 +58,11 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord)) Triple(getString(R.string.about_discord), getString(R.string.about_discord_summary), getCompatDrawable(R.drawable.ic_about_discord))
} }
override val facebookRes: Triple<String, String, Drawable?>?
get() = context?.run {
Triple(getString(R.string.about_facebook), getString(R.string.about_facebook_summary), getCompatDrawable(R.drawable.ic_about_facebook))
}
override val homepageRes: Triple<String, String, Drawable?>? override val homepageRes: Triple<String, String, Drawable?>?
get() = context?.run { get() = context?.run {
Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage)) Triple(getString(R.string.about_homepage), getString(R.string.about_homepage_summary), getCompatDrawable(R.drawable.ic_about_homepage))
@ -113,6 +118,10 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage) context?.openInternetBrowser("https://discord.gg/vccAQBr", ::showMessage)
} }
override fun openFacebookPage() {
context?.openInternetBrowser("https://www.facebook.com/wulkanowy", ::showMessage)
}
override fun openHomepage() { override fun openHomepage() {
context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage) context?.openInternetBrowser("https://wulkanowy.github.io/", ::showMessage)
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.ui.modules.about
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -12,7 +12,7 @@ class AboutPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val appInfo: AppInfo, private val appInfo: AppInfo,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<AboutView>(errorHandler, studentRepository) { ) : BasePresenter<AboutView>(errorHandler, studentRepository) {
override fun onAttachView(view: AboutView) { override fun onAttachView(view: AboutView) {
@ -46,6 +46,11 @@ class AboutPresenter @Inject constructor(
openDiscordInvite() openDiscordInvite()
analytics.logEvent("about_open", "name" to "discord") analytics.logEvent("about_open", "name" to "discord")
} }
facebookRes?.first -> {
Timber.i("Opening facebook")
openFacebookPage()
analytics.logEvent("about_open", "name" to "facebook")
}
homepageRes?.first -> { homepageRes?.first -> {
Timber.i("Opening homepage") Timber.i("Opening homepage")
openHomepage() openHomepage()
@ -78,6 +83,7 @@ class AboutPresenter @Inject constructor(
feedbackRes, feedbackRes,
faqRes, faqRes,
discordRes, discordRes,
facebookRes,
homepageRes, homepageRes,
licensesRes, licensesRes,
privacyRes privacyRes

View File

@ -15,6 +15,8 @@ interface AboutView : BaseView {
val discordRes: Triple<String, String, Drawable?>? val discordRes: Triple<String, String, Drawable?>?
val facebookRes: Triple<String, String, Drawable?>?
val homepageRes: Triple<String, String, Drawable?>? val homepageRes: Triple<String, String, Drawable?>?
val licensesRes: Triple<String, String, Drawable?>? val licensesRes: Triple<String, String, Drawable?>?
@ -31,6 +33,8 @@ interface AboutView : BaseView {
fun openDiscordInvite() fun openDiscordInvite()
fun openFacebookPage()
fun openEmailClient() fun openEmailClient()
fun openFaqPage() fun openFaqPage()

View File

@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
@ -34,7 +34,7 @@ class AttendancePresenter @Inject constructor(
private val attendanceRepository: AttendanceRepository, private val attendanceRepository: AttendanceRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository, private val prefRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<AttendanceView>(errorHandler, studentRepository) { ) : BasePresenter<AttendanceView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().previousOrSameSchoolDay private var baseDate: LocalDate = now().previousOrSameSchoolDay

View File

@ -8,7 +8,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -22,7 +22,7 @@ class AttendanceSummaryPresenter @Inject constructor(
private val attendanceSummaryRepository: AttendanceSummaryRepository, private val attendanceSummaryRepository: AttendanceSummaryRepository,
private val subjectRepository: SubjectRepository, private val subjectRepository: SubjectRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<AttendanceSummaryView>(errorHandler, studentRepository) { ) : BasePresenter<AttendanceSummaryView>(errorHandler, studentRepository) {
private var subjects = emptyList<Subject>() private var subjects = emptyList<Subject>()

View File

@ -0,0 +1,36 @@
package io.github.wulkanowy.ui.modules.conference
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.databinding.ItemConferenceBinding
import io.github.wulkanowy.utils.toFormattedString
import javax.inject.Inject
class ConferenceAdapter @Inject constructor() :
RecyclerView.Adapter<ConferenceAdapter.ItemViewHolder>() {
var items = emptyList<Conference>()
override fun getItemCount() = items.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder(
ItemConferenceBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
val item = items[position]
with(holder.binding) {
conferenceItemDate.text = item.date.toFormattedString("dd.MM.yyyy HH:mm")
conferenceItemName.text = item.presentOnConference
conferenceItemTitle.text = item.title
conferenceItemSubject.text = item.subject
conferenceItemContent.text = item.agenda
conferenceItemContent.visibility = if (item.agenda.isBlank()) View.GONE else View.VISIBLE
}
}
class ItemViewHolder(val binding: ItemConferenceBinding) : RecyclerView.ViewHolder(binding.root)
}

View File

@ -0,0 +1,102 @@
package io.github.wulkanowy.ui.modules.conference
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.databinding.FragmentConferenceBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
import javax.inject.Inject
@AndroidEntryPoint
class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.fragment_conference),
ConferenceView, MainView.TitledView {
@Inject
lateinit var presenter: ConferencePresenter
@Inject
lateinit var conferencesAdapter: ConferenceAdapter
companion object {
fun newInstance() = ConferenceFragment()
}
override val isViewEmpty: Boolean
get() = conferencesAdapter.items.isEmpty()
override val titleStringId: Int
get() = R.string.conferences_title
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentConferenceBinding.bind(view)
messageContainer = binding.conferenceRecycler
presenter.onAttachView(this)
}
override fun initView() {
with(binding.conferenceRecycler) {
layoutManager = LinearLayoutManager(context)
adapter = conferencesAdapter
addItemDecoration(DividerItemDecoration(context))
}
with(binding) {
conferenceSwipe.setOnRefreshListener { presenter.onSwipeRefresh() }
conferenceErrorRetry.setOnClickListener { presenter.onRetry() }
conferenceErrorDetails.setOnClickListener { presenter.onDetailsClick() }
}
}
override fun updateData(data: List<Conference>) {
with(conferencesAdapter) {
items = data
notifyDataSetChanged()
}
}
override fun clearData() {
with(conferencesAdapter) {
items = emptyList()
notifyDataSetChanged()
}
}
override fun hideRefresh() {
binding.conferenceSwipe.isRefreshing = false
}
override fun showProgress(show: Boolean) {
binding.conferenceProgress.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showEmpty(show: Boolean) {
binding.conferenceEmpty.visibility = if (show) View.VISIBLE else View.GONE
}
override fun showErrorView(show: Boolean) {
binding.conferenceError.visibility = if (show) View.VISIBLE else View.GONE
}
override fun setErrorDetails(message: String) {
binding.conferenceErrorMessage.text = message
}
override fun enableSwipe(enable: Boolean) {
binding.conferenceSwipe.isEnabled = enable
}
override fun showContent(show: Boolean) {
binding.conferenceRecycler.visibility = if (show) View.VISIBLE else View.GONE
}
override fun onDestroyView() {
presenter.onDetachView()
super.onDestroyView()
}
}

View File

@ -0,0 +1,96 @@
package io.github.wulkanowy.ui.modules.conference
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.conference.ConferenceRepository
import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import javax.inject.Inject
class ConferencePresenter @Inject constructor(
errorHandler: ErrorHandler,
studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository,
private val conferenceRepository: ConferenceRepository,
private val analytics: AnalyticsHelper
) : BasePresenter<ConferenceView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable
override fun onAttachView(view: ConferenceView) {
super.onAttachView(view)
view.initView()
Timber.i("Conferences view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData()
}
fun onSwipeRefresh() {
loadData(true)
}
fun onRetry() {
view?.run {
showErrorView(false)
showProgress(true)
}
loadData(true)
}
fun onDetailsClick() {
view?.showErrorDetailsDialog(lastError)
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
lastError = error
setErrorDetails(message)
showErrorView(true)
showEmpty(false)
} else showError(message, error)
}
}
private fun loadData(forceRefresh: Boolean = false) {
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
conferenceRepository.getConferences(student, semester, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading conference data started")
Status.SUCCESS -> {
Timber.i("Loading conference result: Success")
view?.run {
updateData(it.data!!.sortedByDescending { conference -> conference.date })
showContent(it.data.isNotEmpty())
showEmpty(it.data.isEmpty())
showErrorView(false)
}
analytics.logEvent(
"load_data",
"type" to "conferences",
"items" to it.data!!.size
)
}
Status.ERROR -> {
Timber.i("Loading conference result: An exception occurred")
errorHandler.dispatch(it.error!!)
}
}
}.afterLoading {
view?.run {
hideRefresh()
showProgress(false)
enableSwipe(true)
}
}.launch()
}
}

View File

@ -0,0 +1,29 @@
package io.github.wulkanowy.ui.modules.conference
import io.github.wulkanowy.data.db.entities.Conference
import io.github.wulkanowy.ui.base.BaseView
interface ConferenceView : BaseView {
val isViewEmpty: Boolean
fun initView()
fun updateData(data: List<Conference>)
fun clearData()
fun hideRefresh()
fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean)
fun setErrorDetails(message: String)
fun showProgress(show: Boolean)
fun enableSwipe(enable: Boolean)
fun showContent(show: Boolean)
}

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
@ -30,7 +30,7 @@ class ExamPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val examRepository: ExamRepository, private val examRepository: ExamRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<ExamView>(errorHandler, studentRepository) { ) : BasePresenter<ExamView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().nextOrSameSchoolDay private var baseDate: LocalDate = now().nextOrSameSchoolDay

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCurrentOrLast import io.github.wulkanowy.utils.getCurrentOrLast
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -18,7 +18,7 @@ class GradePresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<GradeView>(errorHandler, studentRepository) { ) : BasePresenter<GradeView>(errorHandler, studentRepository) {
var selectedIndex = 0 var selectedIndex = 0

View File

@ -40,10 +40,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
} }
fun updateDetailsItem(position: Int, grade: Grade) { fun updateDetailsItem(position: Int, grade: Grade) {
if (items.getOrNull(position)?.viewType != ViewType.ITEM) {
Timber.e("Trying to update item $position on list ${items.size} size, expanded position: $expandedPosition")
return
}
items[position] = GradeDetailsItem(grade, ViewType.ITEM) items[position] = GradeDetailsItem(grade, ViewType.ITEM)
notifyItemChanged(position) notifyItemChanged(position)
} }
@ -62,10 +58,6 @@ class GradeDetailsAdapter @Inject constructor() : BaseExpandableAdapter<Recycler
val headerPosition = headers.indexOf(item) val headerPosition = headers.indexOf(item)
val itemPosition = items.indexOf(item) val itemPosition = items.indexOf(item)
if (headerPosition == NO_POSITION || itemPosition == NO_POSITION) {
Timber.e("Invalid update header positions! Header: $headerPosition, item: $itemPosition")
}
headers[headerPosition] = item headers[headerPosition] = item
items[itemPosition] = item items[itemPosition] = item
notifyItemChanged(itemPosition) notifyItemChanged(itemPosition)

View File

@ -14,6 +14,7 @@ data class GradeDetailsHeader(
val subject: String, val subject: String,
val average: Double?, val average: Double?,
val pointsSum: String?, val pointsSum: String?,
var newGrades: Int,
val grades: List<GradeDetailsItem> val grades: List<GradeDetailsItem>
) ) {
var newGrades = 0
}

View File

@ -13,7 +13,7 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.ALPHABETIC
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
@ -29,7 +29,7 @@ class GradeDetailsPresenter @Inject constructor(
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val averageProvider: GradeAverageProvider, private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<GradeDetailsView>(errorHandler, studentRepository) { ) : BasePresenter<GradeDetailsView>(errorHandler, studentRepository) {
private var newGradesAmount: Int = 0 private var newGradesAmount: Int = 0
@ -81,7 +81,10 @@ class GradeDetailsPresenter @Inject constructor(
}.onEach { }.onEach {
when (it.status) { when (it.status) {
Status.LOADING -> Timber.i("Select mark grades as read") Status.LOADING -> Timber.i("Select mark grades as read")
Status.SUCCESS -> Timber.i("Mark as read result: Success") Status.SUCCESS -> {
Timber.i("Mark as read result: Success")
loadData(currentSemesterId, false)
}
Status.ERROR -> { Status.ERROR -> {
Timber.i("Mark as read result: An exception occurred") Timber.i("Mark as read result: An exception occurred")
errorHandler.dispatch(it.error!!) errorHandler.dispatch(it.error!!)
@ -210,9 +213,10 @@ class GradeDetailsPresenter @Inject constructor(
subject = subject, subject = subject,
average = average, average = average,
pointsSum = points, pointsSum = points,
newGrades = grades.filter { grade -> !grade.isRead }.size,
grades = subItems grades = subItems
), ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems ).apply {
newGrades = grades.filter { grade -> !grade.isRead }.size
}, ViewType.HEADER)) + if (preferencesRepository.isGradeExpandable) emptyList() else subItems
}.flatten() }.flatten()
} }

View File

@ -17,8 +17,9 @@ import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry import com.github.mikephil.charting.data.PieEntry
import com.github.mikephil.charting.formatter.ValueFormatter import com.github.mikephil.charting.formatter.ValueFormatter
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
@ -68,22 +69,32 @@ class GradeStatisticsAdapter @Inject constructor() :
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return when (viewType) { return when (viewType) {
ViewType.PARTIAL.id, ViewType.SEMESTER.id -> PieViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) ViewType.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
ViewType.POINTS.id -> BarViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)) ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false))
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) { when (holder) {
is PieViewHolder -> bindPieChart(holder, items[position].partial) is PartialViewHolder -> bindPartialChart(holder, items[position].partial!!)
is BarViewHolder -> bindBarChart(holder, items[position].points!!) is SemesterViewHolder -> bindSemesterChart(holder, items[position].semester!!)
is PointsViewHolder -> bindBarChart(holder, items[position].points!!)
} }
} }
private fun bindPieChart(holder: PieViewHolder, partials: List<GradeStatistics>) { private fun bindPartialChart(holder: PartialViewHolder, partials: GradePartialStatistics) {
with(holder.binding.gradeStatisticsPieTitle) { bindPieChart(holder.binding, partials.subject, partials.classAverage, partials.classAmounts)
text = partials.firstOrNull()?.subject }
private fun bindSemesterChart(holder: SemesterViewHolder, semester: GradeSemesterStatistics) {
bindPieChart(holder.binding, semester.subject, semester.average, semester.amounts)
}
private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List<Int>) {
with(binding.gradeStatisticsPieTitle) {
text = subject
visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE
} }
@ -92,22 +103,23 @@ class GradeStatisticsAdapter @Inject constructor() :
else -> materialGradeColors else -> materialGradeColors
} }
val dataset = PieDataSet(partials.map { val dataset = PieDataSet(amounts.mapIndexed { grade, amount ->
PieEntry(it.amount.toFloat(), it.grade.toString()) PieEntry(amount.toFloat(), (grade + 1).toString())
}, "Legenda") }.reversed().filterNot { it.value == 0f }, "Legenda")
with(dataset) { with(dataset) {
valueTextSize = 12f valueTextSize = 12f
sliceSpace = 1f sliceSpace = 1f
valueTextColor = Color.WHITE valueTextColor = Color.WHITE
setColors(partials.map { val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }.filterNot { it.second == 0 }
gradeColors.single { color -> color.first == it.grade }.second setColors(grades.reversed().map { (grade, _) ->
}.toIntArray(), holder.binding.root.context) gradeColors.single { color -> color.first == grade }.second
}.toIntArray(), binding.root.context)
} }
with(holder.binding.gradeStatisticsPie) { with(binding.gradeStatisticsPie) {
setTouchEnabled(false) setTouchEnabled(false)
if (partials.size == 1) animateXY(1000, 1000) if (amounts.size == 1) animateXY(1000, 1000)
data = PieData(dataset).apply { data = PieData(dataset).apply {
setValueFormatter(object : ValueFormatter() { setValueFormatter(object : ValueFormatter() {
override fun getPieLabel(value: Float, pieEntry: PieEntry): String { override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
@ -128,8 +140,9 @@ class GradeStatisticsAdapter @Inject constructor() :
minAngleForSlices = 25f minAngleForSlices = 25f
description.isEnabled = false description.isEnabled = false
centerText = partials.fold(0) { acc, it -> acc + it.amount } centerText = amounts.fold(0) { acc, it -> acc + it }
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } +
("\n\nŚrednia: $average").takeIf { average.isNotBlank() }.orEmpty()
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
@ -137,7 +150,7 @@ class GradeStatisticsAdapter @Inject constructor() :
} }
} }
private fun bindBarChart(holder: BarViewHolder, points: GradePointsStatistics) { private fun bindBarChart(holder: PointsViewHolder, points: GradePointsStatistics) {
with(holder.binding.gradeStatisticsBarTitle) { with(holder.binding.gradeStatisticsBarTitle) {
text = points.subject text = points.subject
visibility = if (items.size == 1) GONE else VISIBLE visibility = if (items.size == 1) GONE else VISIBLE
@ -200,9 +213,12 @@ class GradeStatisticsAdapter @Inject constructor() :
} }
} }
private class PieViewHolder(val binding: ItemGradeStatisticsPieBinding) : private class PartialViewHolder(val binding: ItemGradeStatisticsPieBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
private class BarViewHolder(val binding: ItemGradeStatisticsBarBinding) : private class SemesterViewHolder(val binding: ItemGradeStatisticsPieBinding) :
RecyclerView.ViewHolder(binding.root)
private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
} }

View File

@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.subject.SubjectRepository import io.github.wulkanowy.data.repositories.subject.SubjectRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -23,7 +23,7 @@ class GradeStatisticsPresenter @Inject constructor(
private val subjectRepository: SubjectRepository, private val subjectRepository: SubjectRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<GradeStatisticsView>(errorHandler, studentRepository) { ) : BasePresenter<GradeStatisticsView>(errorHandler, studentRepository) {
private var subjects = emptyList<Subject>() private var subjects = emptyList<Subject>()
@ -153,8 +153,8 @@ class GradeStatisticsPresenter @Inject constructor(
with(gradeStatisticsRepository) { with(gradeStatisticsRepository) {
when (type) { when (type) {
ViewType.SEMESTER -> getGradesStatistics(student, semester, currentSubjectName, true, forceRefresh) ViewType.PARTIAL -> getGradesPartialStatistics(student, semester, currentSubjectName, forceRefresh)
ViewType.PARTIAL -> getGradesStatistics(student, semester, currentSubjectName, false, forceRefresh) ViewType.SEMESTER -> getGradesSemesterStatistics(student, semester, currentSubjectName, forceRefresh)
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh) ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh)
} }
} }
@ -164,8 +164,15 @@ class GradeStatisticsPresenter @Inject constructor(
Status.SUCCESS -> { Status.SUCCESS -> {
Timber.i("Loading grade stats result: Success") Timber.i("Loading grade stats result: Success")
view?.run { view?.run {
showEmpty(it.data!!.isEmpty() || it.data.first().partial.isEmpty()) val isNoContent = it.data!!.isEmpty() || when (type) {
showContent(it.data.isNotEmpty() && it.data.first().partial.isNotEmpty()) ViewType.SEMESTER -> it.data.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
ViewType.PARTIAL -> it.data.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
ViewType.POINTS -> it.data.firstOrNull()?.points?.let { points ->
points.student == .0 && points.others == .0
} ?: false
}
showEmpty(isNoContent)
showContent(!isNoContent)
showErrorView(false) showErrorView(false)
updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider import io.github.wulkanowy.ui.modules.grade.GradeAverageProvider
import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage import io.github.wulkanowy.ui.modules.grade.GradeDetailsWithAverage
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -18,7 +18,7 @@ class GradeSummaryPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val averageProvider: GradeAverageProvider, private val averageProvider: GradeAverageProvider,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository) { ) : BasePresenter<GradeSummaryView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable private lateinit var lastError: Throwable

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
@ -29,7 +29,7 @@ class HomeworkPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository, private val homeworkRepository: HomeworkRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<HomeworkView>(errorHandler, studentRepository) { ) : BasePresenter<HomeworkView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay private var baseDate: LocalDate = LocalDate.now().nextOrSameSchoolDay

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
@ -17,7 +17,7 @@ class HomeworkDetailsPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val homeworkRepository: HomeworkRepository, private val homeworkRepository: HomeworkRepository,
private val analytics: FirebaseAnalyticsHelper, private val analytics: AnalyticsHelper,
private val preferencesRepository: PreferencesRepository private val preferencesRepository: PreferencesRepository
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) { ) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) {

View File

@ -14,6 +14,7 @@ import io.github.wulkanowy.ui.modules.login.form.LoginFormFragment
import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment import io.github.wulkanowy.ui.modules.login.recover.LoginRecoverFragment
import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment import io.github.wulkanowy.ui.modules.login.studentselect.LoginStudentSelectFragment
import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
import io.github.wulkanowy.utils.UpdateHelper
import io.github.wulkanowy.utils.setOnSelectPageListener import io.github.wulkanowy.utils.setOnSelectPageListener
import javax.inject.Inject import javax.inject.Inject
@ -25,6 +26,9 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager) private val loginAdapter = BaseFragmentPagerAdapter(supportFragmentManager)
@Inject
lateinit var updateHelper: UpdateHelper
companion object { companion object {
fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java) fun getStartIntent(context: Context) = Intent(context, LoginActivity::class.java)
@ -37,8 +41,20 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root) setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root)
setSupportActionBar(binding.loginToolbar) setSupportActionBar(binding.loginToolbar)
messageContainer = binding.loginContainer messageContainer = binding.loginContainer
updateHelper.messageContainer = binding.loginContainer
presenter.onAttachView(this) presenter.onAttachView(this)
updateHelper.checkAndInstallUpdates(this)
}
override fun onResume() {
super.onResume()
updateHelper.onResume(this)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
} }
override fun initView() { override fun initView() {

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
@ -17,7 +17,7 @@ import javax.inject.Inject
class LoginAdvancedPresenter @Inject constructor( class LoginAdvancedPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<LoginAdvancedView>(loginErrorHandler, studentRepository) { ) : BasePresenter<LoginAdvancedView>(loginErrorHandler, studentRepository) {
override fun onAttachView(view: LoginAdvancedView) { override fun onAttachView(view: LoginAdvancedView) {

View File

@ -89,6 +89,8 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
} }
} }
override fun getHostsValues(): List<String> = hostValues.toList()
override fun setCredentials(username: String, pass: String) { override fun setCredentials(username: String, pass: String) {
with(binding) { with(binding) {
loginFormUsername.setText(username) loginFormUsername.setText(username)
@ -96,6 +98,12 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
} }
} }
override fun setHost(host: String) {
binding.loginFormHost.setText(
hostKeys.getOrNull(hostValues.indexOf(host)).orEmpty()
)
}
override fun setUsernameLabel(label: String) { override fun setUsernameLabel(label: String) {
binding.loginFormUsernameLayout.hint = label binding.loginFormUsernameLayout.hint = label
} }

View File

@ -1,10 +1,11 @@
package io.github.wulkanowy.ui.modules.login.form package io.github.wulkanowy.ui.modules.login.form
import androidx.core.net.toUri
import io.github.wulkanowy.data.Status import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
@ -15,7 +16,7 @@ import javax.inject.Inject
class LoginFormPresenter @Inject constructor( class LoginFormPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository) { ) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository) {
private var lastError: Throwable? = null private var lastError: Throwable? = null
@ -56,7 +57,7 @@ class LoginFormPresenter @Inject constructor(
fun updateUsernameLabel() { fun updateUsernameLabel() {
view?.run { view?.run {
setUsernameLabel(if ("standard" in formHostValue) emailLabel else nicknameLabel) setUsernameLabel(if ("email" !in formHostValue) nicknameLabel else emailLabel)
} }
} }
@ -66,6 +67,16 @@ class LoginFormPresenter @Inject constructor(
fun onUsernameTextChanged() { fun onUsernameTextChanged() {
view?.clearUsernameError() view?.clearUsernameError()
val username = view?.formUsernameValue.orEmpty().trim()
if ("@" in username && "@vulcan" !in username) {
val hosts = view?.getHostsValues().orEmpty().map { it.toUri().host to it }.toMap()
val usernameHost = username.substringAfter("@")
hosts[usernameHost]?.let {
view?.setHost(it)
}
}
} }
fun onSignInClick() { fun onSignInClick() {
@ -135,12 +146,12 @@ class LoginFormPresenter @Inject constructor(
view?.setErrorUsernameRequired() view?.setErrorUsernameRequired()
isCorrect = false isCorrect = false
} else { } else {
if ("@" in login && "standard" !in host) { if ("@" in login && "login" in host) {
view?.setErrorLoginRequired() view?.setErrorLoginRequired()
isCorrect = false isCorrect = false
} }
if ("@" !in login && "standard" in host) { if ("@" !in login && "email" in host) {
view?.setErrorEmailRequired() view?.setErrorEmailRequired()
isCorrect = false isCorrect = false
} }

View File

@ -19,8 +19,12 @@ interface LoginFormView : BaseView {
val emailLabel: String val emailLabel: String
fun getHostsValues(): List<String>
fun setCredentials(username: String, pass: String) fun setCredentials(username: String, pass: String)
fun setHost(host: String)
fun setUsernameLabel(label: String) fun setUsernameLabel(label: String)
fun setErrorUsernameRequired() fun setErrorUsernameRequired()

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.recover.RecoverRepository import io.github.wulkanowy.data.repositories.recover.RecoverRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
@ -15,7 +15,7 @@ import javax.inject.Inject
class LoginRecoverPresenter @Inject constructor( class LoginRecoverPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: RecoverErrorHandler, private val loginErrorHandler: RecoverErrorHandler,
private val analytics: FirebaseAnalyticsHelper, private val analytics: AnalyticsHelper,
private val recoverRepository: RecoverRepository private val recoverRepository: RecoverRepository
) : BasePresenter<LoginRecoverView>(loginErrorHandler, studentRepository) { ) : BasePresenter<LoginRecoverView>(loginErrorHandler, studentRepository) {

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -17,7 +17,7 @@ import javax.inject.Inject
class LoginStudentSelectPresenter @Inject constructor( class LoginStudentSelectPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository) { ) : BasePresenter<LoginStudentSelectView>(loginErrorHandler, studentRepository) {
private var lastError: Throwable? = null private var lastError: Throwable? = null

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.ifNullOrBlank import io.github.wulkanowy.utils.ifNullOrBlank
@ -16,7 +16,7 @@ import javax.inject.Inject
class LoginSymbolPresenter @Inject constructor( class LoginSymbolPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler, private val loginErrorHandler: LoginErrorHandler,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository) { ) : BasePresenter<LoginSymbolView>(loginErrorHandler, studentRepository) {
private var lastError: Throwable? = null private var lastError: Throwable? = null

View File

@ -5,7 +5,7 @@ import io.github.wulkanowy.data.repositories.luckynumber.LuckyNumberRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -16,7 +16,7 @@ class LuckyNumberPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val luckyNumberRepository: LuckyNumberRepository, private val luckyNumberRepository: LuckyNumberRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository) { ) : BasePresenter<LuckyNumberView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable private lateinit var lastError: Throwable

View File

@ -38,8 +38,9 @@ import io.github.wulkanowy.ui.modules.message.MessageFragment
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.timetable.TimetableFragment import io.github.wulkanowy.ui.modules.timetable.TimetableFragment
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.UpdateHelper
import io.github.wulkanowy.utils.dpToPx import io.github.wulkanowy.utils.dpToPx
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.safelyPopFragments import io.github.wulkanowy.utils.safelyPopFragments
@ -54,7 +55,10 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
override lateinit var presenter: MainPresenter override lateinit var presenter: MainPresenter
@Inject @Inject
lateinit var analytics: FirebaseAnalyticsHelper lateinit var analytics: AnalyticsHelper
@Inject
lateinit var updateHelper: UpdateHelper
@Inject @Inject
lateinit var appInfo: AppInfo lateinit var appInfo: AppInfo
@ -100,6 +104,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root) setContentView(ActivityMainBinding.inflate(layoutInflater).apply { binding = this }.root)
setSupportActionBar(binding.mainToolbar) setSupportActionBar(binding.mainToolbar)
messageContainer = binding.mainFragmentContainer messageContainer = binding.mainFragmentContainer
updateHelper.messageContainer = binding.mainFragmentContainer
presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) }) presenter.onAttachView(this, MainView.Section.values().singleOrNull { it.id == intent.getIntExtra(EXTRA_START_MENU, -1) })
@ -107,6 +112,18 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
initialize(startMenuIndex, savedInstanceState) initialize(startMenuIndex, savedInstanceState)
pushFragment(moreMenuFragments[startMenuMoreIndex]) pushFragment(moreMenuFragments[startMenuMoreIndex])
} }
updateHelper.checkAndInstallUpdates(this)
}
override fun onResume() {
super.onResume()
updateHelper.onResume(this)
}
@SuppressLint("NewApi")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts() if (appInfo.systemVersion >= Build.VERSION_CODES.N_MR1) initShortcuts()
} }
@ -165,7 +182,10 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
} }
with(navController) { with(navController) {
setOnViewChangeListener(presenter::onViewChange) setOnViewChangeListener { section, name ->
analytics.setCurrentScreen(this@MainActivity, name)
presenter.onViewChange(section)
}
fragmentHideStrategy = HIDE fragmentHideStrategy = HIDE
rootFragments = listOf( rootFragments = listOf(
GradeFragment.newInstance(), GradeFragment.newInstance(),
@ -177,12 +197,8 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
} }
} }
override fun setCurrentScreen(name: String?) { override fun onOptionsItemSelected(item: MenuItem): Boolean {
analytics.setCurrentScreen(this, name) return if (item.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
return if (item?.itemId == R.id.mainMenuAccount) presenter.onAccountManagerSelected()
else false else false
} }
@ -191,6 +207,7 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
} }
override fun switchMenuView(position: Int) { override fun switchMenuView(position: Int) {
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.switchTab(position) navController.switchTab(position)
} }
@ -228,10 +245,12 @@ class MainActivity : BaseActivity<MainPresenter, ActivityMainBinding>(), MainVie
} }
fun pushView(fragment: Fragment) { fun pushView(fragment: Fragment) {
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.pushFragment(fragment) navController.pushFragment(fragment)
} }
override fun popView(depth: Int) { override fun popView(depth: Int) {
analytics.popCurrentScreen(navController.currentFrag!!::class.simpleName)
navController.safelyPopFragments(depth) navController.safelyPopFragments(depth)
} }

View File

@ -8,7 +8,7 @@ import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE import io.github.wulkanowy.ui.modules.main.MainView.Section.GRADE
import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE import io.github.wulkanowy.ui.modules.main.MainView.Section.MESSAGE
import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL import io.github.wulkanowy.ui.modules.main.MainView.Section.SCHOOL
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -17,7 +17,7 @@ class MainPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val prefRepository: PreferencesRepository, private val prefRepository: PreferencesRepository,
private val syncManager: SyncManager, private val syncManager: SyncManager,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<MainView>(errorHandler, studentRepository) { ) : BasePresenter<MainView>(errorHandler, studentRepository) {
fun onAttachView(view: MainView, initMenu: MainView.Section?) { fun onAttachView(view: MainView, initMenu: MainView.Section?) {
@ -35,9 +35,8 @@ class MainPresenter @Inject constructor(
analytics.logEvent("app_open", "destination" to initMenu?.name) analytics.logEvent("app_open", "destination" to initMenu?.name)
} }
fun onViewChange(section: MainView.Section?, name: String?) { fun onViewChange(section: MainView.Section?) {
view?.apply { view?.apply {
setCurrentScreen(name)
showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL) showActionBarElevation(section != GRADE && section != MESSAGE && section != SCHOOL)
currentViewTitle?.let { setViewTitle(it) } currentViewTitle?.let { setViewTitle(it) }
currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) } currentViewSubtitle?.let { setViewSubTitle(it.ifBlank { null }) }

View File

@ -24,8 +24,6 @@ interface MainView : BaseView {
fun showAccountPicker() fun showAccountPicker()
fun setCurrentScreen(name: String?)
fun showActionBarElevation(show: Boolean) fun showActionBarElevation(show: Boolean)
fun notifyMenuViewReselected() fun notifyMenuViewReselected()

View File

@ -174,7 +174,7 @@ class MessagePreviewFragment :
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun printDocument(html: String, jobName: String) { override fun printDocument(html: String, jobName: String) {
val webView = WebView(activity) val webView = WebView(requireContext())
webView.webViewClient = object : WebViewClient() { webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest) = false

View File

@ -11,7 +11,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
@ -24,7 +24,7 @@ class MessagePreviewPresenter @Inject constructor(
errorHandler: ErrorHandler, errorHandler: ErrorHandler,
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val analytics: FirebaseAnalyticsHelper, private val analytics: AnalyticsHelper,
private var appInfo: AppInfo private var appInfo: AppInfo
) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) { ) : BasePresenter<MessagePreviewView>(errorHandler, studentRepository) {

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.ui.modules.message.send package io.github.wulkanowy.ui.modules.message.send
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
@ -74,6 +75,7 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean) presenter.onAttachView(this, intent.getSerializableExtra(EXTRA_MESSAGE) as? Message, intent.getSerializableExtra(EXTRA_REPLY) as? Boolean)
} }
@SuppressLint("ClickableViewAccessibility")
override fun initView() { override fun initView() {
setUpExtendedHitArea() setUpExtendedHitArea()
with(binding) { with(binding) {
@ -87,8 +89,8 @@ class SendMessageActivity : BaseActivity<SendMessagePresenter, ActivitySendMessa
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return if (item?.itemId == R.id.sendMessageMenuSend) presenter.onSend() return if (item.itemId == R.id.sendMessageMenuSend) presenter.onSend()
else false else false
} }

View File

@ -11,7 +11,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
@ -27,7 +27,7 @@ class SendMessagePresenter @Inject constructor(
private val reportingUnitRepository: ReportingUnitRepository, private val reportingUnitRepository: ReportingUnitRepository,
private val recipientRepository: RecipientRepository, private val recipientRepository: RecipientRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<SendMessageView>(errorHandler, studentRepository) { ) : BasePresenter<SendMessageView>(errorHandler, studentRepository) {
fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) { fun onAttachView(view: SendMessageView, message: Message?, reply: Boolean?) {

View File

@ -8,7 +8,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.toFormattedString import io.github.wulkanowy.utils.toFormattedString
@ -32,7 +32,7 @@ class MessageTabPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val messageRepository: MessageRepository, private val messageRepository: MessageRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<MessageTabView>(errorHandler, studentRepository) { ) : BasePresenter<MessageTabView>(errorHandler, studentRepository) {
lateinit var folder: MessageFolder lateinit var folder: MessageFolder

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
@ -21,7 +21,7 @@ class MobileDevicePresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val mobileDeviceRepository: MobileDeviceRepository, private val mobileDeviceRepository: MobileDeviceRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<MobileDeviceView>(errorHandler, studentRepository) { ) : BasePresenter<MobileDeviceView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable private lateinit var lastError: Throwable

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -18,7 +18,7 @@ class MobileDeviceTokenPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val mobileDeviceRepository: MobileDeviceRepository, private val mobileDeviceRepository: MobileDeviceRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<MobileDeviceTokenVIew>(errorHandler, studentRepository) { ) : BasePresenter<MobileDeviceTokenVIew>(errorHandler, studentRepository) {
override fun onAttachView(view: MobileDeviceTokenVIew) { override fun onAttachView(view: MobileDeviceTokenVIew) {

View File

@ -9,6 +9,7 @@ import io.github.wulkanowy.R
import io.github.wulkanowy.databinding.FragmentMoreBinding import io.github.wulkanowy.databinding.FragmentMoreBinding
import io.github.wulkanowy.ui.base.BaseFragment import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.about.AboutFragment import io.github.wulkanowy.ui.modules.about.AboutFragment
import io.github.wulkanowy.ui.modules.conference.ConferenceFragment
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.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
@ -53,6 +54,9 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
override val mobileDevicesRes: Pair<String, Drawable?>? override val mobileDevicesRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) } get() = context?.run { getString(R.string.mobile_devices_title) to getCompatDrawable(R.drawable.ic_more_mobile_devices) }
override val conferencesRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.conferences_title) to getCompatDrawable(R.drawable.ic_more_conferences) }
override val schoolAndTeachersRes: Pair<String, Drawable?>? override val schoolAndTeachersRes: Pair<String, Drawable?>?
get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) } get() = context?.run { getString(R.string.schoolandteachers_title) to getCompatDrawable((R.drawable.ic_more_schoolandteachers)) }
@ -108,6 +112,10 @@ class MoreFragment : BaseFragment<FragmentMoreBinding>(R.layout.fragment_more),
(activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance()) (activity as? MainActivity)?.pushView(MobileDeviceFragment.newInstance())
} }
override fun openConferencesView() {
(activity as? MainActivity)?.pushView(ConferenceFragment.newInstance())
}
override fun openSchoolAndTeachersView() { override fun openSchoolAndTeachersView() {
(activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance()) (activity as? MainActivity)?.pushView(SchoolAndTeachersFragment.newInstance())
} }

View File

@ -27,6 +27,7 @@ class MorePresenter @Inject constructor(
noteRes?.first -> openNoteView() noteRes?.first -> openNoteView()
luckyNumberRes?.first -> openLuckyNumberView() luckyNumberRes?.first -> openLuckyNumberView()
mobileDevicesRes?.first -> openMobileDevicesView() mobileDevicesRes?.first -> openMobileDevicesView()
conferencesRes?.first -> openConferencesView()
schoolAndTeachersRes?.first -> openSchoolAndTeachersView() schoolAndTeachersRes?.first -> openSchoolAndTeachersView()
settingsRes?.first -> openSettingsView() settingsRes?.first -> openSettingsView()
aboutRes?.first -> openAboutView() aboutRes?.first -> openAboutView()
@ -48,6 +49,7 @@ class MorePresenter @Inject constructor(
noteRes, noteRes,
luckyNumberRes, luckyNumberRes,
mobileDevicesRes, mobileDevicesRes,
conferencesRes,
schoolAndTeachersRes, schoolAndTeachersRes,
settingsRes, settingsRes,
aboutRes aboutRes

View File

@ -15,6 +15,8 @@ interface MoreView : BaseView {
val mobileDevicesRes: Pair<String, Drawable?>? val mobileDevicesRes: Pair<String, Drawable?>?
val conferencesRes: Pair<String, Drawable?>?
val schoolAndTeachersRes: Pair<String, Drawable?>? val schoolAndTeachersRes: Pair<String, Drawable?>?
val settingsRes: Pair<String, Drawable?>? val settingsRes: Pair<String, Drawable?>?
@ -41,5 +43,7 @@ interface MoreView : BaseView {
fun openMobileDevicesView() fun openMobileDevicesView()
fun openConferencesView()
fun openSchoolAndTeachersView() fun openSchoolAndTeachersView()
} }

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResource import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
@ -21,7 +21,7 @@ class NotePresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val noteRepository: NoteRepository, private val noteRepository: NoteRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<NoteView>(errorHandler, studentRepository) { ) : BasePresenter<NoteView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable private lateinit var lastError: Throwable

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -18,7 +18,7 @@ class SchoolPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val schoolRepository: SchoolRepository, private val schoolRepository: SchoolRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<SchoolView>(errorHandler, studentRepository) { ) : BasePresenter<SchoolView>(errorHandler, studentRepository) {
private var address: String? = null private var address: String? = null

View File

@ -6,7 +6,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.teacher.TeacherRepository import io.github.wulkanowy.data.repositories.teacher.TeacherRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -18,7 +18,7 @@ class TeacherPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val teacherRepository: TeacherRepository, private val teacherRepository: TeacherRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<TeacherView>(errorHandler, studentRepository) { ) : BasePresenter<TeacherView>(errorHandler, studentRepository) {
private lateinit var lastError: Throwable private lateinit var lastError: Throwable

View File

@ -9,7 +9,7 @@ import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AppInfo import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.isHolidays import io.github.wulkanowy.utils.isHolidays
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -22,7 +22,7 @@ class SettingsPresenter @Inject constructor(
studentRepository: StudentRepository, studentRepository: StudentRepository,
private val preferencesRepository: PreferencesRepository, private val preferencesRepository: PreferencesRepository,
private val timetableNotificationHelper: TimetableNotificationSchedulerHelper, private val timetableNotificationHelper: TimetableNotificationSchedulerHelper,
private val analytics: FirebaseAnalyticsHelper, private val analytics: AnalyticsHelper,
private val syncManager: SyncManager, private val syncManager: SyncManager,
private val chuckerCollector: ChuckerCollector, private val chuckerCollector: ChuckerCollector,
private val appInfo: AppInfo private val appInfo: AppInfo

View File

@ -9,7 +9,7 @@ import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.data.repositories.timetable.TimetableRepository import io.github.wulkanowy.data.repositories.timetable.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import timber.log.Timber import timber.log.Timber
import java.lang.NullPointerException
import java.time.LocalDate import java.time.LocalDate
import java.time.LocalDate.now import java.time.LocalDate.now
import java.time.LocalDate.of import java.time.LocalDate.of
@ -35,7 +34,7 @@ class TimetablePresenter @Inject constructor(
private val timetableRepository: TimetableRepository, private val timetableRepository: TimetableRepository,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository, private val prefRepository: PreferencesRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<TimetableView>(errorHandler, studentRepository) { ) : BasePresenter<TimetableView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().nextOrSameSchoolDay private var baseDate: LocalDate = now().nextOrSameSchoolDay

View File

@ -7,7 +7,7 @@ import io.github.wulkanowy.data.repositories.completedlessons.CompletedLessonsRe
import io.github.wulkanowy.data.repositories.semester.SemesterRepository import io.github.wulkanowy.data.repositories.semester.SemesterRepository
import io.github.wulkanowy.data.repositories.student.StudentRepository import io.github.wulkanowy.data.repositories.student.StudentRepository
import io.github.wulkanowy.ui.base.BasePresenter import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.afterLoading import io.github.wulkanowy.utils.afterLoading
import io.github.wulkanowy.utils.flowWithResourceIn import io.github.wulkanowy.utils.flowWithResourceIn
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
@ -30,7 +30,7 @@ class CompletedLessonsPresenter @Inject constructor(
private val completedLessonsErrorHandler: CompletedLessonsErrorHandler, private val completedLessonsErrorHandler: CompletedLessonsErrorHandler,
private val semesterRepository: SemesterRepository, private val semesterRepository: SemesterRepository,
private val completedLessonsRepository: CompletedLessonsRepository, private val completedLessonsRepository: CompletedLessonsRepository,
private val analytics: FirebaseAnalyticsHelper private val analytics: AnalyticsHelper
) : BasePresenter<CompletedLessonsView>(completedLessonsErrorHandler, studentRepository) { ) : BasePresenter<CompletedLessonsView>(completedLessonsErrorHandler, studentRepository) {
private var baseDate: LocalDate = now().nextOrSameSchoolDay private var baseDate: LocalDate = now().nextOrSameSchoolDay

View File

@ -24,7 +24,7 @@ import io.github.wulkanowy.services.HiltBroadcastReceiver
import io.github.wulkanowy.services.widgets.TimetableWidgetService import io.github.wulkanowy.services.widgets.TimetableWidgetService
import io.github.wulkanowy.ui.modules.main.MainActivity import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.FirebaseAnalyticsHelper import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.nextOrSameSchoolDay import io.github.wulkanowy.utils.nextOrSameSchoolDay
import io.github.wulkanowy.utils.nextSchoolDay import io.github.wulkanowy.utils.nextSchoolDay
import io.github.wulkanowy.utils.previousSchoolDay import io.github.wulkanowy.utils.previousSchoolDay
@ -49,7 +49,7 @@ class TimetableWidgetProvider : HiltBroadcastReceiver() {
lateinit var sharedPref: SharedPrefProvider lateinit var sharedPref: SharedPrefProvider
@Inject @Inject
lateinit var analytics: FirebaseAnalyticsHelper lateinit var analytics: AnalyticsHelper
companion object { companion object {

View File

@ -22,32 +22,32 @@ private fun Bundle?.checkSavedState() = if (this == null) "(STATE IS NULL)" else
class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks { class ActivityLifecycleLogger : Application.ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity?) { override fun onActivityPaused(activity: Activity) {
activity?.let { Timber.d("${it::class.java.simpleName} PAUSED") } Timber.d("${activity::class.java.simpleName} PAUSED")
} }
override fun onActivityResumed(activity: Activity?) { override fun onActivityResumed(activity: Activity) {
activity?.let { Timber.d("${it::class.java.simpleName} RESUMED") } Timber.d("${activity::class.java.simpleName} RESUMED")
} }
override fun onActivityStarted(activity: Activity?) { override fun onActivityStarted(activity: Activity) {
activity?.let { Timber.d("${it::class.java.simpleName} STARTED") } Timber.d("${activity::class.java.simpleName} STARTED")
} }
override fun onActivityDestroyed(activity: Activity?) { override fun onActivityDestroyed(activity: Activity) {
activity?.let { Timber.d("${it::class.java.simpleName} DESTROYED") } Timber.d("${activity::class.java.simpleName} DESTROYED")
} }
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
activity?.let { Timber.d("${it::class.java.simpleName} SAVED INSTANCE STATE") } Timber.d("${activity::class.java.simpleName} SAVED INSTANCE STATE")
} }
override fun onActivityStopped(activity: Activity?) { override fun onActivityStopped(activity: Activity) {
activity?.let { Timber.d("${it::class.java.simpleName} STOPPED") } Timber.d("${activity::class.java.simpleName} STOPPED")
} }
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activity?.let { Timber.d("${it::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}") } Timber.d("${activity::class.java.simpleName} CREATED ${savedInstanceState.checkSavedState()}")
} }
} }

View File

@ -4,20 +4,26 @@ import android.content.res.Resources
import io.github.wulkanowy.R import io.github.wulkanowy.R
import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException import io.github.wulkanowy.sdk.exception.FeatureNotAvailableException
import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException import io.github.wulkanowy.sdk.scrapper.exception.FeatureDisabledException
import io.github.wulkanowy.sdk.scrapper.exception.ScrapperException
import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException import io.github.wulkanowy.sdk.scrapper.exception.ServiceUnavailableException
import io.github.wulkanowy.sdk.scrapper.exception.VulcanException
import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException import io.github.wulkanowy.sdk.scrapper.login.NotLoggedInException
import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.SocketTimeoutException import java.net.SocketTimeoutException
import java.net.UnknownHostException import java.net.UnknownHostException
fun Resources.getString(error: Throwable) = when (error) { fun Resources.getString(error: Throwable) = when (error) {
is UnknownHostException -> getString(R.string.error_no_internet) is UnknownHostException -> getString(R.string.error_no_internet)
is SocketTimeoutException, is InterruptedIOException -> getString(R.string.error_timeout) is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout)
is NotLoggedInException -> getString(R.string.error_login_failed) is NotLoggedInException -> getString(R.string.error_login_failed)
is PasswordChangeRequiredException -> getString(R.string.error_password_change_required) is PasswordChangeRequiredException -> getString(R.string.error_password_change_required)
is ServiceUnavailableException -> getString(R.string.error_service_unavailable) is ServiceUnavailableException -> getString(R.string.error_service_unavailable)
is FeatureDisabledException -> getString(R.string.error_feature_disabled) is FeatureDisabledException -> getString(R.string.error_feature_disabled)
is FeatureNotAvailableException -> getString(R.string.error_feature_not_available) is FeatureNotAvailableException -> getString(R.string.error_feature_not_available)
is VulcanException -> getString(R.string.error_unknown_uonet)
is ScrapperException -> getString(R.string.error_unknown_app)
else -> getString(R.string.error_unknown) else -> getString(R.string.error_unknown)
} }

View File

@ -1,10 +1,5 @@
Wersja 0.21.0 Wersja 0.23.1
- naprawiliśmy logowanie do tarnowskiego dziennika - naprawiliśmy opis pola na email/login przy wyborze niestandardowych dzienników
- naprawiliśmy wyświetlanie podsumowania punktów klasy - naprawiliśmy obsługę dziennika lubelskiego i koszalińskiego na starszych urządzeniach (z Androidem 5 i starszymi)
- dodaliśmy skróty aplikacji
- dodaliśmy opcję pokazywania nazwy grupy w planie lekcji na liście
- dodaliśmy opcję pokazywania w ocenach przedmiotów bez ocen
- dodaliśmy dodatkowe opcje sortowania ocen
- dodaliśmy rozróżnianie powiadomień dla pochwał i uwag
Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF"
android:fillType="nonZero"
android:pathData="M12,2C6.4883,2 2,6.4883 2,12C2,17.5117 6.4883,22 12,22C17.5117,22 22,17.5117 22,12C22,6.4883 17.5117,2 12,2ZM12,4C16.4297,4 20,7.5703 20,12C20,16.0156 17.0664,19.3125 13.2188,19.8984L13.2188,14.3828L15.5469,14.3828L15.9102,12.0195L13.2188,12.0195L13.2188,10.7266C13.2188,9.7422 13.5391,8.8711 14.457,8.8711L15.9336,8.8711L15.9336,6.8047C15.6758,6.7734 15.125,6.6953 14.0898,6.6953C11.9258,6.6953 10.6523,7.8398 10.6523,10.4453L10.6523,12.0195L8.4258,12.0195L8.4258,14.3828L10.6523,14.3828L10.6523,19.8789C6.8711,19.2422 4,15.9688 4,12C4,7.5703 7.5703,4 12,4ZM12,4" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M9,13.75c-2.34,0 -7,1.17 -7,3.5L2,19h14v-1.75c0,-2.33 -4.66,-3.5 -7,-3.5zM4.34,17c0.84,-0.58 2.87,-1.25 4.66,-1.25s3.82,0.67 4.66,1.25L4.34,17zM9,12c1.93,0 3.5,-1.57 3.5,-3.5S10.93,5 9,5 5.5,6.57 5.5,8.5 7.07,12 9,12zM9,7c0.83,0 1.5,0.67 1.5,1.5S9.83,10 9,10s-1.5,-0.67 -1.5,-1.5S8.17,7 9,7zM16.04,13.81c1.16,0.84 1.96,1.96 1.96,3.44L18,19h4v-1.75c0,-2.02 -3.5,-3.17 -5.96,-3.44zM15,12c1.93,0 3.5,-1.57 3.5,-3.5S16.93,5 15,5c-0.54,0 -1.04,0.13 -1.5,0.35 0.63,0.89 1,1.98 1,3.15s-0.37,2.26 -1,3.15c0.46,0.22 0.96,0.35 1.5,0.35z" />
</vector>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/mainContainer" android:id="@+id/mainContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -10,18 +10,19 @@
style="@style/Widget.MaterialComponents.Toolbar.Surface" style="@style/Widget.MaterialComponents.Toolbar.Surface"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:contentInsetStartWithNavigation="0dp" /> app:contentInsetStartWithNavigation="0dp"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/mainFragmentContainer" android:id="@+id/mainFragmentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_marginTop="?actionBarSize" app:layout_constraintBottom_toTopOf="@id/mainBottomNav"
android:layout_marginBottom="@dimen/bottom_navigation_height" /> app:layout_constraintTop_toBottomOf="@id/mainToolbar" />
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation <com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="@+id/mainBottomNav" android:id="@+id/mainBottomNav"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" /> app:layout_constraintBottom_toBottomOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,104 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.modules.conference.ConferenceFragment">
<me.zhanghai.android.materialprogressbar.MaterialProgressBar
android:id="@+id/conferenceProgress"
style="@style/Widget.MaterialProgressBar.ProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/conferenceSwipe"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/conferenceRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_conference" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<LinearLayout
android:id="@+id/conferenceEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:visibility="gone"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_more_conferences"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/conference_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/conferenceError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/conferenceErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/conferenceErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/conferenceErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Some files were not shown because too many files have changed in this diff Show More