forked from github/wulkanowy-mirror
Compare commits
140 Commits
Author | SHA1 | Date | |
---|---|---|---|
afee97a706 | |||
73a92497ed | |||
67cef0f6d9 | |||
6ca5e11371 | |||
05a597313b | |||
33d540e1c9 | |||
d115372c3b | |||
e637896ad3 | |||
ce802cc737 | |||
bf342ed289 | |||
40ec5bbe86 | |||
41dbd2d25f | |||
f263b5534a | |||
7ed4787496 | |||
1428887204 | |||
0fc828f006 | |||
13906a7d62 | |||
3c0dda9a82 | |||
c42333cd35 | |||
c675dc8b84 | |||
5e9853b043 | |||
c7fdcc2bbd | |||
8a00ae95b8 | |||
a1ebf6c6ad | |||
ada5854d10 | |||
fe191bb0df | |||
9eb091fbf4 | |||
57e760844f | |||
20644a7a67 | |||
b3109aed0b | |||
9ba999feb0 | |||
7c9e85793b | |||
6af8263952 | |||
83d1d860a6 | |||
8830240182 | |||
c3061e75b5 | |||
5f1bb7c1d0 | |||
c6f4c868b2 | |||
c634c64e70 | |||
da2b7dbf7e | |||
26267507eb | |||
24d0c5057b | |||
b05026a6e6 | |||
8036f3d7f7 | |||
23e309d38e | |||
bf92c6b2e9 | |||
c00b5edaf7 | |||
cb09ca13dc | |||
3d68b8e629 | |||
b3173581e5 | |||
ddac1d0f98 | |||
db6a359bea | |||
e7221e6a32 | |||
db9c2640c7 | |||
ca67e144e4 | |||
da2346ed83 | |||
a87818f3d0 | |||
5092f8c0bf | |||
af0787c0b1 | |||
721b4ac797 | |||
26a69092cc | |||
2bd0c75055 | |||
d6f3c57293 | |||
518387e7bb | |||
ca6dfbf2d0 | |||
da6d8a74fd | |||
e35e4ef152 | |||
40fc6ec2e0 | |||
b91973aec3 | |||
047e70ad46 | |||
112c1eb793 | |||
c479b31670 | |||
43ed8c8fce | |||
613fa44c27 | |||
f21216286d | |||
7298d0d75a | |||
ee0fbcdfd6 | |||
c362ad12c7 | |||
8a1a712d6d | |||
1f0f6b3e51 | |||
11487e77ca | |||
fd0fd4df55 | |||
d95a33787b | |||
e5661098d9 | |||
d020b01794 | |||
d8b1264024 | |||
cddd17650b | |||
a0f9c70036 | |||
2e05416fb5 | |||
d32ebd66de | |||
c6a99f1000 | |||
bafe52e310 | |||
e08abc1fc2 | |||
2a74b11cce | |||
b0b3ccfd53 | |||
6c68456f7a | |||
3e8e9b4ecc | |||
d6ebc343d5 | |||
73be416807 | |||
0cb65a29ba | |||
13198f2ab4 | |||
cd92f37435 | |||
5d8fb376ab | |||
47150364d8 | |||
792b123598 | |||
acf5c8e9ba | |||
53561668fc | |||
7cfe58d311 | |||
cd51fac621 | |||
adde5541e2 | |||
6e56d3ff06 | |||
ec761f6329 | |||
6363c90e37 | |||
c30f105be5 | |||
9f85b2206a | |||
42515fd084 | |||
9a7c04fe7b | |||
debb21f5f9 | |||
18b9bf42e1 | |||
6ded83d132 | |||
71d37a1c6c | |||
3975d06cde | |||
ee168bafe0 | |||
42ed7e0ae1 | |||
0e92447974 | |||
40492e6c01 | |||
69a1193154 | |||
0f65af8958 | |||
2ad1d086e0 | |||
f8b7baef24 | |||
90be9d1add | |||
20f931c5cc | |||
9997b1adbb | |||
eb616eedc7 | |||
a5de39a366 | |||
57bc2b2533 | |||
d1ce16d2b1 | |||
54fb01cd0d | |||
370cfbf22a | |||
d198a2ba21 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||||
|
31
.travis.yml
31
.travis.yml
@ -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.20.0
|
- 0.23.0
|
||||||
|
|
||||||
android:
|
android:
|
||||||
licenses:
|
licenses:
|
||||||
@ -28,32 +28,37 @@ android:
|
|||||||
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
|
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
|
||||||
# The SDK version used to compile your project
|
# The SDK version used to compile your project
|
||||||
- android-$ANDROID_API_LEVEL
|
- android-$ANDROID_API_LEVEL
|
||||||
# Additional components
|
# Additional components
|
||||||
- extra-google-google_play_services
|
- extra-google-google_play_services
|
||||||
- extra-google-m2repository
|
- extra-google-m2repository
|
||||||
- extra-android-m2repository
|
- extra-android-m2repository
|
||||||
- addon-google_apis-google-$ANDROID_API_LEVEL
|
- addon-google_apis-google-$ANDROID_API_LEVEL
|
||||||
# Android emulator
|
# Android emulator
|
||||||
- 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
|
||||||
- emulator -avd test -no-audio -no-window &
|
- emulator -avd test -no-audio -no-window &
|
||||||
- android-wait-for-emulator
|
- android-wait-for-emulator
|
||||||
- adb shell input keyevent 82 &
|
- adb shell input keyevent 82 &
|
||||||
- "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash"
|
- "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | sudo bash"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./gradlew dependencies --stacktrace --daemon
|
- ./gradlew dependencies --stacktrace --daemon
|
||||||
- fossa --no-ansi || true
|
- fossa --no-ansi || true
|
||||||
- ./gradlew -Pcoverage testPlayDebugUnitTest --stacktrace --daemon
|
- ./gradlew -Pcoverage testFdroidDebugUnitTest --stacktrace --daemon
|
||||||
- ./gradlew -Pcoverage createFdroidDebugCoverageReport --stacktrace --daemon
|
- ./gradlew -Pcoverage connectedFdroidDebugAndroidTest --stacktrace --daemon
|
||||||
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
|
- ./gradlew -Pcoverage jacocoTestReport --stacktrace --daemon
|
||||||
- |
|
- |
|
||||||
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;
|
||||||
|
10
README.en.md
10
README.en.md
@ -32,14 +32,17 @@ 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"
|
||||||
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
[<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
|
||||||
|
|
||||||
@ -47,7 +50,6 @@ You can also download a [development version](https://wulkanowy.github.io/#downl
|
|||||||
|
|
||||||
|
|
||||||
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
* [Wulkanowy SDK](https://github.com/wulkanowy/sdk)
|
||||||
* [RxJava 2](https://github.com/ReactiveX/RxJava)
|
|
||||||
* [Dagger 2](https://github.com/google/dagger)
|
* [Dagger 2](https://github.com/google/dagger)
|
||||||
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
10
README.md
10
README.md
@ -32,14 +32,17 @@ 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"
|
||||||
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
height="80">](https://play.google.com/store/apps/details?id=io.github.wulkanowy)
|
||||||
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
[<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
|
||||||
@ -48,7 +51,6 @@ Możesz także pobrać [wersję rozwojową](https://wulkanowy.github.io/#downloa
|
|||||||
## Zbudowana za pomocą
|
## Zbudowana za pomocą
|
||||||
|
|
||||||
* [Wulkanowy SDK](https://github.com/wulkanowy/SDK)
|
* [Wulkanowy SDK](https://github.com/wulkanowy/SDK)
|
||||||
* [RxJava 2](https://github.com/ReactiveX/RxJava)
|
|
||||||
* [Dagger 2](https://github.com/google/dagger)
|
* [Dagger 2](https://github.com/google/dagger)
|
||||||
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
* [Room](https://developer.android.com/topic/libraries/architecture/room)
|
||||||
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
* [WorkManager](https://developer.android.com/topic/libraries/architecture/workmanager)
|
||||||
|
@ -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 64
|
versionCode 76
|
||||||
versionName "0.20.0"
|
versionName "0.23.0"
|
||||||
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,13 +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.11.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.all {
|
configurations.all {
|
||||||
@ -126,14 +142,14 @@ configurations.all {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "io.github.wulkanowy:sdk:0.20.0"
|
implementation "io.github.wulkanowy:sdk:0.23.0"
|
||||||
|
|
||||||
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"
|
||||||
@ -145,9 +161,9 @@ dependencies {
|
|||||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||||
implementation "androidx.viewpager:viewpager:1.0.0"
|
implementation "androidx.viewpager:viewpager:1.0.0"
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:1.1.3"
|
implementation "androidx.constraintlayout:constraintlayout:2.0.1"
|
||||||
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
|
||||||
implementation "com.google.android.material:material:1.1.0"
|
implementation "com.google.android.material:material:1.2.1"
|
||||||
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
|
implementation "com.github.wulkanowy:material-chips-input:2.1.1"
|
||||||
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
|
||||||
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
implementation "me.zhanghai.android.materialprogressbar:library:1.6.1"
|
||||||
@ -170,34 +186,40 @@ dependencies {
|
|||||||
implementation "com.ncapdevi:frag-nav:3.3.0"
|
implementation "com.ncapdevi:frag-nav:3.3.0"
|
||||||
implementation "com.github.YarikSOffice:lingver:1.2.2"
|
implementation "com.github.YarikSOffice:lingver:1.2.2"
|
||||||
|
|
||||||
implementation "com.google.code.gson:gson:2.8.6"
|
implementation "com.squareup.moshi:moshi:$moshi"
|
||||||
|
implementation "com.squareup.moshi:moshi-adapters:$moshi"
|
||||||
|
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi"
|
||||||
implementation "com.jakewharton.timber:timber:4.7.1"
|
implementation "com.jakewharton.timber:timber:4.7.1"
|
||||||
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
implementation "at.favre.lib:slf4j-timber:1.0.1"
|
||||||
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-rc1"
|
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.0'
|
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.1.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.2.0"
|
androidTestImplementation "androidx.test:core:1.3.0"
|
||||||
androidTestImplementation "androidx.test:runner:1.2.0"
|
androidTestImplementation "androidx.test:runner:1.3.0"
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
||||||
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
androidTestImplementation "io.mockk:mockk-android:$mockk"
|
||||||
androidTestImplementation "androidx.room:room-testing:$room"
|
androidTestImplementation "androidx.room:room-testing:$room"
|
||||||
@ -205,3 +227,4 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: 'com.google.gms.google-services'
|
apply plugin: 'com.google.gms.google-services'
|
||||||
|
apply plugin: 'com.huawei.agconnect'
|
||||||
|
@ -35,13 +35,13 @@ task jacocoTestReport(type: JacocoReport) {
|
|||||||
dir: "$buildDir/intermediates/classes/debug",
|
dir: "$buildDir/intermediates/classes/debug",
|
||||||
excludes: excludes
|
excludes: excludes
|
||||||
) + fileTree(
|
) + fileTree(
|
||||||
dir: "$buildDir/tmp/kotlin-classes/playDebug",
|
dir: "$buildDir/tmp/kotlin-classes/fdroidDebug",
|
||||||
excludes: excludes
|
excludes: excludes
|
||||||
))
|
))
|
||||||
|
|
||||||
sourceDirectories.setFrom(files([
|
sourceDirectories.setFrom(files([
|
||||||
"src/main/java",
|
"src/main/java",
|
||||||
"src/play/java"
|
"src/fdroid/java"
|
||||||
]))
|
]))
|
||||||
executionData.setFrom(fileTree(
|
executionData.setFrom(fileTree(
|
||||||
dir: project.projectDir,
|
dir: project.projectDir,
|
||||||
|
1842
app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json
Normal file
1842
app/schemas/io.github.wulkanowy.data.db.AppDatabase/28.json
Normal file
File diff suppressed because it is too large
Load Diff
1898
app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json
Normal file
1898
app/schemas/io.github.wulkanowy.data.db.AppDatabase/29.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||||
|
33
app/src/debug/agconnect-services.json
Normal file
33
app/src/debug/agconnect-services.json
Normal 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"
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
@ -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()
|
@ -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) {}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
52
app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
Normal file
52
app/src/hms/java/io/github/wulkanowy/utils/CrashLogUtils.kt
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package io.github.wulkanowy.utils
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.huawei.agconnect.crash.AGConnectCrash
|
||||||
|
import fr.bipi.tressence.base.FormatterPriorityTree
|
||||||
|
import fr.bipi.tressence.common.StackTraceRecorder
|
||||||
|
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
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal file
17
app/src/hms/java/io/github/wulkanowy/utils/UpdateHelper.kt
Normal 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) {}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -33,6 +33,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"displayName": "Mateusz Idziejczak",
|
"displayName": "Mateusz Idziejczak",
|
||||||
"githubUsername": "PanTajemnic"
|
"githubUsername": "Luncenok"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package io.github.wulkanowy.data
|
package io.github.wulkanowy.data
|
||||||
|
|
||||||
data class Resource<out T>(val status: Status, val data: T?, val error: Throwable?) {
|
data class Resource<T>(val status: Status, val data: T?, val error: Throwable?) {
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> success(data: T?): Resource<T> {
|
fun <T> success(data: T?): Resource<T> {
|
||||||
return Resource(Status.SUCCESS, data, null)
|
return Resource(Status.SUCCESS, data, null)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package io.github.wulkanowy.data.db
|
package io.github.wulkanowy.data.db
|
||||||
|
|
||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import com.google.gson.Gson
|
import com.squareup.moshi.Moshi
|
||||||
import com.google.gson.reflect.TypeToken
|
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,6 +13,16 @@ import java.util.Date
|
|||||||
|
|
||||||
class Converters {
|
class Converters {
|
||||||
|
|
||||||
|
private val moshi by lazy { Moshi.Builder().add(PairAdapterFactory).build() }
|
||||||
|
|
||||||
|
private val integerListAdapter by lazy {
|
||||||
|
moshi.adapter<List<Int>>(Types.newParameterizedType(List::class.java, Integer::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val stringListPairAdapter by lazy {
|
||||||
|
moshi.adapter<List<Pair<String, String>>>(Types.newParameterizedType(List::class.java, Pair::class.java, String::class.java, String::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun timestampToDate(value: Long?): LocalDate? = value?.run {
|
fun timestampToDate(value: Long?): LocalDate? = value?.run {
|
||||||
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
|
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
|
||||||
@ -39,22 +50,22 @@ class Converters {
|
|||||||
fun intToMonth(value: Int?) = value?.let { Month.of(it) }
|
fun intToMonth(value: Int?) = value?.let { Month.of(it) }
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun intListToGson(list: List<Int>): String {
|
fun intListToJson(list: List<Int>): String {
|
||||||
return Gson().toJson(list)
|
return integerListAdapter.toJson(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun gsonToIntList(value: String): List<Int> {
|
fun jsonToIntList(value: String): List<Int> {
|
||||||
return Gson().fromJson(value, object : TypeToken<List<Int>>() {}.type)
|
return integerListAdapter.fromJson(value).orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun stringPairListToGson(list: List<Pair<String, String>>): String {
|
fun stringPairListToJson(list: List<Pair<String, String>>): String {
|
||||||
return Gson().toJson(list)
|
return stringListPairAdapter.toJson(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun gsonToStringPairList(value: String): List<Pair<String, String>> {
|
fun jsonToStringPairList(value: String): List<Pair<String, String>> {
|
||||||
return Gson().fromJson(value, object : TypeToken<List<Pair<String, String>>>() {}.type)
|
return stringListPairAdapter.fromJson(value).orEmpty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>>
|
||||||
|
}
|
@ -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>>
|
||||||
|
}
|
@ -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>>
|
||||||
|
}
|
@ -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>>
|
|
||||||
}
|
|
@ -12,5 +12,5 @@ import javax.inject.Singleton
|
|||||||
interface LuckyNumberDao : BaseDao<LuckyNumber> {
|
interface LuckyNumberDao : BaseDao<LuckyNumber> {
|
||||||
|
|
||||||
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
|
@Query("SELECT * FROM LuckyNumbers WHERE student_id = :studentId AND date = :date")
|
||||||
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber>
|
fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber?>
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ interface MessagesDao : BaseDao<Message> {
|
|||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND message_id = :messageId")
|
||||||
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment>
|
fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment?>
|
||||||
|
|
||||||
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
|
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder ORDER BY date DESC")
|
||||||
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
|
fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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 = ""
|
||||||
}
|
}
|
@ -36,12 +36,6 @@ data class Message(
|
|||||||
|
|
||||||
var unread: Boolean,
|
var unread: Boolean,
|
||||||
|
|
||||||
@ColumnInfo(name = "unread_by")
|
|
||||||
val unreadBy: Int,
|
|
||||||
|
|
||||||
@ColumnInfo(name = "read_by")
|
|
||||||
val readBy: Int,
|
|
||||||
|
|
||||||
val removed: Boolean,
|
val removed: Boolean,
|
||||||
|
|
||||||
@ColumnInfo(name = "has_attachments")
|
@ColumnInfo(name = "has_attachments")
|
||||||
@ -54,5 +48,11 @@ data class Message(
|
|||||||
@ColumnInfo(name = "is_notified")
|
@ColumnInfo(name = "is_notified")
|
||||||
var isNotified: Boolean = true
|
var isNotified: Boolean = true
|
||||||
|
|
||||||
|
@ColumnInfo(name = "unread_by")
|
||||||
|
var unreadBy: Int = 0
|
||||||
|
|
||||||
|
@ColumnInfo(name = "read_by")
|
||||||
|
var readBy: Int = 0
|
||||||
|
|
||||||
var content: String = ""
|
var content: String = ""
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,9 @@
|
|||||||
package io.github.wulkanowy.data.pojos
|
package io.github.wulkanowy.data.pojos
|
||||||
|
|
||||||
class Contributor(val displayName: String, val githubUsername: String)
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
class Contributor(
|
||||||
|
val displayName: String,
|
||||||
|
val githubUsername: String
|
||||||
|
)
|
||||||
|
@ -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?
|
||||||
)
|
)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package io.github.wulkanowy.data.repositories.appcreator
|
package io.github.wulkanowy.data.repositories.appcreator
|
||||||
|
|
||||||
import android.content.res.AssetManager
|
import android.content.res.AssetManager
|
||||||
import com.google.gson.Gson
|
import com.squareup.moshi.Moshi
|
||||||
|
import com.squareup.moshi.Types
|
||||||
import io.github.wulkanowy.data.pojos.Contributor
|
import io.github.wulkanowy.data.pojos.Contributor
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -15,9 +16,9 @@ class AppCreatorRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
|
suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
|
||||||
Gson().fromJson(
|
val moshi = Moshi.Builder().build()
|
||||||
assets.open("contributors.json").bufferedReader().use { it.readText() },
|
val type = Types.newParameterizedType(List::class.java, Contributor::class.java)
|
||||||
Array<Contributor>::class.java
|
val adapter = moshi.adapter<List<Contributor>>(type)
|
||||||
).toList()
|
adapter.fromJson(assets.open("contributors.json").bufferedReader().use { it.readText() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@ -2,9 +2,9 @@ package io.github.wulkanowy.data.repositories.exam
|
|||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.utils.monday
|
import io.github.wulkanowy.utils.endExamsDay
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
import io.github.wulkanowy.utils.networkBoundResource
|
||||||
import io.github.wulkanowy.utils.sunday
|
import io.github.wulkanowy.utils.startExamsDay
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -18,8 +18,8 @@ class ExamRepository @Inject constructor(
|
|||||||
|
|
||||||
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
|
||||||
shouldFetch = { it.isEmpty() || forceRefresh },
|
shouldFetch = { it.isEmpty() || forceRefresh },
|
||||||
query = { local.getExams(semester, start.monday, end.sunday) },
|
query = { local.getExams(semester, start.startExamsDay, start.endExamsDay) },
|
||||||
fetch = { remote.getExams(student, semester, start.monday, end.sunday) },
|
fetch = { remote.getExams(student, semester, start.startExamsDay, start.endExamsDay) },
|
||||||
saveFetchResult = { old, new ->
|
saveFetchResult = { old, new ->
|
||||||
local.deleteExams(old uniqueSubtract new)
|
local.deleteExams(old uniqueSubtract new)
|
||||||
local.saveExams(new uniqueSubtract old)
|
local.saveExams(new uniqueSubtract old)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,20 +13,38 @@ 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,
|
||||||
semesterId = semester.semesterId,
|
studentId = student.studentId,
|
||||||
studentId = semester.studentId,
|
subject = it.subject,
|
||||||
subject = it.subject,
|
classAverage = it.classAverage,
|
||||||
grade = it.gradeValue,
|
studentAverage = it.studentAverage,
|
||||||
amount = it.amount,
|
classAmounts = it.classItems
|
||||||
semester = isSemester
|
.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,
|
||||||
|
studentId = semester.studentId,
|
||||||
|
subject = it.subject,
|
||||||
|
amounts = it.items
|
||||||
|
.sortedBy { item -> item.grade }
|
||||||
|
.map { item -> item.amount },
|
||||||
|
studentGrade = it.items.singleOrNull { item -> item.isStudentHere }?.grade ?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getGradePointsStatistics(student: Student, semester: Semester): List<GradePointsStatistics> {
|
suspend fun getGradePointsStatistics(student: Student, semester: Semester): List<GradePointsStatistics> {
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ package io.github.wulkanowy.data.repositories.luckynumber
|
|||||||
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
import io.github.wulkanowy.data.db.entities.LuckyNumber
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.utils.networkBoundResource
|
import io.github.wulkanowy.utils.networkBoundResource
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.now
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -28,11 +29,8 @@ class LuckyNumberRepository @Inject constructor(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getNotNotifiedLuckyNumber(student: Student): Flow<LuckyNumber?> {
|
suspend fun getNotNotifiedLuckyNumber(student: Student) =
|
||||||
return local.getLuckyNumber(student, now())
|
local.getLuckyNumber(student, now()).map { if (it?.isNotified == false) it else null }.first()
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) {
|
suspend fun updateLuckyNumber(luckyNumber: LuckyNumber?) = local.updateLuckyNumber(luckyNumber)
|
||||||
local.updateLuckyNumber(luckyNumber)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import io.github.wulkanowy.data.db.entities.Message
|
|||||||
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
import io.github.wulkanowy.data.db.entities.MessageAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.message.MessageFolder.TRASHED
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -29,7 +28,7 @@ class MessageLocal @Inject constructor(
|
|||||||
messagesDb.deleteAll(messages)
|
messagesDb.deleteAll(messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessageWithAttachment(student: Student, message: Message): Flow<MessageWithAttachment> {
|
fun getMessageWithAttachment(student: Student, message: Message): Flow<MessageWithAttachment?> {
|
||||||
return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
|
return messagesDb.loadMessageWithAttachment(student.id.toInt(), message.messageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +30,12 @@ class MessageRemote @Inject constructor(private val sdk: Sdk) {
|
|||||||
date = it.date ?: now(),
|
date = it.date ?: now(),
|
||||||
folderId = it.folderId,
|
folderId = it.folderId,
|
||||||
unread = it.unread ?: false,
|
unread = it.unread ?: false,
|
||||||
unreadBy = it.unreadBy ?: 0,
|
|
||||||
readBy = it.readBy ?: 0,
|
|
||||||
removed = it.removed,
|
removed = it.removed,
|
||||||
hasAttachments = it.hasAttachments
|
hasAttachments = it.hasAttachments
|
||||||
).apply {
|
).apply {
|
||||||
content = it.content.orEmpty()
|
content = it.content.orEmpty()
|
||||||
|
unreadBy = it.unreadBy ?: 0
|
||||||
|
readBy = it.readBy ?: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,12 +34,14 @@ class MessageRepository @Inject constructor(
|
|||||||
|
|
||||||
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource(
|
fun getMessage(student: Student, message: Message, markAsRead: Boolean = false) = networkBoundResource(
|
||||||
shouldFetch = {
|
shouldFetch = {
|
||||||
|
checkNotNull(it, { "This message no longer exist!" })
|
||||||
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
Timber.d("Message content in db empty: ${it.message.content.isEmpty()}")
|
||||||
it.message.unread || it.message.content.isEmpty()
|
it.message.unread || it.message.content.isEmpty()
|
||||||
},
|
},
|
||||||
query = { local.getMessageWithAttachment(student, message) },
|
query = { local.getMessageWithAttachment(student, message) },
|
||||||
fetch = { remote.getMessagesContentDetails(student, it.message, markAsRead) },
|
fetch = { remote.getMessagesContentDetails(student, it!!.message, markAsRead) },
|
||||||
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
saveFetchResult = { old, (downloadedMessage, attachments) ->
|
||||||
|
checkNotNull(old, { "Fetched message no longer exist!" })
|
||||||
local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply {
|
local.updateMessages(listOf(old.message.copy(unread = !markAsRead).apply {
|
||||||
id = old.message.id
|
id = old.message.id
|
||||||
content = content.ifBlank { downloadedMessage }
|
content = content.ifBlank { downloadedMessage }
|
||||||
|
@ -5,6 +5,7 @@ import android.content.SharedPreferences
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -74,12 +75,25 @@ class PreferencesRepository @Inject constructor(
|
|||||||
val fillMessageContent: Boolean
|
val fillMessageContent: Boolean
|
||||||
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
|
get() = getBoolean(R.string.pref_key_fill_message_content, R.bool.pref_default_fill_message_content)
|
||||||
|
|
||||||
|
val showGroupsInPlan: Boolean
|
||||||
|
get() = getBoolean(R.string.pref_key_timetable_show_groups, R.bool.pref_default_timetable_show_groups)
|
||||||
|
|
||||||
val showWholeClassPlan: String
|
val showWholeClassPlan: String
|
||||||
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
|
get() = getString(R.string.pref_key_timetable_show_whole_class, R.string.pref_default_timetable_show_whole_class)
|
||||||
|
|
||||||
|
val gradeSortingMode: GradeSortingMode
|
||||||
|
get() = GradeSortingMode.getByValue(getString(R.string.pref_key_grade_sorting_mode, R.string.pref_default_grade_sorting_mode))
|
||||||
|
|
||||||
val showTimetableTimers: Boolean
|
val showTimetableTimers: Boolean
|
||||||
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
|
get() = getBoolean(R.string.pref_key_timetable_show_timers, R.bool.pref_default_timetable_show_timers)
|
||||||
|
|
||||||
|
var isHomeworkFullscreen: Boolean
|
||||||
|
get() = getBoolean(R.string.pref_key_homework_fullscreen, R.bool.pref_default_homework_fullscreen)
|
||||||
|
set(value) = sharedPref.edit().putBoolean("homework_fullscreen", value).apply()
|
||||||
|
|
||||||
|
val showSubjectsWithoutGrades: Boolean
|
||||||
|
get() = getBoolean(R.string.pref_key_subjects_without_grades, R.bool.pref_default_subjects_without_grades)
|
||||||
|
|
||||||
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
private fun getString(id: Int, default: Int) = getString(context.getString(id), default)
|
||||||
|
|
||||||
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
private fun getString(id: String, default: Int) = sharedPref.getString(id, context.getString(default)) ?: context.getString(default)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.data.repositories.semester
|
package io.github.wulkanowy.data.repositories.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
|
||||||
import io.github.wulkanowy.utils.DispatchersProvider
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
@ -7,6 +8,7 @@ import io.github.wulkanowy.utils.getCurrentOrLast
|
|||||||
import io.github.wulkanowy.utils.isCurrent
|
import io.github.wulkanowy.utils.isCurrent
|
||||||
import io.github.wulkanowy.utils.uniqueSubtract
|
import io.github.wulkanowy.utils.uniqueSubtract
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -18,24 +20,33 @@ class SemesterRepository @Inject constructor(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
|
suspend fun getSemesters(student: Student, forceRefresh: Boolean = false, refreshOnNoCurrent: Boolean = false) = withContext(dispatchers.backgroundThread) {
|
||||||
local.getSemesters(student).let { semesters ->
|
val semesters = local.getSemesters(student)
|
||||||
semesters.filter {
|
|
||||||
!forceRefresh && when {
|
|
||||||
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API -> semesters.firstOrNull { it.isCurrent }?.diaryId != 0
|
|
||||||
refreshOnNoCurrent -> semesters.any { semester -> semester.isCurrent }
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.ifEmpty {
|
|
||||||
val new = remote.getSemesters(student)
|
|
||||||
if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!")
|
|
||||||
|
|
||||||
val old = local.getSemesters(student)
|
|
||||||
local.deleteSemesters(old.uniqueSubtract(new))
|
|
||||||
local.saveSemesters(new.uniqueSubtract(old))
|
|
||||||
|
|
||||||
|
if (isShouldFetch(student, semesters, forceRefresh, refreshOnNoCurrent)) {
|
||||||
|
refreshSemesters(student)
|
||||||
local.getSemesters(student)
|
local.getSemesters(student)
|
||||||
}
|
} else semesters
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isShouldFetch(student: Student, semesters: List<Semester>, forceRefresh: Boolean, refreshOnNoCurrent: Boolean): Boolean {
|
||||||
|
val isNoSemesters = semesters.isEmpty()
|
||||||
|
|
||||||
|
val isRefreshOnModeChangeRequired = if (Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.API) {
|
||||||
|
semesters.firstOrNull { it.isCurrent }?.diaryId == 0
|
||||||
|
} else false
|
||||||
|
|
||||||
|
val isRefreshOnNoCurrentAppropriate = refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
|
||||||
|
|
||||||
|
return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun refreshSemesters(student: Student) {
|
||||||
|
val new = remote.getSemesters(student)
|
||||||
|
if (new.isEmpty()) return Timber.i("Empty semester list!")
|
||||||
|
|
||||||
|
val old = local.getSemesters(student)
|
||||||
|
local.deleteSemesters(old.uniqueSubtract(new))
|
||||||
|
local.saveSemesters(new.uniqueSubtract(old))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
|
suspend fun getCurrentSemester(student: Student, forceRefresh: Boolean = false) = withContext(dispatchers.backgroundThread) {
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -26,7 +26,9 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
|
|||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID
|
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID
|
||||||
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME
|
||||||
import io.github.wulkanowy.ui.modules.main.MainView
|
import io.github.wulkanowy.ui.modules.main.MainView
|
||||||
|
import io.github.wulkanowy.utils.DispatchersProvider
|
||||||
import io.github.wulkanowy.utils.toTimestamp
|
import io.github.wulkanowy.utils.toTimestamp
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalDateTime.now
|
import java.time.LocalDateTime.now
|
||||||
@ -35,7 +37,8 @@ import javax.inject.Inject
|
|||||||
class TimetableNotificationSchedulerHelper @Inject constructor(
|
class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val alarmManager: AlarmManager,
|
private val alarmManager: AlarmManager,
|
||||||
private val preferencesRepository: PreferencesRepository
|
private val preferencesRepository: PreferencesRepository,
|
||||||
|
private val dispatchersProvider: DispatchersProvider,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
|
private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt()
|
||||||
@ -44,13 +47,15 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
||||||
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
withContext(dispatchersProvider.backgroundThread) {
|
||||||
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
||||||
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
|
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
||||||
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId))
|
||||||
|
cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId))
|
||||||
|
|
||||||
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,28 +66,34 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
|||||||
|
|
||||||
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id)
|
||||||
|
|
||||||
fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
|
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId)
|
||||||
|
|
||||||
lessons.groupBy { it.date }
|
withContext(dispatchersProvider.backgroundThread) {
|
||||||
.map { it.value.sortedBy { lesson -> lesson.start } }
|
lessons.groupBy { it.date }
|
||||||
.map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } }
|
.map { it.value.sortedBy { lesson -> lesson.start } }
|
||||||
.map { day ->
|
.map { it.filter { lesson -> lesson.isStudentPlan } }
|
||||||
day.forEachIndexed { index, lesson ->
|
.map { day ->
|
||||||
val intent = createIntent(student, lesson, day.getOrNull(index + 1))
|
val canceled = day.filter { it.canceled }
|
||||||
|
val active = day.filter { !it.canceled }
|
||||||
|
|
||||||
if (lesson.start > now()) {
|
cancelScheduled(canceled)
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson))
|
active.forEachIndexed { index, lesson ->
|
||||||
}
|
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
|
||||||
|
|
||||||
if (lesson.end > now()) {
|
if (lesson.start > now()) {
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
|
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, active, lesson))
|
||||||
if (day.lastIndex == index) {
|
}
|
||||||
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
|
|
||||||
|
if (lesson.end > now()) {
|
||||||
|
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start)
|
||||||
|
if (active.lastIndex == index) {
|
||||||
|
scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent {
|
||||||
|
@ -44,6 +44,7 @@ class SyncManager @Inject constructor(
|
|||||||
|
|
||||||
if (SDK_INT >= O) {
|
if (SDK_INT >= O) {
|
||||||
channels.forEach { it.create() }
|
channels.forEach { it.create() }
|
||||||
|
notificationManager.deleteNotificationChannel("lesson_channel")
|
||||||
notificationManager.deleteNotificationChannel("new_entries_channel")
|
notificationManager.deleteNotificationChannel("new_entries_channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,13 +50,16 @@ class SyncWorker @WorkerInject constructor(
|
|||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
Timber.w("${work::class.java.simpleName} result: An exception ${e.message} occurred")
|
||||||
if (e is FeatureDisabledException || e is FeatureNotAvailableException) null
|
if (e is FeatureDisabledException || e is FeatureNotAvailableException) null
|
||||||
else e
|
else {
|
||||||
|
Timber.e(e)
|
||||||
|
e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val result = when {
|
val result = when {
|
||||||
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
exceptions.isNotEmpty() && inputData.getBoolean("one_time", false) -> {
|
||||||
Result.failure(Data.Builder()
|
Result.failure(Data.Builder()
|
||||||
.putString("error", exceptions.toString())
|
.putString("error", exceptions.map { it.stackTraceToString() }.toString())
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class UpcomingLessonsChannel @Inject constructor(
|
|||||||
) : Channel {
|
) : Channel {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL_ID = "lesson_channel"
|
const val CHANNEL_ID = "upcoming_lesson_channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun create() {
|
override fun create() {
|
||||||
@ -26,6 +26,7 @@ class UpcomingLessonsChannel @Inject constructor(
|
|||||||
lockscreenVisibility = VISIBILITY_PUBLIC
|
lockscreenVisibility = VISIBILITY_PUBLIC
|
||||||
setShowBadge(false)
|
setShowBadge(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
|
setSound(null, null)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@ package io.github.wulkanowy.services.sync.works
|
|||||||
import io.github.wulkanowy.data.db.entities.Semester
|
import io.github.wulkanowy.data.db.entities.Semester
|
||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.exam.ExamRepository
|
import io.github.wulkanowy.data.repositories.exam.ExamRepository
|
||||||
import io.github.wulkanowy.utils.monday
|
|
||||||
import io.github.wulkanowy.utils.sunday
|
|
||||||
import io.github.wulkanowy.utils.waitForResult
|
import io.github.wulkanowy.utils.waitForResult
|
||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.now
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -12,6 +10,6 @@ import javax.inject.Inject
|
|||||||
class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
|
class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work {
|
||||||
|
|
||||||
override suspend fun doWork(student: Student, semester: Semester) {
|
override suspend fun doWork(student: Student, semester: Semester) {
|
||||||
examRepository.getExams(student, semester, now().monday, now().sunday, true).waitForResult()
|
examRepository.getExams(student, semester, now(), now(), true).waitForResult()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ 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.getCompatColor
|
import io.github.wulkanowy.utils.getCompatColor
|
||||||
import io.github.wulkanowy.utils.waitForResult
|
import io.github.wulkanowy.utils.waitForResult
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ class LuckyNumberWork @Inject constructor(
|
|||||||
override suspend fun doWork(student: Student, semester: Semester) {
|
override suspend fun doWork(student: Student, semester: Semester) {
|
||||||
luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult()
|
luckyNumberRepository.getLuckyNumber(student, true, preferencesRepository.isNotificationsEnable).waitForResult()
|
||||||
|
|
||||||
luckyNumberRepository.getNotNotifiedLuckyNumber(student).first()?.let {
|
luckyNumberRepository.getNotNotifiedLuckyNumber(student)?.let {
|
||||||
notify(it)
|
notify(it)
|
||||||
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
luckyNumberRepository.updateLuckyNumber(it.apply { isNotified = true })
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,9 @@ import io.github.wulkanowy.data.db.entities.Semester
|
|||||||
import io.github.wulkanowy.data.db.entities.Student
|
import io.github.wulkanowy.data.db.entities.Student
|
||||||
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
import io.github.wulkanowy.data.repositories.note.NoteRepository
|
||||||
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
import io.github.wulkanowy.data.repositories.preferences.PreferencesRepository
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.NEUTRAL
|
||||||
|
import io.github.wulkanowy.sdk.scrapper.notes.NoteCategory.POSITIVE
|
||||||
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
import io.github.wulkanowy.services.sync.channels.NewNotesChannel
|
||||||
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
|
||||||
@ -41,8 +44,20 @@ class NoteWork @Inject constructor(
|
|||||||
|
|
||||||
private fun notify(notes: List<Note>) {
|
private fun notify(notes: List<Note>) {
|
||||||
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
|
notificationManager.notify(Random.nextInt(Int.MAX_VALUE), NotificationCompat.Builder(context, NewNotesChannel.CHANNEL_ID)
|
||||||
.setContentTitle(context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size))
|
.setContentTitle(
|
||||||
.setContentText(context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size))
|
when (NoteCategory.getByValue(notes.first().categoryType)) {
|
||||||
|
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_new_items, notes.size, notes.size)
|
||||||
|
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_new_items, notes.size, notes.size)
|
||||||
|
else -> context.resources.getQuantityString(R.plurals.note_new_items, notes.size, notes.size)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.setContentText(
|
||||||
|
when (NoteCategory.getByValue(notes.first().categoryType)) {
|
||||||
|
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_notify_new_items, notes.size, notes.size)
|
||||||
|
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_notify_new_items, notes.size, notes.size)
|
||||||
|
else -> context.resources.getQuantityString(R.plurals.note_notify_new_items, notes.size, notes.size)
|
||||||
|
}
|
||||||
|
)
|
||||||
.setSmallIcon(R.drawable.ic_stat_note)
|
.setSmallIcon(R.drawable.ic_stat_note)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setDefaults(DEFAULT_ALL)
|
.setDefaults(DEFAULT_ALL)
|
||||||
@ -52,7 +67,13 @@ class NoteWork @Inject constructor(
|
|||||||
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
|
PendingIntent.getActivity(context, MainView.Section.NOTE.id,
|
||||||
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
|
MainActivity.getStartIntent(context, MainView.Section.NOTE, true), FLAG_UPDATE_CURRENT))
|
||||||
.setStyle(NotificationCompat.InboxStyle().run {
|
.setStyle(NotificationCompat.InboxStyle().run {
|
||||||
setSummaryText(context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size))
|
setSummaryText(
|
||||||
|
when (NoteCategory.getByValue(notes.first().categoryType)) {
|
||||||
|
POSITIVE -> context.resources.getQuantityString(R.plurals.praise_number_item, notes.size, notes.size)
|
||||||
|
NEUTRAL -> context.resources.getQuantityString(R.plurals.neutral_note_number_item, notes.size, notes.size)
|
||||||
|
else -> context.resources.getQuantityString(R.plurals.note_number_item, notes.size, notes.size)
|
||||||
|
}
|
||||||
|
)
|
||||||
notes.forEach { addLine("${it.teacher}: ${it.category}") }
|
notes.forEach { addLine("${it.teacher}: ${it.category}") }
|
||||||
this
|
this
|
||||||
})
|
})
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@ -63,7 +64,7 @@ open class BasePresenter<T : BaseView>(
|
|||||||
|
|
||||||
fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job {
|
fun <T> Flow<T>.launch(individualJobTag: String = "load"): Job {
|
||||||
jobs[individualJobTag]?.cancel()
|
jobs[individualJobTag]?.cancel()
|
||||||
val job = launchIn(this@BasePresenter)
|
val job = catch { errorHandler.dispatch(it) }.launchIn(this@BasePresenter)
|
||||||
jobs[individualJobTag] = job
|
jobs[individualJobTag] = job
|
||||||
Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job")
|
Timber.d("Job $individualJobTag launched in ${this@BasePresenter.javaClass.simpleName}: $job")
|
||||||
return job
|
return job
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -9,6 +9,7 @@ import io.github.wulkanowy.R
|
|||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
import io.github.wulkanowy.data.repositories.attendance.SentExcuseStatus
|
||||||
import io.github.wulkanowy.databinding.ItemAttendanceBinding
|
import io.github.wulkanowy.databinding.ItemAttendanceBinding
|
||||||
|
import io.github.wulkanowy.utils.description
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class AttendanceAdapter @Inject constructor() :
|
class AttendanceAdapter @Inject constructor() :
|
||||||
@ -34,7 +35,7 @@ class AttendanceAdapter @Inject constructor() :
|
|||||||
with(holder.binding) {
|
with(holder.binding) {
|
||||||
attendanceItemNumber.text = item.number.toString()
|
attendanceItemNumber.text = item.number.toString()
|
||||||
attendanceItemSubject.text = item.subject
|
attendanceItemSubject.text = item.subject
|
||||||
attendanceItemDescription.text = item.name
|
attendanceItemDescription.setText(item.description)
|
||||||
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
|
attendanceItemAlert.visibility = item.run { if (absence && !excused) View.VISIBLE else View.INVISIBLE }
|
||||||
attendanceItemNumber.visibility = View.GONE
|
attendanceItemNumber.visibility = View.GONE
|
||||||
attendanceItemExcuseInfo.visibility = View.GONE
|
attendanceItemExcuseInfo.visibility = View.GONE
|
||||||
|
@ -7,6 +7,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import io.github.wulkanowy.data.db.entities.Attendance
|
import io.github.wulkanowy.data.db.entities.Attendance
|
||||||
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
import io.github.wulkanowy.databinding.DialogAttendanceBinding
|
||||||
|
import io.github.wulkanowy.utils.description
|
||||||
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
import io.github.wulkanowy.utils.lifecycleAwareVariable
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class AttendanceDialog : DialogFragment() {
|
|||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
attendanceDialogSubject.text = attendance.subject
|
attendanceDialogSubject.text = attendance.subject
|
||||||
attendanceDialogDescription.text = attendance.name
|
attendanceDialogDescription.setText(attendance.description)
|
||||||
attendanceDialogDate.text = attendance.date.toFormattedString()
|
attendanceDialogDate.text = attendance.date.toFormattedString()
|
||||||
attendanceDialogNumber.text = attendance.number.toString()
|
attendanceDialogNumber.text = attendance.number.toString()
|
||||||
attendanceDialogClose.setOnClickListener { dismiss() }
|
attendanceDialogClose.setOnClickListener { dismiss() }
|
||||||
|
@ -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
|
||||||
|
@ -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>()
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.grade
|
||||||
|
|
||||||
|
enum class GradeSortingMode(val value: String) {
|
||||||
|
ALPHABETIC("alphabetic"),
|
||||||
|
DATE("date");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getByValue(value: String) = values().firstOrNull { it.value == value } ?: ALPHABETIC
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.grade.details
|
package io.github.wulkanowy.ui.modules.grade.details
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.Status
|
||||||
import io.github.wulkanowy.data.db.entities.Grade
|
import io.github.wulkanowy.data.db.entities.Grade
|
||||||
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
import io.github.wulkanowy.data.repositories.grade.GradeRepository
|
||||||
@ -10,7 +11,9 @@ 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.ui.modules.grade.GradeSortingMode.ALPHABETIC
|
||||||
|
import io.github.wulkanowy.ui.modules.grade.GradeSortingMode.DATE
|
||||||
|
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
|
||||||
@ -26,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
|
||||||
@ -78,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!!)
|
||||||
@ -184,10 +190,20 @@ class GradeDetailsPresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
|
private fun createGradeItems(items: List<GradeDetailsWithAverage>): List<GradeDetailsItem> {
|
||||||
return items
|
return items
|
||||||
.filter { it.grades.isNotEmpty() }
|
.let { gradesWithAverages ->
|
||||||
.sortedBy { it.subject }
|
if (!preferencesRepository.showSubjectsWithoutGrades) {
|
||||||
|
gradesWithAverages.filter { it.grades.isNotEmpty() }
|
||||||
|
} else gradesWithAverages
|
||||||
|
}
|
||||||
|
.let {
|
||||||
|
when (preferencesRepository.gradeSortingMode) {
|
||||||
|
DATE -> it.sortedByDescending { gradeDetailsWithAverage -> gradeDetailsWithAverage.grades.firstOrNull()?.date }
|
||||||
|
ALPHABETIC -> it.sortedBy { gradeDetailsWithAverage -> gradeDetailsWithAverage.subject.toLowerCase() }
|
||||||
|
}
|
||||||
|
}
|
||||||
.map { (subject, average, points, _, grades) ->
|
.map { (subject, average, points, _, grades) ->
|
||||||
val subItems = grades
|
val subItems = grades
|
||||||
.sortedByDescending { it.date }
|
.sortedByDescending { it.date }
|
||||||
@ -197,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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
val isNoContent = it.data!!.isEmpty() || when (type) {
|
||||||
showContent(it.data.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)
|
||||||
|
@ -21,7 +21,7 @@ class GradeSummaryAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerV
|
|||||||
|
|
||||||
var items = emptyList<GradeSummary>()
|
var items = emptyList<GradeSummary>()
|
||||||
|
|
||||||
override fun getItemCount() = items.size + 1
|
override fun getItemCount() = items.size + if (items.isNotEmpty()) 1 else 0
|
||||||
|
|
||||||
override fun getItemViewType(position: Int) = when (position) {
|
override fun getItemViewType(position: Int) = when (position) {
|
||||||
0 -> ViewType.HEADER.id
|
0 -> ViewType.HEADER.id
|
||||||
|
@ -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
|
||||||
@ -45,16 +45,17 @@ class GradeSummaryPresenter @Inject constructor(
|
|||||||
Status.LOADING -> Timber.i("Loading grade summary started")
|
Status.LOADING -> Timber.i("Loading grade summary started")
|
||||||
Status.SUCCESS -> {
|
Status.SUCCESS -> {
|
||||||
Timber.i("Loading grade summary result: Success")
|
Timber.i("Loading grade summary result: Success")
|
||||||
|
val items = createGradeSummaryItems(it.data!!)
|
||||||
view?.run {
|
view?.run {
|
||||||
showEmpty(it.data!!.isEmpty())
|
showEmpty(items.isEmpty())
|
||||||
showContent(it.data.isNotEmpty())
|
showContent(items.isNotEmpty())
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
updateData(createGradeSummaryItems(it.data))
|
updateData(items)
|
||||||
}
|
}
|
||||||
analytics.logEvent(
|
analytics.logEvent(
|
||||||
"load_data",
|
"load_data",
|
||||||
"type" to "grade_summary",
|
"type" to "grade_summary",
|
||||||
"items" to it.data!!.size
|
"items" to it.data.size
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Status.ERROR -> {
|
Status.ERROR -> {
|
||||||
|
@ -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
|
||||||
|
@ -29,6 +29,8 @@ class HomeworkDetailsAdapter @Inject constructor() :
|
|||||||
attachments = value?.attachments.orEmpty()
|
attachments = value?.attachments.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isHomeworkFullscreen = false
|
||||||
|
|
||||||
var onAttachmentClickListener: (url: String) -> Unit = {}
|
var onAttachmentClickListener: (url: String) -> Unit = {}
|
||||||
|
|
||||||
var onFullScreenClickListener = {}
|
var onFullScreenClickListener = {}
|
||||||
@ -67,6 +69,8 @@ class HomeworkDetailsAdapter @Inject constructor() :
|
|||||||
homeworkDialogSubject.text = homework?.subject
|
homeworkDialogSubject.text = homework?.subject
|
||||||
homeworkDialogTeacher.text = homework?.teacher
|
homeworkDialogTeacher.text = homework?.teacher
|
||||||
homeworkDialogContent.text = homework?.content
|
homeworkDialogContent.text = homework?.content
|
||||||
|
homeworkDialogFullScreen.visibility = if (isHomeworkFullscreen) GONE else VISIBLE
|
||||||
|
homeworkDialogFullScreenExit.visibility = if (isHomeworkFullscreen) VISIBLE else GONE
|
||||||
homeworkDialogFullScreen.setOnClickListener {
|
homeworkDialogFullScreen.setOnClickListener {
|
||||||
homeworkDialogFullScreen.visibility = GONE
|
homeworkDialogFullScreen.visibility = GONE
|
||||||
homeworkDialogFullScreenExit.visibility = VISIBLE
|
homeworkDialogFullScreenExit.visibility = VISIBLE
|
||||||
|
@ -62,12 +62,25 @@ class HomeworkDetailsDialog : BaseDialogFragment<DialogHomeworkBinding>(), Homew
|
|||||||
homeworkDialogClose.setOnClickListener { dismiss() }
|
homeworkDialogClose.setOnClickListener { dismiss() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (presenter.isHomeworkFullscreen) {
|
||||||
|
dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
} else {
|
||||||
|
dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
|
||||||
|
}
|
||||||
|
|
||||||
with(binding.homeworkDialogRecycler) {
|
with(binding.homeworkDialogRecycler) {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
adapter = detailsAdapter.apply {
|
adapter = detailsAdapter.apply {
|
||||||
onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) }
|
onAttachmentClickListener = { context.openInternetBrowser(it, ::showMessage) }
|
||||||
onFullScreenClickListener = { dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT) }
|
onFullScreenClickListener = {
|
||||||
onFullScreenExitClickListener = { dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT) }
|
dialog?.window?.setLayout(MATCH_PARENT, MATCH_PARENT)
|
||||||
|
presenter.isHomeworkFullscreen = true
|
||||||
|
}
|
||||||
|
onFullScreenExitClickListener = {
|
||||||
|
dialog?.window?.setLayout(WRAP_CONTENT, WRAP_CONTENT)
|
||||||
|
presenter.isHomeworkFullscreen = false
|
||||||
|
}
|
||||||
|
isHomeworkFullscreen = presenter.isHomeworkFullscreen
|
||||||
homework = this@HomeworkDetailsDialog.homework
|
homework = this@HomeworkDetailsDialog.homework
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,11 @@ package io.github.wulkanowy.ui.modules.homework.details
|
|||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.Status
|
||||||
import io.github.wulkanowy.data.db.entities.Homework
|
import io.github.wulkanowy.data.db.entities.Homework
|
||||||
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
|
import io.github.wulkanowy.data.repositories.homework.HomeworkRepository
|
||||||
|
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
|
||||||
@ -16,9 +17,16 @@ 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
|
||||||
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) {
|
) : BasePresenter<HomeworkDetailsView>(errorHandler, studentRepository) {
|
||||||
|
|
||||||
|
var isHomeworkFullscreen
|
||||||
|
get() = preferencesRepository.isHomeworkFullscreen
|
||||||
|
set(value) {
|
||||||
|
preferencesRepository.isHomeworkFullscreen = value
|
||||||
|
}
|
||||||
|
|
||||||
override fun onAttachView(view: HomeworkDetailsView) {
|
override fun onAttachView(view: HomeworkDetailsView) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
|
@ -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() {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 ("login" 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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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) {
|
||||||
|
|
||||||
@ -66,7 +66,12 @@ class LoginRecoverPresenter @Inject constructor(
|
|||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showCaptcha(false)
|
showCaptcha(false)
|
||||||
}
|
}
|
||||||
Status.SUCCESS -> view?.loadReCaptcha(siteKey = it.data!!.first, url = it.data.second)
|
Status.SUCCESS -> view?.run {
|
||||||
|
loadReCaptcha(url = it.data!!.first, siteKey = it.data.second)
|
||||||
|
showProgress(false)
|
||||||
|
showErrorView(false)
|
||||||
|
showCaptcha(true)
|
||||||
|
}
|
||||||
Status.ERROR -> {
|
Status.ERROR -> {
|
||||||
Timber.i("Obtain captcha site key result: An exception occurred")
|
Timber.i("Obtain captcha site key result: An exception occurred")
|
||||||
errorHandler.dispatch(it.error!!)
|
errorHandler.dispatch(it.error!!)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user