1
0

Compare commits

..

156 Commits

Author SHA1 Message Date
26267507eb Merge branch 'release/0.22.1' into master 2020-10-30 01:49:42 +01:00
24d0c5057b Version 0.22.1 2020-10-30 01:49:30 +01:00
b05026a6e6 New Crowdin updates (#1002) 2020-10-30 00:54:35 +01:00
8036f3d7f7 Add HMS flavor (#998) 2020-10-29 13:58:56 +01:00
23e309d38e New Crowdin updates (#993) 2020-10-26 11:54:27 +01:00
bf92c6b2e9 Fix grade header item object comparision (#999) 2020-10-25 12:16:19 +01:00
c00b5edaf7 Make AppGallery badge background transparent. (#997) 2020-10-22 16:17:52 +02:00
cb09ca13dc Bump junit from 4.13 to 4.13.1 (#995) 2020-10-15 18:36:31 +00:00
3d68b8e629 Bump gradle from 4.0.2 to 4.1.0 (#996) 2020-10-15 18:36:13 +00:00
b3173581e5 Merge tag '0.22.0' into develop
Version 0.22.0
2020-10-15 17:54:27 +02:00
ddac1d0f98 Merge branch 'release/0.22.0' into master 2020-10-15 17:54:22 +02:00
db6a359bea Version 0.22.0 2020-10-15 17:54:14 +02:00
e7221e6a32 Add Vulcan and Scrapper Exceptions to known exceptions (#994) 2020-10-15 15:55:59 +02:00
db9c2640c7 Add in-app updates support (#914) 2020-10-15 01:00:41 +02:00
ca67e144e4 Add ConnectException and StreamResetException to known exceptions (#992) 2020-10-13 22:32:15 +02:00
da2346ed83 New Crowdin updates (#987) 2020-10-11 21:00:48 +02:00
a87818f3d0 Cancel scheduled lesson notifications which turned into canceled (#991) 2020-10-11 20:51:03 +02:00
5092f8c0bf Reload grade list on mark all as read (#990) 2020-10-11 20:33:18 +02:00
af0787c0b1 Add support for BIG font (#989)
Co-authored-by: Faierbel <RafalBO99@outlook.com>
2020-10-10 02:53:25 +02:00
721b4ac797 Add support for night mode in account switcher dialog (#988) 2020-10-09 21:24:58 +02:00
26a69092cc Add AppGallery badge (#986) 2020-10-07 11:19:34 +02:00
2bd0c75055 Bump about_libraries from 8.3.1 to 8.4.2 (#985) 2020-10-06 21:56:46 +00:00
d6f3c57293 Bump moshi from 1.10.0 to 1.11.0 (#984) 2020-10-06 21:45:38 +00:00
518387e7bb Bump gradle from 4.0.1 to 4.0.2 (#982) 2020-10-06 21:43:56 +00:00
ca6dfbf2d0 Bump core-ktx from 1.3.1 to 1.3.2 (#983) 2020-10-06 21:43:50 +00:00
da6d8a74fd New Crowdin updates (#981) 2020-10-06 23:34:27 +02:00
e35e4ef152 New Crowdin updates (#948) 2020-10-03 13:01:11 +02:00
40fc6ec2e0 Upgrade android sdk to 30 (#966) 2020-10-03 01:08:57 +02:00
b91973aec3 Bump chucker from 3.2.0 to 3.3.0 (#979) 2020-10-01 15:38:29 +00:00
047e70ad46 Bump firebase-crashlytics from 17.2.1 to 17.2.2 (#974) 2020-09-30 20:13:18 +00:00
112c1eb793 Bump moshi from 1.9.3 to 1.10.0 (#972) 2020-09-30 20:13:01 +00:00
c479b31670 Bump coil from 1.0.0-rc2 to 1.0.0-rc3 (#976) 2020-09-30 20:01:45 +00:00
43ed8c8fce Bump firebase-messaging from 20.2.4 to 20.3.0 (#973) 2020-09-30 19:52:02 +00:00
613fa44c27 Bump about_libraries from 8.3.0 to 8.3.1 (#975) 2020-09-30 19:34:53 +00:00
f21216286d Bump mockk from 1.10.0 to 1.10.2 (#977) 2020-09-30 19:34:13 +00:00
7298d0d75a Bump google-services from 4.3.3 to 4.3.4 (#978) 2020-09-30 19:33:22 +00:00
ee0fbcdfd6 Merge tag '0.21.2' into develop
Version 0.21.2
2020-09-29 21:10:17 +02:00
c362ad12c7 Merge branch 'release/0.21.2' into master 2020-09-29 21:10:11 +02:00
8a1a712d6d Version 0.21.2 2020-09-29 21:10:05 +02:00
1f0f6b3e51 Fix string pair list type converter (#971) 2020-09-29 21:02:49 +02:00
11487e77ca Merge tag '0.21.1' into develop
Version 0.21.1
2020-09-29 11:43:58 +02:00
fd0fd4df55 Merge branch 'release/0.21.1' into master 2020-09-29 11:43:54 +02:00
d95a33787b Version 0.21.1 2020-09-29 11:43:49 +02:00
e5661098d9 Fix string pair list type converter (#970) 2020-09-29 11:42:54 +02:00
d020b01794 Merge tag '0.21.0' into develop
Version 0.21.0
2020-09-27 22:12:31 +02:00
d8b1264024 Merge branch 'release/0.21.0' into master 2020-09-27 22:12:27 +02:00
cddd17650b Version 0.21.0 2020-09-27 22:11:55 +02:00
a0f9c70036 Migrate from gson to moshi (#969) 2020-09-27 20:59:27 +02:00
2e05416fb5 Show groups next to subjects in timetable (#953) 2020-09-27 16:33:36 +02:00
d32ebd66de Add subjects sorting in grades (#946) 2020-09-27 16:28:39 +02:00
c6a99f1000 Add remembering the full screen mode in homework (#956)
Co-authored-by: Faierbel <RafalBO99@outlook.com>
2020-09-27 14:49:19 +02:00
bafe52e310 Different notification for notes and praises (#952) 2020-09-25 15:46:08 +02:00
e08abc1fc2 Show subjects without grades in "Grades" (#947) 2020-09-25 15:45:07 +02:00
2a74b11cce Add app shortcuts (#939)
Co-authored-by: Faierbel <RafalBO99@outlook.com>
2020-09-25 15:37:19 +02:00
b0b3ccfd53 Upgrade gradle wrapper to 6.6.1 (#968) 2020-09-20 21:54:59 +02:00
6c68456f7a Merge tag '0.20.5' into develop
Version 0.20.5
2020-09-19 13:03:32 +02:00
3e8e9b4ecc Merge branch 'release/0.20.5' into master 2020-09-19 13:03:28 +02:00
d6ebc343d5 Version 0.20.5 2020-09-19 01:25:34 +02:00
73be416807 Fix crash in flowWithResourceIn() (#967) 2020-09-19 00:57:55 +02:00
0cb65a29ba Merge tag '0.20.4' into develop
Version 0.20.4
2020-09-13 19:00:45 +02:00
13198f2ab4 Merge branch 'release/0.20.4' into master 2020-09-13 19:00:39 +02:00
cd92f37435 Version 0.20.4 2020-09-13 19:00:32 +02:00
5d8fb376ab Expand exam sync date range to next month (#960) 2020-09-13 18:37:34 +02:00
47150364d8 Fix lifecycle of timer tasks in timetable lessons (#958)
Co-authored-by: Faierbel <RafalBO99@outlook.com>
2020-09-13 16:27:53 +02:00
792b123598 Bump coil from 1.0.0-rc1 to 1.0.0-rc2 (#961) 2020-09-13 14:07:30 +00:00
acf5c8e9ba Bump firebase-crashlytics-gradle from 2.2.1 to 2.3.0 (#964) 2020-09-13 14:04:41 +00:00
53561668fc Bump hilt_version from 2.28.3-alpha to 2.29.1-alpha (#962) 2020-09-13 14:03:09 +00:00
7cfe58d311 Bump material from 1.2.0 to 1.2.1 (#963) 2020-09-13 14:02:31 +00:00
cd51fac621 Add eduportal.koszalin.pl register (#959) 2020-09-13 13:46:45 +02:00
adde5541e2 Move timetable notifications scheduling to background thread (#954) 2020-09-11 13:02:16 +02:00
6e56d3ff06 Ignore empty semesters on refresh (#955) 2020-09-09 13:28:44 +02:00
ec761f6329 Fix bug in grade statistics (#951) 2020-09-08 20:13:17 +02:00
6363c90e37 Disable sound of upcoming lessons notification (fix) (#950) 2020-09-07 20:28:32 +02:00
c30f105be5 Fix crash on unknown attendance category type (#949) 2020-09-07 09:35:26 +02:00
9f85b2206a Merge tag '0.20.3' into develop
Version 0.20.3
2020-09-04 23:42:12 +02:00
42515fd084 Merge branch 'release/0.20.3' into master 2020-09-04 23:42:07 +02:00
9a7c04fe7b Version 0.20.3 2020-09-04 23:42:01 +02:00
debb21f5f9 Add full stacktrace to errors list in sync now (#945) 2020-09-03 21:10:39 +02:00
18b9bf42e1 Fix crash in flowWithResourceIn() (#944) 2020-09-03 20:54:28 +02:00
6ded83d132 Fix attendance item description (#943) 2020-09-03 20:52:24 +02:00
71d37a1c6c Merge tag '0.20.2' into develop
Version 0.20.2
2020-09-02 00:23:49 +02:00
3975d06cde Merge branch 'release/0.20.2' into master 2020-09-02 00:23:45 +02:00
ee168bafe0 Version 0.20.2 2020-09-02 00:23:41 +02:00
42ed7e0ae1 Merge tag '0.20.1' into develop
Version 0.20.1
2020-09-02 00:14:35 +02:00
0e92447974 Merge branch 'release/0.20.1' into master 2020-09-02 00:14:23 +02:00
40492e6c01 Version 0.20.1 2020-09-02 00:14:18 +02:00
69a1193154 Fix semester list refresh on no current semester found (#940) 2020-09-01 23:58:18 +02:00
0f65af8958 Fix grade summary empty view (#941) 2020-09-01 23:57:56 +02:00
2ad1d086e0 Fix lucky number notification (#937) 2020-09-01 15:39:34 +02:00
f8b7baef24 Remove force sync dialog (#938) 2020-09-01 14:58:45 +02:00
90be9d1add Disable notification sound (#936) 2020-09-01 12:57:45 +02:00
20f931c5cc Fix recaptcha loading in password recover (#935) 2020-09-01 09:33:14 +02:00
9997b1adbb Add skarzyskokamienna vulcan register (#934) 2020-09-01 09:31:36 +02:00
eb616eedc7 Fix crash in flowWithResourceIn() (#933) 2020-09-01 09:31:01 +02:00
a5de39a366 Update UI dependencies (#927) 2020-08-31 12:55:51 +02:00
57bc2b2533 Bump firebase-crashlytics from 17.1.1 to 17.2.1 (#931) 2020-08-31 10:09:37 +00:00
d1ce16d2b1 Bump runner from 1.2.0 to 1.3.0 (#932) 2020-08-31 10:03:20 +00:00
54fb01cd0d Bump firebase-inappmessaging-display-ktx from 19.1.0 to 19.1.1 (#930) 2020-08-31 09:42:25 +00:00
370cfbf22a Bump core from 1.2.0 to 1.3.0 (#929) 2020-08-31 09:42:18 +00:00
d198a2ba21 Merge tag '0.20.0' into develop
Version 0.20.0
2020-08-29 23:57:20 +02:00
580ad58dd6 Merge branch 'release/0.20.0' into master 2020-08-29 23:57:14 +02:00
b5f7c5f318 Version 0.20.0 2020-08-29 23:57:06 +02:00
7e2de594a4 Fix messages after 20.09 update (#916) 2020-08-29 23:53:55 +02:00
36984e08b5 Bump coil from 0.11.0 to 1.0.0-rc1 (#920) 2020-08-29 11:12:27 +00:00
f8031e1eca Bump appcompat from 1.2.0-rc02 to 1.2.0 (#926) 2020-08-29 09:39:31 +00:00
5bc49b2e74 Bump firebase-inappmessaging-ktx from 19.1.0 to 19.1.1 (#918) 2020-08-29 09:29:00 +00:00
ba81b4b465 Bump junit from 1.1.1 to 1.1.2 (#924) 2020-08-29 09:20:43 +00:00
5cc31a7c5e Bump firebase-crashlytics-gradle from 2.2.0 to 2.2.1 (#923) 2020-08-29 09:14:35 +00:00
de70719d38 Bump appcompat-resources from 1.1.0 to 1.2.0 (#922) 2020-08-29 09:09:41 +00:00
9f315b3af1 Bump firebase-analytics from 17.4.4 to 17.5.0 (#917) 2020-08-29 08:50:49 +00:00
d5187d1808 Save semesters with students during registration (#915) 2020-08-26 10:25:01 +02:00
ca7d977342 Bump work_manager from 2.3.4 to 2.4.0 (#910)
Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-08-02 18:41:22 +02:00
062985c5a0 Bump firebase-messaging from 20.2.3 to 20.2.4 (#912) 2020-08-02 16:30:31 +00:00
8922d7d48d Bump core-ktx from 1.3.0 to 1.3.1 (#911) 2020-08-02 16:30:06 +00:00
3ba16f2903 Migrate to dagger hilt (#909) 2020-08-02 18:29:41 +02:00
6a1a347579 Migrate workers and app widgets to coroutines (#907) 2020-07-27 13:20:45 +02:00
6c4f27aff5 Remove threetenabp (#908) 2020-07-27 11:44:30 +02:00
f6dce0fbda Bump kotlinx-coroutines-rx2 from 1.3.7 to 1.3.8 (#901) 2020-07-19 12:00:08 +00:00
1ac42bb56d Migrate presenters from rxjava to coroutines flow (#894) 2020-07-19 13:30:29 +02:00
b0a674b471 Bump kotlinx-coroutines-core from 1.3.7 to 1.3.8 (#903) 2020-07-19 11:25:48 +00:00
1c1a90c12f Bump firebase-inappmessaging-display-ktx from 19.0.7 to 19.1.0 (#905) 2020-07-18 23:51:24 +00:00
d4ee1f8b98 Bump firebase-crashlytics from 17.1.0 to 17.1.1 (#902) 2020-07-18 23:32:38 +00:00
152382a0c9 Bump firebase-analytics from 17.4.3 to 17.4.4 (#906) 2020-07-18 23:31:27 +00:00
776972514a Bump firebase-inappmessaging-ktx from 19.0.7 to 19.1.0 (#904) 2020-07-18 23:00:03 +00:00
e072bf9fe3 Bump gradle from 4.0.0 to 4.0.1 (#900) 2020-07-18 22:42:05 +00:00
927415f9a3 Bump dagger from 2.28.1 to 2.28.3 (#899) 2020-07-18 22:41:41 +00:00
76b2ab1f25 Bump firebase-messaging from 20.2.1 to 20.2.3 (#898) 2020-07-18 22:40:01 +00:00
c9a0bbda01 New Crowdin updates (#888) 2020-07-12 17:55:27 +02:00
cbabe44461 Bump about_libraries from 8.2.0 to 8.3.0 (#896) 2020-07-11 09:41:12 +00:00
5c313f986c Bump swiperefreshlayout from 1.1.0-rc01 to 1.1.0 (#897) 2020-07-02 10:15:34 +00:00
f2682c6d30 Bump dagger from 2.28 to 2.28.1 (#895) 2020-07-02 10:15:28 +00:00
3571f8bd04 Bump firebase-crashlytics from 17.0.1 to 17.1.0 (#892) 2020-06-20 16:00:47 +00:00
dfcd5fc4d0 Bump firebase-crashlytics-gradle from 2.1.1 to 2.2.0 (#889) 2020-06-20 15:38:08 +00:00
b8ea0ab0f9 Bump firebase-messaging from 20.2.0 to 20.2.1 (#890) 2020-06-20 15:37:34 +00:00
4434d6f024 Migrate repositories from rxjava to coroutines (#885) 2020-06-20 15:07:57 +02:00
8cee882c08 Merge tag '0.19.0' into develop
Version 0.19.0
2020-06-14 22:44:41 +02:00
9f87b92937 Merge branch 'release/0.19.0' 2020-06-14 22:44:36 +02:00
c13f12f729 Version 0.19.0 2020-06-14 22:40:36 +02:00
dfe7981e7f New Crowdin translations (#874)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
2020-06-14 22:37:58 +02:00
6e1ddb482e Message fuzzy search (#869) 2020-06-14 14:05:24 +02:00
924bcb0d64 Message sharing and printing (#866) 2020-06-14 00:50:09 +02:00
a6682c9b73 Add predicted and final grade notifications (#872) 2020-06-13 17:11:18 +02:00
a529836937 Fix lint errors (#873) 2020-06-13 14:12:01 +02:00
a05da2656a Add account headers in student picker (#871) 2020-06-12 21:35:51 +02:00
30af77614e Fix showing summary summary for subjects without partial grades (#877) 2020-06-11 14:38:04 +02:00
eedaa63771 Bump sonarqube-gradle-plugin from 2.8 to 3.0 (#882) 2020-06-11 12:05:54 +00:00
f151f7bd62 Bump about_libraries from 8.1.6 to 8.2.0 (#879) 2020-06-11 12:05:30 +00:00
00943717a2 Bump firebase-crashlytics from 17.0.0 to 17.0.1 (#880) 2020-06-11 12:02:38 +00:00
8cce81585a Bump firebase-analytics from 17.4.2 to 17.4.3 (#881) 2020-06-11 12:02:10 +00:00
5529ffcf73 Bump fragment-ktx from 1.2.4 to 1.2.5 (#878) 2020-06-11 12:01:52 +00:00
7e6f892e23 Merge tag '0.18.3' into develop
Version 0.18.3
2020-06-10 19:18:31 +02:00
d3a6ea5acf Merge branch 'release/0.18.3' 2020-06-10 19:18:23 +02:00
674a78b661 Version 0.18.3 2020-06-10 19:18:09 +02:00
5c84c8d5b1 Fix force average calc from two semesters (#870) 2020-06-10 17:28:49 +02:00
522a36d670 Fix message deleting (#875) 2020-06-10 16:26:45 +02:00
2d0cfc3e8e Merge tag '0.18.2' into develop
Version 0.18.2
2020-06-02 17:08:02 +02:00
435 changed files with 11004 additions and 4982 deletions

3
.gitignore vendored
View File

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

View File

@ -4,7 +4,16 @@
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> <option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value> <value>
<package name="kotlinx.android.synthetic" withSubpackages="true" static="false" /> <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" />
</value>
</option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value> </value>
</option> </option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />

View File

@ -3,8 +3,8 @@ jdk: oraclejdk8
env: env:
global: global:
- ANDROID_API_LEVEL=29 - ANDROID_API_LEVEL=30
- ANDROID_BUILD_TOOLS_VERSION=29.0.3 - ANDROID_BUILD_TOOLS_VERSION=30.0.2
cache: cache:
directories: directories:
@ -14,7 +14,7 @@ cache:
branches: branches:
only: only:
- develop - develop
- 0.18.2 - 0.22.1
android: android:
licenses: licenses:
@ -28,28 +28,32 @@ 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

View File

@ -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)

View File

@ -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)

View File

@ -1,6 +1,7 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.github.triplet.play' apply plugin: 'com.github.triplet.play'
apply plugin: 'com.mikepenz.aboutlibraries.plugin' apply plugin: 'com.mikepenz.aboutlibraries.plugin'
@ -9,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 61 versionCode 74
versionName "0.18.2" versionName "0.22.1"
multiDexEnabled true multiDexEnabled true
resValue "string", "app_name", "Wulkanowy" resValue "string", "app_name", "Wulkanowy"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -28,7 +29,7 @@ android {
] ]
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
arguments = [ arguments += [
"room.schemaLocation": "$projectDir/schemas".toString(), "room.schemaLocation": "$projectDir/schemas".toString(),
"room.incremental" : "true" "room.incremental" : "true"
] ]
@ -68,17 +69,31 @@ 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"
]
} }
} }
viewBinding { buildFeatures {
enabled = true viewBinding = true
} }
lintOptions { lintOptions {
@ -86,12 +101,14 @@ android {
} }
compileOptions { compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
} }
packagingOptions { packagingOptions {
@ -109,14 +126,15 @@ play {
serviceAccountCredentials = file('key.p12') serviceAccountCredentials = file('key.p12')
defaultToAppBundles = false defaultToAppBundles = false
track = 'alpha' track = 'alpha'
updatePriority = 1
} }
ext { ext {
work_manager = "2.3.4" work_manager = "2.4.0"
room = "2.2.5" room = "2.2.5"
dagger = "2.28" chucker = "3.3.0"
chucker = "3.2.0" mockk = "1.10.2"
mockk = "1.9.2" moshi = "1.11.0"
} }
configurations.all { configurations.all {
@ -124,87 +142,89 @@ configurations.all {
} }
dependencies { dependencies {
implementation "io.github.wulkanowy:sdk:0.18.2" implementation "io.github.wulkanowy:sdk:0.22.1"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "androidx.core:core-ktx:1.2.0" implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
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-rc01" implementation "androidx.appcompat:appcompat:1.2.0"
implementation "androidx.appcompat:appcompat-resources:1.1.0" implementation "androidx.appcompat:appcompat-resources:1.2.0"
implementation "androidx.fragment:fragment-ktx:1.2.4" implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.preference:preference-ktx:1.1.1" implementation "androidx.preference:preference-ktx:1.1.1"
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-rc01" 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.0.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"
implementation "androidx.work:work-runtime-ktx:$work_manager" implementation "androidx.work:work-runtime-ktx:$work_manager"
implementation "androidx.work:work-rxjava2:$work_manager"
implementation "androidx.work:work-gcm:$work_manager" implementation "androidx.work:work-gcm:$work_manager"
implementation 'com.github.PaulinaSadowska:RxWorkManagerObservers:1.0.0' implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation "androidx.room:room-runtime:$room" implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-rxjava2:$room"
implementation "androidx.room:room-ktx:$room" implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room" kapt "androidx.room:room-compiler:$room"
implementation "com.google.dagger:dagger-android-support:$dagger" implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:dagger-compiler:$dagger" kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
kapt "com.google.dagger:dagger-android-processor:$dagger" implementation 'androidx.hilt:hilt-work:1.0.0-alpha02'
implementation "com.squareup.inject:assisted-inject-annotations-dagger2:0.5.2" kapt 'androidx.hilt:hilt-compiler:1.0.0-alpha02'
kapt "com.squareup.inject:assisted-inject-processor-dagger2:0.5.2"
implementation "com.aurelhubert:ahbottomnavigation:2.3.4" implementation "com.aurelhubert:ahbottomnavigation:2.3.4"
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.github.pwittchen:reactivenetwork-rx2:3.0.8" implementation "com.squareup.moshi:moshi:$moshi"
implementation "io.reactivex.rxjava2:rxandroid:2.1.1" implementation "com.squareup.moshi:moshi-adapters:$moshi"
implementation "io.reactivex.rxjava2:rxjava:2.2.19" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi"
implementation "com.google.code.gson:gson:2.8.6"
implementation "com.jakewharton.threetenabp:threetenabp:1.2.4"
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:0.11.0" implementation "io.coil-kt:coil:1.0.0-rc3"
implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation "io.github.wulkanowy:AppKillerManager:3.0.0"
implementation 'me.xdrop:fuzzywuzzy:1.3.1'
playImplementation 'com.google.firebase:firebase-analytics:17.4.2' playImplementation 'com.google.firebase:firebase-analytics:17.6.0'
playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.0.7' playImplementation 'com.google.firebase:firebase-inappmessaging-display-ktx:19.1.1'
playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.0.7" playImplementation "com.google.firebase:firebase-inappmessaging-ktx:19.1.1"
playImplementation 'com.google.firebase:firebase-messaging:20.2.0' playImplementation 'com.google.firebase:firebase-messaging:20.3.0'
playImplementation 'com.google.firebase:firebase-crashlytics:17.0.0' playImplementation 'com.google.firebase:firebase-crashlytics:17.2.2'
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.4.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.4.1.300'
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.threeten:threetenbp:1.4.4" testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.9'
testImplementation "org.mockito:mockito-inline:3.3.3"
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.1" 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"
androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version" androidTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
androidTestImplementation "org.mockito:mockito-android:3.3.3"
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'

View File

@ -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,

View File

@ -30,13 +30,6 @@
-dontwarn javax.annotation.** -dontwarn javax.annotation.**
#Config for ReactiveNetwork
-dontwarn com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
-dontwarn io.reactivex.functions.Function
-dontwarn rx.internal.util.**
-dontwarn sun.misc.Unsafe
#Config for MPAndroidChart #Config for MPAndroidChart
-keep class com.github.mikephil.charting.** { *; } -keep class com.github.mikephil.charting.** { *; }

View File

@ -1741,4 +1741,4 @@
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd101f5a26a024f62e6fee161e421b882')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd101f5a26a024f62e6fee161e421b882')"
] ]
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data
import io.github.wulkanowy.utils.DispatchersProvider
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
class TestDispatchersProvider : DispatchersProvider() {
override val backgroundThread: CoroutineDispatcher
get() = Dispatchers.Unconfined
}

View File

@ -4,6 +4,7 @@ import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL import android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -29,7 +30,7 @@ class Migration12Test : AbstractMigrationTest() {
helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
val db = getMigratedRoomDatabase() val db = getMigratedRoomDatabase()
val students = db.studentDao.loadAll().blockingGet() val students = runBlocking { db.studentDao.loadAll() }
assertEquals(2, students.size) assertEquals(2, students.size)
@ -58,7 +59,7 @@ class Migration12Test : AbstractMigrationTest() {
helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
val db = getMigratedRoomDatabase() val db = getMigratedRoomDatabase()
val students = db.studentDao.loadAll().blockingGet() val students = runBlocking { db.studentDao.loadAll() }
assertEquals(1, students.size) assertEquals(1, students.size)
@ -84,7 +85,7 @@ class Migration12Test : AbstractMigrationTest() {
helper.runMigrationsAndValidate(dbName, 12, true, Migration12()) helper.runMigrationsAndValidate(dbName, 12, true, Migration12())
val db = getMigratedRoomDatabase() val db = getMigratedRoomDatabase()
val students = db.studentDao.loadAll().blockingGet() val students = runBlocking { db.studentDao.loadAll() }
assertEquals(3, students.size) assertEquals(3, students.size)

View File

@ -5,10 +5,11 @@ import android.database.sqlite.SQLiteDatabase
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.github.wulkanowy.data.db.Converters import io.github.wulkanowy.data.db.Converters
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.threeten.bp.LocalDate.of import java.time.LocalDate.of
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -26,7 +27,7 @@ class Migration13Test : AbstractMigrationTest() {
helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
val db = getMigratedRoomDatabase() val db = getMigratedRoomDatabase()
val students = db.studentDao.loadAll().blockingGet() val students = runBlocking { db.studentDao.loadAll() }
assertEquals(3, students.size) assertEquals(3, students.size)
@ -60,7 +61,7 @@ class Migration13Test : AbstractMigrationTest() {
helper.runMigrationsAndValidate(dbName, 13, true, Migration13()) helper.runMigrationsAndValidate(dbName, 13, true, Migration13())
val db = getMigratedRoomDatabase() val db = getMigratedRoomDatabase()
val students = db.studentDao.loadAll().blockingGet() val students = runBlocking { db.studentDao.loadAll() }
assertEquals(2, students.size) assertEquals(2, students.size)
@ -120,23 +121,23 @@ class Migration13Test : AbstractMigrationTest() {
assertEquals(2, first.diaryId) assertEquals(2, first.diaryId)
} }
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { semesters ->
assertTrue { it.single { it.second }.second } assertTrue { semesters.single { it.second }.second }
assertEquals(1970, it[0].first.schoolYear) assertEquals(1970, semesters[0].first.schoolYear)
assertEquals(of(1970, 1, 1), it[0].first.end) assertEquals(of(1970, 1, 1), semesters[0].first.end)
assertEquals(of(1970, 1, 1), it[0].first.start) assertEquals(of(1970, 1, 1), semesters[0].first.start)
assertFalse(it[0].second) assertFalse(semesters[0].second)
assertFalse(it[1].second) assertFalse(semesters[1].second)
assertFalse(it[2].second) assertFalse(semesters[2].second)
assertTrue(it[3].second) assertTrue(semesters[3].second)
} }
getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { getSemesters(db, "SELECT * FROM Semesters WHERE student_id = 2 AND class_id = 5").let { semesters ->
assertTrue { it.single { it.second }.second } assertTrue { semesters.single { it.second }.second }
assertFalse(it[0].second) assertFalse(semesters[0].second)
assertFalse(it[1].second) assertFalse(semesters[1].second)
assertFalse(it[2].second) assertFalse(semesters[2].second)
assertTrue(it[3].second) assertTrue(semesters[3].second)
} }
} }

View File

@ -0,0 +1,124 @@
package io.github.wulkanowy.data.db.migrations
import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Test
import kotlin.random.Random
class Migration27Test : AbstractMigrationTest() {
@Test
fun userWithoutCorrespondingUnit() {
with(helper.createDatabase(dbName, 26)) {
createStudent(this, 321, 123, "Jan Student")
createUnit(this, 9999, "Unit Jan")
close()
}
helper.runMigrationsAndValidate(dbName, 27, true, Migration27())
val db = getMigratedRoomDatabase()
val students = runBlocking { db.studentDao.loadAll() }
assertEquals(1, students.size)
with(students[0]) {
assertEquals(321, id)
assertEquals(123, userLoginId)
assertEquals("Student Jan", userName)
}
}
@Test
fun userWithCorrespondingUnit() {
with(helper.createDatabase(dbName, 26)) {
createStudent(this, 1, 2, "Jan Kowalski Student")
createUnit(this, 2, "Unit Jan")
close()
}
helper.runMigrationsAndValidate(dbName, 27, true, Migration27())
val db = getMigratedRoomDatabase()
val students = runBlocking { db.studentDao.loadAll() }
assertEquals(1, students.size)
with(students[0]) {
assertEquals(1, id)
assertEquals(2, userLoginId)
assertEquals("Unit Jan", userName)
}
}
@Test
fun studentAccountAndParentAccountWithCorrespondingUnits() {
with(helper.createDatabase(dbName, 26)) {
createStudent(this, 1, 222, "Jan Student")
createStudent(this, 2, 333, "Jan Parent")
createUnit(this, 222, "Unit Jan")
createUnit(this, 333, "Unit Tomasz")
close()
}
helper.runMigrationsAndValidate(dbName, 27, true, Migration27())
val db = getMigratedRoomDatabase()
val students = runBlocking { db.studentDao.loadAll() }
assertEquals(2, students.size)
with(students[0]) {
assertEquals(1, id)
assertEquals(222, userLoginId)
assertEquals("Unit Jan", userName)
}
with(students[1]) {
assertEquals(2, id)
assertEquals(333, userLoginId)
assertEquals("Unit Tomasz", userName)
}
}
private fun createStudent(db: SupportSQLiteDatabase, id: Long, userLoginId: Int, studentName: String) {
db.insert("Students", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("id", id)
put("scrapper_base_url", "https://fakelog.cf")
put("mobile_base_url", "")
put("login_mode", "SCRAPPER")
put("login_type", "STANDARD")
put("certificate_key", "")
put("private_key", "")
put("is_parent", false)
put("email", "jan@fakelog.cf")
put("password", "******")
put("symbol", "Default")
put("school_short", "")
put("class_name", "")
put("student_id", Random.nextInt())
put("class_id", Random.nextInt())
put("school_id", "123")
put("school_name", "Wulkan first class school")
put("is_current", false)
put("registration_date", "0")
put("user_login_id", userLoginId)
put("student_name", studentName)
})
}
private fun createUnit(db: SupportSQLiteDatabase, senderId: Int, senderName: String) {
db.insert("ReportingUnits", SQLiteDatabase.CONFLICT_FAIL, ContentValues().apply {
put("student_id", Random.nextInt())
put("real_id", Random.nextInt())
put("short", "SHORT")
put("roles", "[0]")
put("sender_id", senderId)
put("sender_name", senderName)
})
}
}

View File

@ -1,7 +1,9 @@
package io.github.wulkanowy.data.repositories package io.github.wulkanowy.data.repositories
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 org.threeten.bp.LocalDateTime import java.time.LocalDate.now
import java.time.LocalDateTime
fun getStudent(): Student { fun getStudent(): Student {
return Student( return Student(
@ -11,6 +13,7 @@ fun getStudent(): Student {
scrapperBaseUrl = "fakelog.cf", scrapperBaseUrl = "fakelog.cf",
loginType = "AUTO", loginType = "AUTO",
isCurrent = true, isCurrent = true,
userName = "",
studentName = "", studentName = "",
schoolShortName = "", schoolShortName = "",
schoolName = "", schoolName = "",
@ -27,3 +30,16 @@ fun getStudent(): Student {
isParent = false isParent = false
) )
} }
fun getSemester() = Semester(
semesterId = 1,
studentId = 1,
classId = 1,
diaryId = 2,
diaryName = "",
end = now(),
schoolYear = 2019,
semesterName = 1,
start = now(),
unitId = 1
)

View File

@ -1,19 +0,0 @@
package io.github.wulkanowy.data.repositories
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingStrategy
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.error.ErrorHandler
import io.reactivex.Observable
import io.reactivex.Single
class TestInternetObservingStrategy : InternetObservingStrategy {
override fun checkInternetConnectivity(host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Single<Boolean> {
return Single.just(true)
}
override fun observeInternetConnectivity(initialIntervalInMs: Int, intervalInMs: Int, host: String?, port: Int, timeoutInMs: Int, httpResponse: Int, errorHandler: ErrorHandler?): Observable<Boolean> {
return Observable.just(true)
}
override fun getDefaultPingHost() = "localhost"
}

View File

@ -6,12 +6,15 @@ 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.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate.now import java.time.LocalDate
import org.threeten.bp.LocalDate.of import java.time.LocalDate.now
import java.time.LocalDate.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -34,20 +37,47 @@ class AttendanceLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
attendanceLocal.saveAttendance(listOf( val list = listOf(
Attendance(1, 2, 3, of(2018, 9, 10), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name), getAttendanceEntity(
Attendance(1, 2, 3, of(2018, 9, 14), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.WAITING.name),
Attendance(1, 2, 3, of(2018, 9, 17), 0, "", "", false, false, false, false, false, false, false, SentExcuseStatus.ACCEPTED.name)
))
val attendance = attendanceLocal
.getAttendance(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1),
of(2018, 9, 10), of(2018, 9, 10),
of(2018, 9, 14) SentExcuseStatus.ACCEPTED
),
getAttendanceEntity(
of(2018, 9, 14),
SentExcuseStatus.WAITING
),
getAttendanceEntity(
of(2018, 9, 17),
SentExcuseStatus.ACCEPTED
) )
.blockingGet() )
runBlocking { attendanceLocal.saveAttendance(list) }
val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
val attendance = runBlocking { attendanceLocal.getAttendance(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
assertEquals(2, attendance.size) assertEquals(2, attendance.size)
assertEquals(attendance[0].date, of(2018, 9, 10)) assertEquals(attendance[0].date, of(2018, 9, 10))
assertEquals(attendance[1].date, of(2018, 9, 14)) assertEquals(attendance[1].date, of(2018, 9, 14))
} }
private fun getAttendanceEntity(
date: LocalDate,
excuseStatus: SentExcuseStatus
) = Attendance(
studentId = 1,
diaryId = 2,
timeId = 3,
date = date,
number = 0,
subject = "",
name = "",
presence = false,
absence = false,
exemption = false,
lateness = false,
excused = false,
deleted = false,
excusable = false,
excuseStatus = excuseStatus.name
)
} }

View File

@ -6,13 +6,15 @@ 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.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDate.now import java.time.LocalDate.now
import org.threeten.bp.LocalDate.of import java.time.LocalDate.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -24,7 +26,8 @@ class CompletedLessonsLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room
.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao) completedLessonsLocal = CompletedLessonsLocal(testDb.completedLessonsDao)
} }
@ -36,18 +39,15 @@ class CompletedLessonsLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
completedLessonsLocal.saveCompletedLessons(listOf( val list = listOf(
getCompletedLesson(of(2018, 9, 10), 1), getCompletedLesson(of(2018, 9, 10), 1),
getCompletedLesson(of(2018, 9, 14), 2), getCompletedLesson(of(2018, 9, 14), 2),
getCompletedLesson(of(2018, 9, 17), 3) getCompletedLesson(of(2018, 9, 17), 3)
)) )
runBlocking { completedLessonsLocal.saveCompletedLessons(list) }
val completed = completedLessonsLocal val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
.getCompletedLessons(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1), val completed = runBlocking { completedLessonsLocal.getCompletedLessons(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
of(2018, 9, 10),
of(2018, 9, 14)
)
.blockingGet()
assertEquals(2, completed.size) assertEquals(2, completed.size)
assertEquals(completed[0].date, of(2018, 9, 10)) assertEquals(completed[0].date, of(2018, 9, 10))
assertEquals(completed[1].date, of(2018, 9, 14)) assertEquals(completed[1].date, of(2018, 9, 14))

View File

@ -6,12 +6,14 @@ 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.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate.now import java.time.LocalDate.now
import org.threeten.bp.LocalDate.of import java.time.LocalDate.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -34,18 +36,15 @@ class ExamLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
examLocal.saveExams(listOf( val list = listOf(
Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""), Exam(1, 2, of(2018, 9, 10), now(), "", "", "", "", "", ""),
Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""), Exam(1, 2, of(2018, 9, 14), now(), "", "", "", "", "", ""),
Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "") Exam(1, 2, of(2018, 9, 17), now(), "", "", "", "", "", "")
)) )
runBlocking { examLocal.saveExams(list) }
val exams = examLocal val semester = Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1)
.getExams(Semester(1, 2, "", 1, 3, 2019, now(), now(), 1, 1), val exams = runBlocking { examLocal.getExams(semester, of(2018, 9, 10), of(2018, 9, 14)).first() }
of(2018, 9, 10),
of(2018, 9, 14)
)
.blockingGet()
assertEquals(2, exams.size) assertEquals(2, exams.size)
assertEquals(exams[0].date, of(2018, 9, 10)) assertEquals(exams[0].date, of(2018, 9, 10))
assertEquals(exams[1].date, of(2018, 9, 14)) assertEquals(exams[1].date, of(2018, 9, 14))

View File

@ -5,12 +5,14 @@ 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.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDate.now import java.time.LocalDate.now
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -22,7 +24,8 @@ class GradeLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room
.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
} }
@ -34,17 +37,16 @@ class GradeLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
gradeLocal.saveGrades(listOf( val list = listOf(
createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1), createGradeLocal(5, 3.0, LocalDate.of(2018, 9, 10), "", 1),
createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2), createGradeLocal(4, 4.0, LocalDate.of(2019, 2, 27), "", 2),
createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2) createGradeLocal(3, 5.0, LocalDate.of(2019, 2, 28), "", 2)
)) )
runBlocking { gradeLocal.saveGrades(list) }
val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1) val semester = Semester(1, 2, "", 2019, 2, 1, now(), now(), 1, 1)
val grades = gradeLocal val grades = runBlocking { gradeLocal.getGradesDetails(semester).first() }
.getGradesDetails(semester)
.blockingGet()
assertEquals(2, grades.size) assertEquals(2, grades.size)
assertEquals(grades[0].date, LocalDate.of(2019, 2, 27)) assertEquals(grades[0].date, LocalDate.of(2019, 2, 27))

View File

@ -5,23 +5,25 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Grade
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.TestInternetObservingStrategy
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Grade
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.reactivex.Single import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate.of import java.time.LocalDate.of
import org.threeten.bp.LocalDateTime import java.time.LocalDateTime
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -30,19 +32,12 @@ import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class GradeRepositoryTest { class GradeRepositoryTest {
@MockK
private lateinit var mockSdk: Sdk
private val settings = InternetObservingSettings.builder()
.strategy(TestInternetObservingStrategy())
.build()
@MockK @MockK
private lateinit var semesterMock: Semester private lateinit var semesterMock: Semester
@MockK
private lateinit var studentMock: Student private lateinit var studentMock: Student
@MockK
private lateinit var gradeRemote: GradeRemote private lateinit var gradeRemote: GradeRemote
private lateinit var gradeLocal: GradeLocal private lateinit var gradeLocal: GradeLocal
@ -54,14 +49,12 @@ class GradeRepositoryTest {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao) gradeLocal = GradeLocal(testDb.gradeDao, testDb.gradeSummaryDao)
gradeRemote = GradeRemote(mockSdk) studentMock = getStudentMock()
every { studentMock.registrationDate } returns LocalDateTime.of(2019, 2, 27, 12, 0)
every { semesterMock.studentId } returns 1 every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 1 every { semesterMock.diaryId } returns 1
every { semesterMock.schoolYear } returns 2019 every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1 every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
} }
@After @After
@ -71,15 +64,18 @@ class GradeRepositoryTest {
@Test @Test
fun markOlderThanRegisterDateAsRead() { fun markOlderThanRegisterDateAsRead() {
every { mockSdk.getGrades(1) } returns Single.just(listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"), createGradeLocal(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"), createGradeLocal(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
createGradeApi(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"), createGradeLocal(5, 4.0, of(2019, 2, 27), "Ocena z dnia logowania"),
createGradeApi(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza") createGradeLocal(5, 4.0, of(2019, 2, 28), "Ocena jeszcze nowsza")
) to emptyList()) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date } GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
}
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -89,21 +85,25 @@ class GradeRepositoryTest {
@Test @Test
fun mitigateOldGradesNotifications() { fun mitigateOldGradesNotifications() {
gradeLocal.saveGrades(listOf( val list = listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"), createGradeLocal(4, 4.0, of(2019, 2, 26), "Druga"),
createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia") createGradeLocal(3, 5.0, of(2019, 2, 27), "Trzecia")
)) )
runBlocking { gradeLocal.saveGrades(list) }
every { mockSdk.getGrades(1) } returns Single.just(listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"), createGradeLocal(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"), createGradeLocal(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"), createGradeLocal(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa") createGradeLocal(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
) to emptyList()) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet().first.sortedByDescending { it.date } GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!.first.sortedByDescending { it.date }
}
assertFalse { grades[0].isRead } assertFalse { grades[0].isRead }
assertFalse { grades[1].isRead } assertFalse { grades[1].isRead }
@ -113,70 +113,109 @@ class GradeRepositoryTest {
@Test @Test
fun subtractLocaleDuplicateGrades() { fun subtractLocaleDuplicateGrades() {
gradeLocal.saveGrades(listOf( val list = listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) )
runBlocking { gradeLocal.saveGrades(list) }
every { mockSdk.getGrades(1) } returns Single.just(listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet() GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(2, grades.first.size) assertEquals(2, grades.first.size)
} }
@Test @Test
fun subtractRemoteDuplicateGrades() { fun subtractRemoteDuplicateGrades() {
gradeLocal.saveGrades(listOf( val list = listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) )
runBlocking { gradeLocal.saveGrades(list) }
every { mockSdk.getGrades(1) } returns Single.just(listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet() GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(3, grades.first.size) assertEquals(3, grades.first.size)
} }
@Test @Test
fun emptyLocal() { fun emptyLocal() {
gradeLocal.saveGrades(listOf()) runBlocking { gradeLocal.saveGrades(listOf()) }
every { mockSdk.getGrades(1) } returns Single.just(listOf( coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeApi(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
) to emptyList()) ) to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet() GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(3, grades.first.size) assertEquals(3, grades.first.size)
} }
@Test @Test
fun emptyRemote() { fun emptyRemote() {
gradeLocal.saveGrades(listOf( val list = listOf(
createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"), createGradeLocal(5, 3.0, of(2019, 2, 25), "Taka sama ocena"),
createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena") createGradeLocal(3, 5.0, of(2019, 2, 26), "Jakaś inna ocena")
)) )
runBlocking { gradeLocal.saveGrades(list) }
every { mockSdk.getGrades(1) } returns Single.just(emptyList<Grade>() to emptyList()) coEvery { gradeRemote.getGrades(studentMock, semesterMock) } returns (emptyList<Grade>() to emptyList())
val grades = GradeRepository(settings, gradeLocal, gradeRemote) val grades = runBlocking {
.getGrades(studentMock, semesterMock, true).blockingGet() GradeRepository(gradeLocal, gradeRemote)
.getGrades(studentMock, semesterMock, true)
.filter { it.status == Status.SUCCESS }.first().data!!
}
assertEquals(0, grades.first.size) assertEquals(0, grades.first.size)
} }
private fun getStudentMock() = Student(
scrapperBaseUrl = "http://fakelog.cf",
email = "jan@fakelog.cf",
certificateKey = "",
classId = 0,
className = "",
isCurrent = false,
isParent = false,
loginMode = Sdk.Mode.SCRAPPER.name,
loginType = "STANDARD",
mobileBaseUrl = "",
password = "",
privateKey = "",
registrationDate = LocalDateTime.of(2019, 2, 27, 12, 0),
schoolName = "",
schoolShortName = "test",
schoolSymbol = "",
studentId = 0,
studentName = "",
symbol = "",
userLoginId = 0,
userName = ""
)
} }

View File

@ -1,6 +1,6 @@
package io.github.wulkanowy.data.repositories.grade package io.github.wulkanowy.data.repositories.grade
import org.threeten.bp.LocalDate import java.time.LocalDate
import io.github.wulkanowy.sdk.pojo.Grade as GradeRemote import io.github.wulkanowy.sdk.pojo.Grade as GradeRemote
import io.github.wulkanowy.data.db.entities.Grade as GradeLocal import io.github.wulkanowy.data.db.entities.Grade as GradeLocal

View File

@ -7,11 +7,13 @@ import io.github.wulkanowy.data.db.AppDatabase
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.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.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate.now import java.time.LocalDate.now
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -35,40 +37,44 @@ class GradeStatisticsLocalTest {
@Test @Test
fun saveAndRead_subject() { fun saveAndRead_subject() {
gradeStatisticsLocal.saveGradesStatistics(listOf( val list = listOf(
getGradeStatistics("Matematyka", 2, 1), getGradeStatistics("Matematyka", 2, 1),
getGradeStatistics("Fizyka", 1, 2) getGradeStatistics("Fizyka", 1, 2)
)) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Matematyka").blockingGet() val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(1, stats.size) assertEquals(1, stats.size)
assertEquals(stats[0].subject, "Matematyka") assertEquals(stats[0].subject, "Matematyka")
} }
@Test @Test
fun saveAndRead_all() { fun saveAndRead_all() {
gradeStatisticsLocal.saveGradesStatistics(listOf( val list = listOf(
getGradeStatistics("Matematyka", 2, 1), getGradeStatistics("Matematyka", 2, 1),
getGradeStatistics("Chemia", 2, 1), getGradeStatistics("Chemia", 2, 1),
getGradeStatistics("Fizyka", 1, 2) getGradeStatistics("Fizyka", 1, 2)
)) )
runBlocking { gradeStatisticsLocal.saveGradesStatistics(list) }
val stats = gradeStatisticsLocal.getGradesStatistics(getSemester(), false, "Wszystkie").blockingGet() val stats = runBlocking { gradeStatisticsLocal.getGradesStatistics(getSemester(), false).first() }
assertEquals(3, stats.size) assertEquals(2, stats.size)
assertEquals(stats[0].subject, "Wszystkie") // assertEquals(3, stats.size)
assertEquals(stats[1].subject, "Matematyka") // assertEquals(stats[0].subject, "Wszystkie") // now in main repo
assertEquals(stats[2].subject, "Chemia") assertEquals(stats[0].subject, "Matematyka")
assertEquals(stats[1].subject, "Chemia")
} }
@Test @Test
fun saveAndRead_points() { fun saveAndRead_points() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf( val list = listOf(
getGradePointsStatistics("Matematyka", 2, 1), getGradePointsStatistics("Matematyka", 2, 1),
getGradePointsStatistics("Chemia", 2, 1), getGradePointsStatistics("Chemia", 2, 1),
getGradePointsStatistics("Fizyka", 1, 2) getGradePointsStatistics("Fizyka", 1, 2)
)) )
runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(list) }
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet() val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
with(stats[0]) { with(stats[0]) {
assertEquals(subject, "Matematyka") assertEquals(subject, "Matematyka")
assertEquals(others, 5.0) assertEquals(others, 5.0)
@ -78,18 +84,18 @@ class GradeStatisticsLocalTest {
@Test @Test
fun saveAndRead_subjectEmpty() { fun saveAndRead_subjectEmpty() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Matematyka").blockingGet() val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(null, stats) assertEquals(emptyList(), stats)
} }
@Test @Test
fun saveAndRead_allEmpty() { fun saveAndRead_allEmpty() {
gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) runBlocking { gradeStatisticsLocal.saveGradesPointsStatistics(listOf()) }
val stats = gradeStatisticsLocal.getGradesPointsStatistics(getSemester(), "Wszystkie").blockingGet() val stats = runBlocking { gradeStatisticsLocal.getGradesPointsStatistics(getSemester()).first() }
assertEquals(null, stats) assertEquals(emptyList(), stats)
} }
private fun getSemester(): Semester { private fun getSemester(): Semester {

View File

@ -5,14 +5,15 @@ 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.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
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 kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDateTime.now import java.time.LocalDateTime.now
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -24,7 +25,8 @@ class LuckyNumberLocalTest {
@Before @Before
fun createDb() { fun createDb() {
testDb = Room.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java) testDb = Room
.inMemoryDatabaseBuilder(ApplicationProvider.getApplicationContext(), AppDatabase::class.java)
.build() .build()
luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao) luckyNumberLocal = LuckyNumberLocal(testDb.luckyNumberDao)
} }
@ -36,14 +38,14 @@ class LuckyNumberLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
luckyNumberLocal.saveLuckyNumber(LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)) val number = LuckyNumber(1, LocalDate.of(2019, 1, 20), 14)
runBlocking { luckyNumberLocal.saveLuckyNumber(number) }
val luckyNumber = luckyNumberLocal.getLuckyNumber(Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", 1, false, now()), val student = Student("", "", "", "", "", "", false, "", "", "", 1, 1, "", "", "", "", "", "", 1, false, now())
LocalDate.of(2019, 1, 20) val luckyNumber = runBlocking { luckyNumberLocal.getLuckyNumber(student, LocalDate.of(2019, 1, 20)).first() }
).blockingGet()
assertEquals(1, luckyNumber.studentId) assertEquals(1, luckyNumber?.studentId)
assertEquals(LocalDate.of(2019, 1, 20), luckyNumber.date) assertEquals(LocalDate.of(2019, 1, 20), luckyNumber?.date)
assertEquals(14, luckyNumber.luckyNumber) assertEquals(14, luckyNumber?.luckyNumber)
} }
} }

View File

@ -7,11 +7,12 @@ import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDateTime import java.time.LocalDateTime
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -35,17 +36,21 @@ class RecipientLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
recipientLocal.saveRecipients(listOf( val list = listOf(
Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"), Recipient(1, "2rPracownik", "Kowalski Jan", "Kowalski Jan [KJ] - Pracownik (Fake123456)", 3, 4, 2, "hash"),
Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"), Recipient(1, "3rPracownik", "Kowalska Karolina", "Kowalska Karolina [KK] - Pracownik (Fake123456)", 4, 4, 2, "hash"),
Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash") Recipient(1, "4rPracownik", "Krupa Stanisław", "Krupa Stanisław [KS] - Uczeń (Fake123456)", 5, 4, 1, "hash")
)) )
runBlocking { recipientLocal.saveRecipients(list) }
val recipients = recipientLocal.getRecipients( val student = Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", "", "", 1, true, LocalDateTime.now())
Student("fakelog.cf", "AUTO", "", "", "", "", false, "", "", "", 1, 0, "", "", "", "", "", 1, true, LocalDateTime.now()), val recipients = runBlocking {
2, recipientLocal.getRecipients(
ReportingUnit(1, 4, "", 0, "", emptyList()) student = student,
).blockingGet() role = 2,
unit = ReportingUnit(1, 4, "", 0, "", emptyList())
)
}
assertEquals(2, recipients.size) assertEquals(2, recipients.size)
assertEquals(1, recipients[0].studentId) assertEquals(1, recipients[0].studentId)

View File

@ -4,8 +4,10 @@ import android.content.Context
import androidx.room.Room 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.TestDispatchersProvider
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.repositories.getStudent import io.github.wulkanowy.data.repositories.getStudent
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -26,7 +28,7 @@ class StudentLocalTest {
val context = ApplicationProvider.getApplicationContext<Context>() val context = ApplicationProvider.getApplicationContext<Context>()
testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java) testDb = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
.build() .build()
studentLocal = StudentLocal(testDb.studentDao, context) studentLocal = StudentLocal(testDb.studentDao, TestDispatchersProvider(), context)
} }
@After @After
@ -36,9 +38,9 @@ class StudentLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
studentLocal.saveStudents(listOf(student)).blockingGet() runBlocking { studentLocal.saveStudents(listOf(student)) }
val student = studentLocal.getCurrentStudent(true).blockingGet() val student = runBlocking { studentLocal.getCurrentStudent(true) }
assertEquals("23", student.schoolSymbol) assertEquals("23", student?.schoolSymbol)
} }
} }

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.data.repositories.timetable package io.github.wulkanowy.data.repositories.timetable
import org.threeten.bp.LocalDateTime import java.time.LocalDateTime
import org.threeten.bp.LocalDateTime.now import java.time.LocalDateTime.now
import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal import io.github.wulkanowy.data.db.entities.Timetable as TimetableLocal
import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote import io.github.wulkanowy.sdk.pojo.Timetable as TimetableRemote

View File

@ -5,12 +5,14 @@ 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.Semester import io.github.wulkanowy.data.db.entities.Semester
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDateTime.of import java.time.LocalDateTime.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
@ -34,17 +36,21 @@ class TimetableLocalTest {
@Test @Test
fun saveAndReadTest() { fun saveAndReadTest() {
timetableDb.saveTimetable(listOf( val list = listOf(
createTimetableLocal(of(2018, 9, 10, 0, 0, 0), 1), createTimetableLocal(of(2018, 9, 10, 0, 0, 0), 1),
createTimetableLocal(of(2018, 9, 14, 0, 0, 0), 1), createTimetableLocal(of(2018, 9, 14, 0, 0, 0), 1),
createTimetableLocal(of(2018, 9, 17, 0, 0, 0), 1) createTimetableLocal(of(2018, 9, 17, 0, 0, 0), 1)
)) )
runBlocking { timetableDb.saveTimetable(list) }
val exams = timetableDb.getTimetable( val semester = Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1)
Semester(1, 2, "", 1, 1, 2019, LocalDate.now(), LocalDate.now(), 1, 1), val exams = runBlocking {
LocalDate.of(2018, 9, 10), timetableDb.getTimetable(
LocalDate.of(2018, 9, 14) semester = semester,
).blockingGet() startDate = LocalDate.of(2018, 9, 10),
endDate = LocalDate.of(2018, 9, 14)
).first()
}
assertEquals(2, exams.size) assertEquals(2, exams.size)
assertEquals(exams[0].date, LocalDate.of(2018, 9, 10)) assertEquals(exams[0].date, LocalDate.of(2018, 9, 10))

View File

@ -5,74 +5,48 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SdkSuppress import androidx.test.filters.SdkSuppress
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.AppDatabase import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.repositories.getSemester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.repositories.TestInternetObservingStrategy
import io.github.wulkanowy.data.repositories.getStudent import io.github.wulkanowy.data.repositories.getStudent
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.sdk.Sdk
import io.mockk.MockKAnnotations import io.mockk.MockKAnnotations
import io.mockk.every import io.mockk.coEvery
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.mockk import kotlinx.coroutines.flow.filter
import io.reactivex.Single import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDateTime.of import java.time.LocalDateTime.of
import kotlin.test.assertEquals import kotlin.test.assertEquals
@SdkSuppress(minSdkVersion = P) @SdkSuppress(minSdkVersion = P)
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class TimetableRepositoryTest { class TimetableRepositoryTest {
@MockK @MockK(relaxed = true)
private lateinit var mockSdk: Sdk
private val settings = InternetObservingSettings.builder()
.strategy(TestInternetObservingStrategy())
.build()
@MockK
private lateinit var studentMock: Student
private val student = getStudent()
@MockK
private lateinit var semesterMock: Semester
@MockK
private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper private lateinit var timetableNotificationSchedulerHelper: TimetableNotificationSchedulerHelper
@MockK
private lateinit var timetableRemote: TimetableRemote private lateinit var timetableRemote: TimetableRemote
private lateinit var timetableLocal: TimetableLocal private lateinit var timetableLocal: TimetableLocal
private lateinit var testDb: AppDatabase private lateinit var testDb: AppDatabase
private val student = getStudent()
private val semester = getSemester()
@Before @Before
fun initApi() { fun initApi() {
MockKAnnotations.init(this) MockKAnnotations.init(this)
testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build() testDb = Room.inMemoryDatabaseBuilder(getApplicationContext(), AppDatabase::class.java).build()
timetableLocal = TimetableLocal(testDb.timetableDao) timetableLocal = TimetableLocal(testDb.timetableDao)
timetableRemote = TimetableRemote(mockSdk)
every { timetableNotificationSchedulerHelper.scheduleNotifications(any(), any()) } returns mockk()
every { timetableNotificationSchedulerHelper.cancelScheduled(any(), any()) } returns mockk()
every { studentMock.studentId } returns 1
every { studentMock.studentName } returns "Jan Kowalski"
every { semesterMock.studentId } returns 1
every { semesterMock.diaryId } returns 2
every { semesterMock.schoolYear } returns 2019
every { semesterMock.semesterId } returns 1
every { mockSdk.switchDiary(any(), any()) } returns mockSdk
} }
@After @After
@ -82,23 +56,31 @@ class TimetableRepositoryTest {
@Test @Test
fun copyRoomToCompletedFromPrevious() { fun copyRoomToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf( runBlocking {
createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"), timetableLocal.saveTimetable(listOf(
createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"), createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "123", "Przyroda"),
createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"), createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "321", "Religia"),
createTimetableLocal(of(2019, 3, 5, 10, 30),3, "213", "W-F", "Jan Kowalski") createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "213", "W-F"),
)) createTimetableLocal(of(2019, 3, 5, 10, 30), 3, "213", "W-F", "Jan Kowalski")
))
}
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf( coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableRemote(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"), createTimetableLocal(of(2019, 3, 5, 8, 0), 1, "", "Przyroda"),
createTimetableRemote(of(2019, 3, 5, 8, 50), 2, "", "Religia"), createTimetableLocal(of(2019, 3, 5, 8, 50), 2, "", "Religia"),
createTimetableRemote(of(2019, 3, 5, 9, 40), 3, "", "W-F"), createTimetableLocal(of(2019, 3, 5, 9, 40), 3, "", "W-F"),
createTimetableRemote(of(2019, 3, 5, 10, 30), 4, "", "W-F") createTimetableLocal(of(2019, 3, 5, 10, 30), 4, "", "W-F")
)) )
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper) val lessons = runBlocking {
.getTimetable(student, semesterMock, LocalDate.of(2019, 3, 5), LocalDate.of(2019, 3, 5), true) TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
.blockingGet() student = student,
semester = semester,
start = LocalDate.of(2019, 3, 5),
end = LocalDate.of(2019, 3, 5),
forceRefresh = true
).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
}
assertEquals(4, lessons.size) assertEquals(4, lessons.size)
assertEquals("123", lessons[0].room) assertEquals("123", lessons[0].room)
@ -108,7 +90,7 @@ class TimetableRepositoryTest {
@Test @Test
fun copyTeacherToCompletedFromPrevious() { fun copyTeacherToCompletedFromPrevious() {
timetableLocal.saveTimetable(listOf( val list = listOf(
createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Paweł Poniedziałkowski", false), createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Wtorkowska", true), createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Wtorkowska", true),
@ -123,28 +105,35 @@ class TimetableRepositoryTest {
createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "", false), createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "", false),
createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "", true), createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "", true),
createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "", true) createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "", true)
)) )
runBlocking { timetableLocal.saveTimetable(list) }
every { mockSdk.getTimetable(any(), any()) } returns Single.just(listOf( coEvery { timetableRemote.getTimetable(student, semester, any(), any()) } returns listOf(
createTimetableRemote(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false), createTimetableLocal(of(2019, 12, 23, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
createTimetableRemote(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true), createTimetableLocal(of(2019, 12, 23, 8, 50), 2, "124", "Matematyka", "Jakub Wtorkowski", true),
createTimetableRemote(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false), createTimetableLocal(of(2019, 12, 23, 9, 40), 3, "125", "Język polski", "Joanna Poniedziałkowska", false),
createTimetableRemote(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true), createTimetableLocal(of(2019, 12, 23, 10, 40), 4, "126", "Język polski", "Joanna Wtorkowska", true),
createTimetableRemote(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false), createTimetableLocal(of(2019, 12, 24, 8, 0), 1, "123", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true), createTimetableLocal(of(2019, 12, 24, 8, 50), 2, "124", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false), createTimetableLocal(of(2019, 12, 24, 9, 40), 3, "125", "Język polski", "", false),
createTimetableRemote(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true), createTimetableLocal(of(2019, 12, 24, 10, 40), 4, "126", "Język polski", "", true),
createTimetableRemote(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false), createTimetableLocal(of(2019, 12, 25, 8, 0), 1, "123", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true), createTimetableLocal(of(2019, 12, 25, 8, 50), 2, "124", "Matematyka", "Paweł Czwartkowski", true),
createTimetableRemote(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false), createTimetableLocal(of(2019, 12, 25, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
createTimetableRemote(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true) createTimetableLocal(of(2019, 12, 25, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
)) )
val lessons = TimetableRepository(settings, timetableLocal, timetableRemote, timetableNotificationSchedulerHelper) val lessons = runBlocking {
.getTimetable(student, semesterMock, LocalDate.of(2019, 12, 23), LocalDate.of(2019, 12, 25), true) TimetableRepository(timetableLocal, timetableRemote, timetableNotificationSchedulerHelper).getTimetable(
.blockingGet() student = student,
semester = semester,
start = LocalDate.of(2019, 12, 23),
end = LocalDate.of(2019, 12, 25),
forceRefresh = true
).filter { it.status == Status.SUCCESS }.first().data.orEmpty()
}
assertEquals(12, lessons.size) assertEquals(12, lessons.size)

View File

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

View File

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

View File

@ -2,13 +2,12 @@
package io.github.wulkanowy.utils package io.github.wulkanowy.utils
import android.content.Context
import timber.log.Timber import timber.log.Timber
open class TimberTreeNoOp : Timber.Tree() { open class TimberTreeNoOp : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {} override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {}
} }
class CrashlyticsTree : TimberTreeNoOp() class CrashLogTree : TimberTreeNoOp()
class CrashlyticsExceptionTree : TimberTreeNoOp() class CrashLogExceptionTree : TimberTreeNoOp()

View File

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

View File

@ -0,0 +1,38 @@
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.onEvent("screen_view", Bundle().apply {
putString("screen_name", name)
putString("screen_class", activity::class.simpleName)
})
}
}

View 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)
connectCrash.log(priority, t?.stackTraceToString())
if (t != null) {
connectCrash.log(priority, t.stackTraceToString())
} else {
connectCrash.log(priority, StackTraceRecorder(format(priority, tag, message)).stackTraceToString())
}
}
}

View File

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

View File

@ -18,8 +18,7 @@
android:supportsRtl="false" android:supportsRtl="false"
android:theme="@style/WulkanowyTheme" android:theme="@style/WulkanowyTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute" tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
tools:replace="android:supportsRtl,android:allowBackup">
<activity <activity
android:name=".ui.modules.splash.SplashActivity" android:name=".ui.modules.splash.SplashActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
@ -111,6 +110,11 @@
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<meta-data
android:name="install_channel"
android:value="${install_channel}">
</meta-data>
<!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false --> <!-- workaround for https://github.com/firebase/firebase-android-sdk/issues/473 enabled:false -->
<!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html --> <!-- https://firebase.googleblog.com/2017/03/take-control-of-your-firebase-init-on.html -->
<provider <provider

View File

@ -0,0 +1,94 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>%SUBJECT% | Wulkanowy</title>
<style>
@page {
margin: 2.5cm;
size: A4;
}
body {
margin: 0;
font-family: sans-serif;
}
.title {
line-height: 1.5;
letter-spacing: 1pt;
font-size: 24pt;
font-weight: 200;
margin: 0 0 0.5cm;
}
.info {
margin: 0.5cm 0;
}
.info div {
font-size: 14pt;
font-weight: 400;
margin: 0.5cm 0;
}
h4 {
font-weight: 200;
text-transform: uppercase;
letter-spacing: 1pt;
font-size: 10pt;
margin: 0;
margin-bottom: 0.25cm;
font-family: sans-serif;
}
.content {
margin-top: 0.5cm;
font-size: 14pt;
font-weight: 400;
text-align: justify;
font-family: serif;
line-height: 1.5;
}
.content p {
page-break-after: auto;
page-break-inside: auto;
margin-bottom: 0.6cm;
}
.footer {
font-size: 11pt;
font-weight: 200;
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.5)
margin: 0;
margin-bottom: 0.5cm;
}
.footer .logo {
height: 0.5cm;
width: 0.5cm;
display: block;
margin-right: 0.2cm;
}
</style>
</head>
<body>
<h1 class="title">%SUBJECT%</h1>
<hr>
<div class="info">
%INFO%
</div>
<div class="footer">
<img src="wulkanowy-logo-black.svg" class="logo">
Wulkanowy Dzienniczek
</div>
<hr>
<div class="content">
<h4>Treść wiadomości</h4>
%CONTENT%
</div>
</body>
</html>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
xml:space="preserve"
width="1024"
height="1024"><metadata
id="metadata15"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><style
type="text/css"
id="style2">
.st0{fill:#D32F2F;}
.st1{fill:#AD2A2A;}
.st2{fill:#FFFFFF;}
</style><g
id="layer4"
style="display:none;fill:#808080"><rect
id="XMLID_57_"
x="0"
y="0"
class="st0"
width="3584"
height="1024"
style="display:inline;fill:#808080;stroke-width:1.02195609" /></g><g
id="layer3"
style="display:none;fill:#808080"><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 3046.8164,390.66602 3134.3164,542 v 91.33398 L 3524.9824,1024 H 3584 V 732.18359 L 3242.4824,390.66602 h -23.666 l -53.0352,94.63086 -94.6308,-94.63086 z"
id="path18992" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 2746.9824,390.66602 62,242.66796 L 3199.6484,1024 H 3584 V 940.68359 L 3033.9824,390.66602 h -21 l -21.9043,90.92773 -90.9277,-90.92773 h -18.5 l -25.4043,88.26367 -88.2637,-88.26367 z"
id="path18990" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 2620.8164,387.33398 c -18.6667,0 -35.1667,4.60982 -49.5,13.83204 -14.3333,9.11111 -25.4451,22.22287 -33.334,39.33398 -7.7778,17 -11.666,36.5549 -11.666,58.66602 v 25 c 0,34.44444 8.7216,61.83463 26.166,82.16796 L 2970.1484,1024 h 323.168 l -623.166,-623.16602 c -14.2222,-9 -30.6673,-13.5 -49.334,-13.5 z"
id="path18988" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 2293.4824,390.66602 V 633.33398 L 2684.1484,1024 h 423.336 l -633.334,-633.33398 h -20.334 v 139.66601 l -139.666,-139.66601 z"
id="path18984" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 1864.8164,390.66602 V 633.33398 L 2255.4824,1024 h 413.334 l -633.334,-633.33398 h -25.832 l -60.584,63.75 -63.75,-63.75 z"
id="path18978" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 1684.8164,390.66602 V 633.33398 L 2075.4824,1024 h 263.334 l -633.334,-633.33398 z"
id="path18976" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 1133.6504,390.66602 62,242.66796 L 1586.3164,1024 h 467.668 l -633.334,-633.33398 h -21 l -21.9043,90.92773 -90.9277,-90.92773 h -18.5 l -25.4043,88.26367 -88.2637,-88.26367 z"
id="path19059" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="m 1456.4824,390.66602 v 167.16796 c 0.5556,24.66667 8.5007,44 23.834,58 L 1888.4824,1024 h 372.168 l -633.334,-633.33398 h -20.666 V 520.5 l -129.834,-129.83398 z"
id="path18966" /><path
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;display:inline;fill:#808080;fill-opacity:1;stroke:none"
d="M 2146.3164,390.66602 2054.4824,633.33398 2445.1484,1024 h 354.002 l -633.334,-633.33398 z"
id="path18982" /><path
style="display:inline;fill:#808080;stroke-width:0.78179646"
d="M 637.15234,214.95703 487.75,364.35742 466.01562,386.0918 c 0.31273,0.31271 0.54872,0.54666 0.70508,0.85937 0.0782,0.23454 0.23432,0.54671 0.3125,0.78125 0.31272,0.54726 0.47071,1.17339 0.47071,1.79883 0.0782,0.54726 -0.0799,1.01725 -0.31446,1.48633 -0.23454,0.54725 -0.70285,1.40597 -1.09375,1.79687 l 150.8086,149.71485 -23.68946,23.6875 -12.74414,-12.74219 -13.44726,-13.44727 -78.80469,-78.80664 -11.17969,-11.17968 -7.5039,-7.50391 -35.41602,-35.17969 -3.08984,-0.98047 -4.33594,4.26367 v 0.46876 c 0,7.34888 0.38998,15.00865 -1.48633,22.20117 -0.85998,3.28355 -2.34444,6.25595 -4.14258,8.91406 -0.15636,0.15636 -0.23627,0.23426 -0.31445,0.39062 -1.87631,2.57993 -4.06471,4.84619 -6.48828,6.95704 -5.3944,4.53442 -11.25752,8.67896 -17.27734,12.50976 -0.15637,0.0782 -0.23427,0.1562 -0.39063,0.23438 -2.11085,1.40723 -4.3012,2.7354 -6.49023,4.06445 -8.91248,5.39439 -18.37192,10.08772 -28.37891,13.13672 -1.25087,0.31272 -2.42317,-0.001 -3.36133,-0.70508 l -6.01953,5.94141 c 1.25087,0.62543 2.03136,1.87776 1.875,3.51953 -10e-6,0.15636 -0.0762,0.23231 -0.0762,0.38867 0,0.0782 -0.0781,0.23628 -0.0781,0.31445 -1.32905,4.45624 -2.34505,8.98897 -3.2832,13.60156 -0.15636,0.70363 -0.23622,1.33154 -0.39258,2.03516 -0.85997,4.37806 -1.64209,8.83288 -2.3457,13.21094 0.23453,5.3944 0.39263,11.0234 0.31445,16.65234 v 0.39258 c -0.0782,7.66161 -0.78373,15.32114 -2.8164,22.51367 -2.26721,8.28704 -6.64376,15.63728 -10.55274,23.22071 -0.0782,0.15636 -0.15815,0.23426 -0.23633,0.39062 -1.25088,2.42357 -2.49924,4.92399 -3.59375,7.50391 -4.84714,11.33605 -7.42749,23.92328 -10.55468,35.88476 -0.23454,0.70362 -0.39046,1.48578 -0.625,2.26758 0,0.15636 -0.0801,0.23427 -0.0801,0.39063 -2.97082,11.10151 -6.09819,22.28173 -10.94532,32.75781 -1.40724,2.97082 -2.81531,5.86322 -4.3789,8.75586 -0.15636,0.23454 -0.23231,0.46858 -0.38867,0.70312 -0.62544,1.09451 -1.25152,2.26871 -1.87696,3.44141 -0.0782,0.15636 -0.15619,0.23426 -0.23437,0.39062 -3.51809,6.25438 -7.27098,12.43118 -10.78906,18.68555 -5.0035,8.8343 -8.99075,18.13635 -13.83789,27.04883 -0.0782,0.15636 -0.1562,0.23426 -0.23438,0.39062 -0.70362,1.32905 -1.48579,2.65728 -2.26758,3.98633 -5.0035,8.20887 -10.63256,16.0279 -16.57422,23.61133 -0.15635,0.15636 -0.23426,0.3124 -0.39062,0.46875 -0.7818,1.01634 -1.48578,1.95443 -2.26758,2.89258 -3.90898,4.92532 -7.97378,9.85009 -11.96094,14.77539 -0.0782,0.15637 -0.23432,0.23622 -0.3125,0.39258 -8.75612,10.71061 -17.35628,21.49761 -24.54883,33.30273 0,0.70362 -0.15602,1.33159 -0.46874,1.95703 -1.25087,2.42357 -2.65734,4.68971 -3.90821,7.11328 -0.0782,0.15636 0.62511,1.24989 0.46875,1.40625 L 429.86133,1024 H 1463.0215 L 661.85547,222.92969 c -0.93816,2.11087 -5.23681,1.40935 -7.34766,-0.23242 -1.71995,-1.32906 -3.12603,-3.05147 -4.45508,-4.84961 -0.62544,-0.31271 -1.25168,-0.62288 -1.64257,-0.85743 -2.89265,-1.40723 -6.09933,-1.48632 -9.30469,-1.48632 -0.7818,-0.0782 -1.40588,-0.23416 -1.95313,-0.54688 z m -206.12304,191.41992 0.11914,-0.11523 -0.23438,0.0781 z"
id="XMLID_64_" /></g><g
id="layer2"
style="display:inline;fill:#000000;fill-opacity:0.49803922"><path
id="XMLID_42_"
d="m 295.17362,965.05417 c 1.0692,3.47527 0.5346,7.21786 -1.3367,10.29214 l -25.7972,41.83679 c -2.5396,4.1436 -7.2178,6.8169 -12.297,6.8169 H 14.345318 C 3.1176178,1024 -3.6991822,1012.2376 2.3157178,1003.4158 L 157.76692,774.44928 c 0.9356,-1.33663 1.4704,-2.80694 1.8713,-4.27723 l 71.2428,-304.21933 c 0.8021,-3.60893 3.2081,-6.6832 6.6833,-8.55449 l 96.5054,-52.93096 c 3.4753,-1.8713 5.8812,-4.94557 6.6832,-8.68816 l 12.9654,-56.53988 c 2.6733,-11.76242 19.5151,-14.30205 26.1981,-4.00991 l 4.6783,7.48519 c 2.0049,3.20793 2.5396,7.21785 1.2031,10.82678 l -87.9511,254.22895 c -0.6683,2.00497 -0.9355,4.1436 -0.5346,6.28223 l 21.9209,121.63426 c 0.401,2.40595 0.1334,4.94556 -0.9357,7.21785 l -52.2625,117.357 c -1.203,2.80696 -1.4704,5.88123 -0.5347,8.68817 z M 1009.7413,1024 H 843.46322 c -4.8117,0 -9.2228,-2.4059 -11.8959,-6.1485 L 719.69042,860.52891 c -0.6683,-1.0693 -1.3366,-2.13861 -1.7375,-3.3416 l -55.4707,-162.00078 c -1.0692,-3.20793 -3.6088,-6.01489 -6.8169,-7.61886 l -135.8026,-68.56965 c -3.7426,-1.87127 -6.4159,-5.34655 -7.2179,-9.22281 l -20.0495,-99.44603 c -0.2674,-1.60396 -0.9357,-3.20793 -2.005,-4.67824 l -46.1141,-67.76766 c -2.5396,-3.74259 -2.9405,-8.28717 -1.0693,-12.2971 l 28.0694,-60.01513 c 2.1387,-4.54457 6.817,-7.61886 12.1634,-7.88619 l 52.129,-3.07427 c 3.0742,-0.1337 5.8812,-1.20296 8.1536,-3.07427 l 38.3615,-29.80707 c 7.2178,-5.61388 18.1784,-3.20794 22.0546,4.67824 l 132.1937,268.93201 c 0.5346,1.20297 0.9357,2.40595 1.2029,3.60894 l 16.3072,108.13418 c 0.4009,2.53963 1.4701,4.8119 3.2079,6.6832 l 263.31808,288.17958 c 7.7525,8.5545 1.203,22.0546 -10.8269,22.0546 z M 363.20852,182.58501 c 0,-30.60907 19.3812,-56.94088 47.1834,-69.23798 -2.005,-3.3416 -3.2079,-6.95052 -3.2079,-10.82678 0,-14.836705 17.109,-26.866465 38.0942,-26.866465 0.5346,0 0.9356,0 1.4704,0 8.688,-14.43572 25.2624,-24.19318 44.2426,-24.19318 1.3367,0 2.6733,0 4.01,0.1337 1.7377,0.13369 3.4753,-0.66833 4.4109,-2.00497 14.0347,-21.38624 49.5894,-36.62394 91.159,-36.62394 15.3712,0 29.9406,2.13863 42.906,5.74756 3.0744,-5.07924 9.8911,-8.5545 17.7773,-8.5545 8.9556,0 16.5744,4.54458 18.8466,10.82678 10.9606,-12.69809 29.5398,-20.98524 50.6587,-20.98524 33.6834,0 60.9508,21.25257 60.9508,47.45072 0,3.20793 -0.401,6.2822 -1.203,9.35647 -0.5346,2.13864 0.6683,4.27725 2.9407,5.07924 21.5199,7.88618 36.0893,22.85655 36.0893,39.965535 0,19.51495 -18.8466,36.22296 -45.4458,42.77249 -2.1387,0.53466 -3.4753,2.40595 -3.4753,4.41092 0,0.1337 0,0.26731 0,0.40098 0,15.10404 -14.9704,27.5348 -34.218,28.87144 0.1333,0.66833 0.1333,1.33663 0.1333,2.13862 0,29.00509 -55.2031,52.3963 -123.2382,52.3963 -14.7029,0 -28.7377,-1.06932 -41.7031,-3.07427 0,0.26733 0,0.40099 0,0.66832 0,12.02975 -15.5051,21.78723 -34.4854,21.78723 -1.0692,0 -2.0049,0 -2.9405,-0.13369 1.3367,2.9406 2.005,6.01487 2.005,9.22281 0,18.71296 -23.6586,33.81699 -52.9311,33.81699 -3.2079,0 -6.2821,-0.1337 -9.3563,-0.53466 -2.4061,-0.26731 -4.6783,1.20299 -5.2131,3.47529 -2.5396,9.35647 -10.693,16.17333 -20.4504,16.17333 -11.7625,0 -21.119,-10.0248 -21.119,-22.32189 0,-5.74755 2.005,-10.96045 5.3466,-14.83671 1.203,-1.33663 1.6039,-3.20793 0.8019,-4.81191 -1.8713,-3.47526 -2.6733,-7.08419 -2.6733,-10.96044 v 0 c 0,-2.13862 -1.7376,-3.87626 -3.8763,-4.4109 -36.2228,-8.01985 -63.4903,-38.22792 -63.4903,-74.3172 z m 306.8925,726.06294 c 0.5348,1.60398 0.6683,3.20796 0.6683,4.94558 l -7.7525,97.97577 c -0.5346,6.9505 -6.6832,12.4307 -14.1683,12.4307 h -250.219 c -5.3466,0 -10.2921,-3.0743 -12.6982,-7.4852 l -41.3021,-76.72312 c -0.2673,-0.401 -0.401,-0.80199 -0.5347,-1.20298 l -38.8962,-94.23313 c -1.4702,-3.3416 -1.203,-7.21785 0.4011,-10.42581 l 64.5596,-126.31249 c 1.604,-3.07427 1.8712,-6.6832 0.6683,-9.89114 l -31.5447,-87.41626 c -1.0693,-3.07428 -0.9356,-6.54955 0.4011,-9.49015 l 52.6636,-112.14412 c 5.3464,-11.22778 22.8565,-10.29212 26.5991,1.47031 l 16.4407,51.05965 50.124,134.19868 c 1.3367,3.7426 4.5446,6.6832 8.5545,8.01985 l 106.9312,36.49027 c 4.1435,1.47032 7.3516,4.54458 8.6881,8.42084 z"
style="fill:#000000;stroke-width:0.78179646;fill-opacity:0.49803922" /><g
aria-label="WULKANOWY"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:341.33334351px;line-height:1.25;font-family:Roboto;-inkscape-font-specification:'Roboto Light';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:0.49803922;stroke:none"
id="text4752" /></g></svg>

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,34 +1,30 @@
package io.github.wulkanowy package io.github.wulkanowy
import android.app.Application
import android.content.Context import android.content.Context
import android.util.Log.DEBUG import android.util.Log.DEBUG
import android.util.Log.INFO import android.util.Log.INFO
import android.util.Log.VERBOSE import android.util.Log.VERBOSE
import androidx.hilt.work.HiltWorkerFactory
import androidx.multidex.MultiDex import androidx.multidex.MultiDex
import androidx.work.Configuration import androidx.work.Configuration
import com.jakewharton.threetenabp.AndroidThreeTen
import com.yariksoffice.lingver.Lingver import com.yariksoffice.lingver.Lingver
import dagger.android.AndroidInjector import dagger.hilt.android.HiltAndroidApp
import dagger.android.support.DaggerApplication
import fr.bipi.tressence.file.FileLoggerTree import fr.bipi.tressence.file.FileLoggerTree
import io.github.wulkanowy.di.DaggerAppComponent
import io.github.wulkanowy.services.sync.SyncWorkerFactory
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 io.reactivex.exceptions.UndeliverableException
import io.reactivex.plugins.RxJavaPlugins
import timber.log.Timber import timber.log.Timber
import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
class WulkanowyApp : DaggerApplication(), Configuration.Provider { @HiltAndroidApp
class WulkanowyApp : Application(), Configuration.Provider {
@Inject @Inject
lateinit var workerFactory: SyncWorkerFactory lateinit var workerFactory: HiltWorkerFactory
@Inject @Inject
lateinit var themeManager: ThemeManager lateinit var themeManager: ThemeManager
@ -43,8 +39,6 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
AndroidThreeTen.init(this)
RxJavaPlugins.setErrorHandler(::onError)
Lingver.init(this) Lingver.init(this)
themeManager.applyDefaultTheme() themeManager.applyDefaultTheme()
@ -62,24 +56,12 @@ class WulkanowyApp : DaggerApplication(), Configuration.Provider {
.build() .build()
) )
} else { } else {
Timber.plant(CrashlyticsExceptionTree()) Timber.plant(CrashLogExceptionTree())
Timber.plant(CrashlyticsTree()) Timber.plant(CrashLogTree())
} }
registerActivityLifecycleCallbacks(ActivityLifecycleLogger()) registerActivityLifecycleCallbacks(ActivityLifecycleLogger())
} }
private fun onError(error: Throwable) {
//RxJava's too deep stack traces may cause SOE on older android devices
val cause = error.cause
if (error is UndeliverableException && cause is IOException || cause is InterruptedException || cause is StackOverflowError) {
Timber.e(cause, "An undeliverable error occurred")
} else throw error
}
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.factory().create(this)
}
override fun getWorkManagerConfiguration() = Configuration.Builder() override fun getWorkManagerConfiguration() = Configuration.Builder()
.setWorkerFactory(workerFactory) .setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO) .setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)

View File

@ -1,19 +1,18 @@
package io.github.wulkanowy.data package io.github.wulkanowy.data
import android.app.AlarmManager
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.AssetManager import android.content.res.AssetManager
import android.content.res.Resources import android.content.res.Resources
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.strategy.WalledGardenInternetObservingStrategy
import com.chuckerteam.chucker.api.ChuckerCollector import com.chuckerteam.chucker.api.ChuckerCollector
import com.chuckerteam.chucker.api.ChuckerInterceptor import com.chuckerteam.chucker.api.ChuckerInterceptor
import com.chuckerteam.chucker.api.RetentionManager 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.android.components.ApplicationComponent
import dagger.hilt.android.qualifiers.ApplicationContext
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
@ -22,32 +21,29 @@ import timber.log.Timber
import javax.inject.Singleton import javax.inject.Singleton
@Module @Module
@InstallIn(ApplicationComponent::class)
internal class RepositoryModule { internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideInternetObservingSettings(): InternetObservingSettings { fun provideSdk(chuckerCollector: ChuckerCollector, @ApplicationContext context: Context): Sdk {
return InternetObservingSettings.builder()
.strategy(WalledGardenInternetObservingStrategy())
.build()
}
@Singleton
@Provides
fun provideSdk(chuckerCollector: ChuckerCollector, context: Context): Sdk {
return Sdk().apply { return Sdk().apply {
androidVersion = android.os.Build.VERSION.RELEASE androidVersion = android.os.Build.VERSION.RELEASE
buildTag = android.os.Build.MODEL buildTag = android.os.Build.MODEL
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)
} }
} }
@Singleton @Singleton
@Provides @Provides
fun provideChuckerCollector(context: Context, prefRepository: PreferencesRepository): ChuckerCollector { fun provideChuckerCollector(@ApplicationContext context: Context, prefRepository: PreferencesRepository): ChuckerCollector {
return ChuckerCollector( return ChuckerCollector(
context = context, context = context,
showNotification = prefRepository.isDebugNotificationEnable, showNotification = prefRepository.isDebugNotificationEnable,
@ -57,19 +53,19 @@ internal class RepositoryModule {
@Singleton @Singleton
@Provides @Provides
fun provideDatabase(context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider) fun provideDatabase(@ApplicationContext context: Context, sharedPrefProvider: SharedPrefProvider) = AppDatabase.newInstance(context, sharedPrefProvider)
@Singleton @Singleton
@Provides @Provides
fun provideResources(context: Context): Resources = context.resources fun provideResources(@ApplicationContext context: Context): Resources = context.resources
@Singleton @Singleton
@Provides @Provides
fun provideAssets(context: Context): AssetManager = context.assets fun provideAssets(@ApplicationContext context: Context): AssetManager = context.assets
@Singleton @Singleton
@Provides @Provides
fun provideSharedPref(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) fun provideSharedPref(@ApplicationContext context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
@Singleton @Singleton
@Provides @Provides

View File

@ -0,0 +1,23 @@
package io.github.wulkanowy.data
data class Resource<T>(val status: Status, val data: T?, val error: Throwable?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(error: Throwable?, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, error)
}
fun <T> loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
LOADING,
SUCCESS,
ERROR
}

View File

@ -68,6 +68,8 @@ import io.github.wulkanowy.data.db.migrations.Migration22
import io.github.wulkanowy.data.db.migrations.Migration23 import io.github.wulkanowy.data.db.migrations.Migration23
import io.github.wulkanowy.data.db.migrations.Migration24 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.Migration27
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
@ -110,7 +112,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
companion object { companion object {
const val VERSION_SCHEMA = 25 const val VERSION_SCHEMA = 27
fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> { fun getMigrations(sharedPrefProvider: SharedPrefProvider): Array<Migration> {
return arrayOf( return arrayOf(
@ -137,7 +139,9 @@ abstract class AppDatabase : RoomDatabase() {
Migration22(), Migration22(),
Migration23(), Migration23(),
Migration24(), Migration24(),
Migration25() Migration25(),
Migration26(),
Migration27(),
) )
} }

View File

@ -1,21 +1,31 @@
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 org.threeten.bp.DateTimeUtils import io.github.wulkanowy.data.db.adapters.PairAdapterFactory
import org.threeten.bp.Instant import java.time.Instant
import org.threeten.bp.LocalDate import java.time.LocalDate
import org.threeten.bp.LocalDateTime import java.time.LocalDateTime
import org.threeten.bp.Month import java.time.Month
import org.threeten.bp.ZoneOffset import java.time.ZoneOffset
import java.util.Date 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 {
DateTimeUtils.toInstant(Date(value)).atZone(ZoneOffset.UTC).toLocalDate() Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
} }
@TypeConverter @TypeConverter
@ -40,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()
} }
} }

View File

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

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface AttendanceDao : BaseDao<Attendance> { interface AttendanceDao : BaseDao<Attendance> {
@Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Attendance WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Attendance>> fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Attendance>>
} }

View File

@ -3,11 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface AttendanceSummaryDao : BaseDao<AttendanceSummary> { interface AttendanceSummaryDao : BaseDao<AttendanceSummary> {
@Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId") @Query("SELECT * FROM AttendanceSummary WHERE diary_id = :diaryId AND student_id = :studentId AND subject_id = :subjectId")
fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Maybe<List<AttendanceSummary>> fun loadAll(diaryId: Int, studentId: Int, subjectId: Int): Flow<List<AttendanceSummary>>
} }

View File

@ -7,11 +7,11 @@ import androidx.room.Update
interface BaseDao<T> { interface BaseDao<T> {
@Insert @Insert
fun insertAll(items: List<T>): List<Long> suspend fun insertAll(items: List<T>): List<Long>
@Update @Update
fun updateAll(items: List<T>) suspend fun updateAll(items: List<T>)
@Delete @Delete
fun deleteAll(items: List<T>) suspend fun deleteAll(items: List<T>)
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface CompletedLessonsDao : BaseDao<CompletedLesson> { interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<CompletedLesson>> fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<CompletedLesson>>
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Exam import io.github.wulkanowy.data.db.entities.Exam
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface ExamDao : BaseDao<Exam> { interface ExamDao : BaseDao<Exam> {
@Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Exams WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Exam>> fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Exam>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Grade import io.github.wulkanowy.data.db.entities.Grade
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,6 +11,5 @@ import javax.inject.Singleton
interface GradeDao : BaseDao<Grade> { interface GradeDao : BaseDao<Grade> {
@Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId") @Query("SELECT * FROM Grades WHERE semester_id = :semesterId AND student_id = :studentId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<Grade>> fun loadAll(semesterId: Int, studentId: Int): Flow<List<Grade>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,8 +11,8 @@ import javax.inject.Singleton
interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> { interface GradePointsStatisticsDao : BaseDao<GradePointsStatistics> {
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName") @Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName")
fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Maybe<List<GradePointsStatistics>> fun loadSubject(semesterId: Int, studentId: Int, subjectName: String): Flow<List<GradePointsStatistics>>
@Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId") @Query("SELECT * FROM GradesPointsStatistics WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradePointsStatistics>> fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradePointsStatistics>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeStatistics import io.github.wulkanowy.data.db.entities.GradeStatistics
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,8 +11,8 @@ import javax.inject.Singleton
interface GradeStatisticsDao : BaseDao<GradeStatistics> { interface GradeStatisticsDao : BaseDao<GradeStatistics> {
@Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND subject = :subjectName AND is_semester = :isSemester") @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): Maybe<List<GradeStatistics>> 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") @Query("SELECT * FROM GradesStatistics WHERE student_id = :studentId AND semester_id = :semesterId AND is_semester = :isSemester")
fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Maybe<List<GradeStatistics>> fun loadAll(semesterId: Int, studentId: Int, isSemester: Boolean): Flow<List<GradeStatistics>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.GradeSummary import io.github.wulkanowy.data.db.entities.GradeSummary
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,5 +11,5 @@ import javax.inject.Singleton
interface GradeSummaryDao : BaseDao<GradeSummary> { interface GradeSummaryDao : BaseDao<GradeSummary> {
@Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId") @Query("SELECT * FROM GradesSummary WHERE student_id = :studentId AND semester_id = :semesterId")
fun loadAll(semesterId: Int, studentId: Int): Maybe<List<GradeSummary>> fun loadAll(semesterId: Int, studentId: Int): Flow<List<GradeSummary>>
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Homework import io.github.wulkanowy.data.db.entities.Homework
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface HomeworkDao : BaseDao<Homework> { interface HomeworkDao : BaseDao<Homework> {
@Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Homework WHERE semester_id = :semesterId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Homework>> fun loadAll(semesterId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Homework>>
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.LuckyNumber import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -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): Maybe<LuckyNumber> fun load(studentId: Int, date: LocalDate): Flow<LuckyNumber?>
} }

View File

@ -9,5 +9,5 @@ import io.github.wulkanowy.data.db.entities.MessageAttachment
interface MessageAttachmentDao : BaseDao<MessageAttachment> { interface MessageAttachmentDao : BaseDao<MessageAttachment> {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAttachments(items: List<MessageAttachment>): List<Long> suspend fun insertAttachments(items: List<MessageAttachment>): List<Long>
} }

View File

@ -5,19 +5,15 @@ import androidx.room.Query
import androidx.room.Transaction import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Message import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import io.reactivex.Single
@Dao @Dao
interface MessagesDao : BaseDao<Message> { 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): Single<MessageWithAttachment> fun loadMessageWithAttachment(studentId: Int, messageId: Int): Flow<MessageWithAttachment?>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND folder_id = :folder AND removed = 0 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): Maybe<List<Message>> fun loadAll(studentId: Int, folder: Int): Flow<List<Message>>
@Query("SELECT * FROM Messages WHERE student_id = :studentId AND removed = 1 ORDER BY date DESC")
fun loadDeleted(studentId: Int): Maybe<List<Message>>
} }

View File

@ -3,11 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.MobileDevice import io.github.wulkanowy.data.db.entities.MobileDevice
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface MobileDeviceDao : BaseDao<MobileDevice> { interface MobileDeviceDao : BaseDao<MobileDevice> {
@Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC") @Query("SELECT * FROM MobileDevices WHERE student_id = :studentId ORDER BY date DESC")
fun loadAll(studentId: Int): Maybe<List<MobileDevice>> fun loadAll(studentId: Int): Flow<List<MobileDevice>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Note import io.github.wulkanowy.data.db.entities.Note
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,5 +11,5 @@ import javax.inject.Singleton
interface NoteDao : BaseDao<Note> { interface NoteDao : BaseDao<Note> {
@Query("SELECT * FROM Notes WHERE student_id = :studentId") @Query("SELECT * FROM Notes WHERE student_id = :studentId")
fun loadAll(studentId: Int): Maybe<List<Note>> fun loadAll(studentId: Int): Flow<List<Note>>
} }

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Recipient import io.github.wulkanowy.data.db.entities.Recipient
import io.reactivex.Maybe
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,5 +10,5 @@ import javax.inject.Singleton
interface RecipientDao : BaseDao<Recipient> { interface RecipientDao : BaseDao<Recipient> {
@Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId") @Query("SELECT * FROM Recipients WHERE student_id = :studentId AND role = :role AND unit_id = :unitId")
fun load(studentId: Int, role: Int, unitId: Int): Maybe<List<Recipient>> suspend fun load(studentId: Int, role: Int, unitId: Int): List<Recipient>
} }

View File

@ -3,7 +3,6 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.ReportingUnit import io.github.wulkanowy.data.db.entities.ReportingUnit
import io.reactivex.Maybe
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,8 +10,8 @@ import javax.inject.Singleton
interface ReportingUnitDao : BaseDao<ReportingUnit> { interface ReportingUnitDao : BaseDao<ReportingUnit> {
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId") @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId")
fun load(studentId: Int): Maybe<List<ReportingUnit>> suspend fun load(studentId: Int): List<ReportingUnit>
@Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId") @Query("SELECT * FROM ReportingUnits WHERE student_id = :studentId AND real_id = :unitId")
fun loadOne(studentId: Int, unitId: Int): Maybe<ReportingUnit> suspend fun loadOne(studentId: Int, unitId: Int): ReportingUnit?
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.School import io.github.wulkanowy.data.db.entities.School
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,5 +11,5 @@ import javax.inject.Singleton
interface SchoolDao : BaseDao<School> { interface SchoolDao : BaseDao<School> {
@Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM School WHERE student_id = :studentId AND class_id = :classId")
fun load(studentId: Int, classId: Int): Maybe<School> fun load(studentId: Int, classId: Int): Flow<School?>
} }

View File

@ -1,15 +1,19 @@
package io.github.wulkanowy.data.db.dao package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@Dao @Dao
interface SemesterDao : BaseDao<Semester> { interface SemesterDao : BaseDao<Semester> {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertSemesters(items: List<Semester>): List<Long>
@Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM Semesters WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Semester>> suspend fun loadAll(studentId: Int, classId: Int): List<Semester>
} }

View File

@ -5,8 +5,9 @@ import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy.ABORT import androidx.room.OnConflictStrategy.ABORT
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.reactivex.Maybe import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -14,23 +15,27 @@ import javax.inject.Singleton
interface StudentDao { interface StudentDao {
@Insert(onConflict = ABORT) @Insert(onConflict = ABORT)
fun insertAll(student: List<Student>): List<Long> suspend fun insertAll(student: List<Student>): List<Long>
@Delete @Delete
fun delete(student: Student) suspend fun delete(student: Student)
@Query("SELECT * FROM Students WHERE is_current = 1") @Query("SELECT * FROM Students WHERE is_current = 1")
fun loadCurrent(): Maybe<Student> suspend fun loadCurrent(): Student?
@Query("SELECT * FROM Students WHERE id = :id") @Query("SELECT * FROM Students WHERE id = :id")
fun loadById(id: Int): Maybe<Student> suspend fun loadById(id: Int): Student?
@Query("SELECT * FROM Students") @Query("SELECT * FROM Students")
fun loadAll(): Maybe<List<Student>> suspend fun loadAll(): List<Student>
@Transaction
@Query("SELECT * FROM Students")
suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id") @Query("UPDATE Students SET is_current = 1 WHERE id = :id")
fun updateCurrent(id: Long) suspend fun updateCurrent(id: Long)
@Query("UPDATE Students SET is_current = 0") @Query("UPDATE Students SET is_current = 0")
fun resetCurrent() suspend fun resetCurrent()
} }

View File

@ -3,11 +3,11 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Subject import io.github.wulkanowy.data.db.entities.Subject
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface SubjectDao : BaseDao<Subject> { interface SubjectDao : BaseDao<Subject> {
@Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId") @Query("SELECT * FROM Subjects WHERE diary_id = :diaryId AND student_id = :studentId")
fun loadAll(diaryId: Int, studentId: Int): Maybe<List<Subject>> fun loadAll(diaryId: Int, studentId: Int): Flow<List<Subject>>
} }

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Teacher import io.github.wulkanowy.data.db.entities.Teacher
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -11,5 +11,5 @@ import javax.inject.Singleton
interface TeacherDao : BaseDao<Teacher> { interface TeacherDao : BaseDao<Teacher> {
@Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId") @Query("SELECT * FROM Teachers WHERE student_id = :studentId AND class_id = :classId")
fun loadAll(studentId: Int, classId: Int): Maybe<List<Teacher>> fun loadAll(studentId: Int, classId: Int): Flow<List<Teacher>>
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.dao
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Timetable import io.github.wulkanowy.data.db.entities.Timetable
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface TimetableDao : BaseDao<Timetable> { interface TimetableDao : BaseDao<Timetable> {
@Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end") @Query("SELECT * FROM Timetable WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Maybe<List<Timetable>> fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<Timetable>>
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Attendance") @Entity(tableName = "Attendance")
data class Attendance( data class Attendance(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.Month
import java.io.Serializable import java.io.Serializable
import java.time.Month
@Entity(tableName = "AttendanceSummary") @Entity(tableName = "AttendanceSummary")
data class AttendanceSummary( data class AttendanceSummary(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "CompletedLesson") @Entity(tableName = "CompletedLesson")
data class CompletedLesson( data class CompletedLesson(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Exams") @Entity(tableName = "Exams")
data class Exam( data class Exam(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Grades") @Entity(tableName = "Grades")
data class Grade( data class Grade(

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import java.time.LocalDateTime
@Entity(tableName = "GradesSummary") @Entity(tableName = "GradesSummary")
data class GradeSummary( data class GradeSummary(
@ -36,4 +37,16 @@ data class GradeSummary(
) { ) {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0
@ColumnInfo(name = "is_predicted_grade_notified")
var isPredictedGradeNotified: Boolean = true
@ColumnInfo(name = "is_final_grade_notified")
var isFinalGradeNotified: Boolean = true
@ColumnInfo(name = "predicted_grade_last_change")
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
@ColumnInfo(name = "final_grade_last_change")
var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Homework") @Entity(tableName = "Homework")
data class Homework( data class Homework(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "LuckyNumbers") @Entity(tableName = "LuckyNumbers")
data class LuckyNumber ( data class LuckyNumber (

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDateTime
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime
@Entity(tableName = "Messages") @Entity(tableName = "Messages")
data class Message( data class Message(
@ -29,8 +29,6 @@ data class Message(
val subject: String, val subject: String,
var content: String,
val date: LocalDateTime, val date: LocalDateTime,
@ColumnInfo(name = "folder_id") @ColumnInfo(name = "folder_id")
@ -38,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")
@ -55,4 +47,12 @@ 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 = ""
} }

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDateTime
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime
@Entity(tableName = "MobileDevices") @Entity(tableName = "MobileDevices")
data class MobileDevice( data class MobileDevice(

View File

@ -3,8 +3,8 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Notes") @Entity(tableName = "Notes")
data class Note( data class Note(

View File

@ -4,7 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate import java.io.Serializable
import java.time.LocalDate
@Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)]) @Entity(tableName = "Semesters", indices = [Index(value = ["student_id", "diary_id", "semester_id"], unique = true)])
data class Semester( data class Semester(
@ -36,7 +37,7 @@ data class Semester(
@ColumnInfo(name = "unit_id") @ColumnInfo(name = "unit_id")
val unitId: Int val unitId: Int
) { ): Serializable {
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
var id: Long = 0 var id: Long = 0

View File

@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.Index import androidx.room.Index
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDateTime
import java.io.Serializable import java.io.Serializable
import java.time.LocalDateTime
@Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)]) @Entity(tableName = "Students", indices = [Index(value = ["email", "symbol", "student_id", "school_id", "class_id"], unique = true)])
data class Student( data class Student(
@ -43,6 +43,9 @@ data class Student(
@ColumnInfo(name = "user_login_id") @ColumnInfo(name = "user_login_id")
val userLoginId: Int, val userLoginId: Int,
@ColumnInfo(name = "user_name")
val userName: String,
@ColumnInfo(name = "student_name") @ColumnInfo(name = "student_name")
val studentName: String, val studentName: String,

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.data.db.entities
import androidx.room.Embedded
import androidx.room.Relation
import java.io.Serializable
data class StudentWithSemesters(
@Embedded
val student: Student,
@Relation(parentColumn = "student_id", entityColumn = "student_id")
val semesters: List<Semester>
) : Serializable

View File

@ -3,9 +3,9 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalDateTime
import java.io.Serializable import java.io.Serializable
import java.time.LocalDate
import java.time.LocalDateTime
@Entity(tableName = "Timetable") @Entity(tableName = "Timetable")
data class Timetable( data class Timetable(

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration26 : Migration(25, 26) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0")
}
}

View File

@ -0,0 +1,45 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
class Migration27 : Migration(26, 27) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN user_name TEXT NOT NULL DEFAULT \"\"")
val students = getStudentsIdsAndNames(database)
val units = getReportingUnits(database)
students.forEach { (id, userLoginId, studentName) ->
val userNameFromUnits = units.singleOrNull { (senderId, _) -> senderId == userLoginId }?.second
val normalizedStudentName = studentName.split(" ").asReversed().joinToString(" ")
val userName = userNameFromUnits ?: normalizedStudentName
database.execSQL("UPDATE Students SET user_name = '$userName' WHERE id = '$id'")
}
}
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
val students = mutableListOf<Triple<Long, Int, String>>()
val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students")
if (studentsCursor.moveToFirst()) {
do {
students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2)))
} while (studentsCursor.moveToNext())
}
return students
}
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val units = mutableListOf<Pair<Int, String>>()
val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits")
if (unitsCursor.moveToFirst()) {
do {
units.add(unitsCursor.getInt(0) to unitsCursor.getString(1))
} while (unitsCursor.moveToNext())
}
return units
}
}

View File

@ -2,8 +2,8 @@ package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import org.threeten.bp.LocalDateTime.now import java.time.LocalDateTime.now
import org.threeten.bp.ZoneOffset import java.time.ZoneOffset
class Migration5 : Migration(4, 5) { class Migration5 : Migration(4, 5) {

View File

@ -0,0 +1,19 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
fun List<SdkSemester>.mapToEntities(studentId: Int) = map {
Semester(
studentId = studentId,
diaryId = it.diaryId,
diaryName = it.diaryName,
schoolYear = it.schoolYear,
semesterId = it.semesterId,
semesterName = it.semesterNumber,
start = it.start,
end = it.end,
classId = it.classId,
unitId = it.unitId
)
}

View File

@ -0,0 +1,35 @@
package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import java.time.LocalDateTime
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
fun List<SdkStudent>.mapToEntities(password: String = "") = map {
StudentWithSemesters(
student = Student(
email = it.email,
password = password,
isParent = it.isParent,
symbol = it.symbol,
studentId = it.studentId,
userLoginId = it.userLoginId,
userName = it.userName,
studentName = it.studentName + " " + it.studentSurname,
schoolSymbol = it.schoolSymbol,
schoolShortName = it.schoolShortName,
schoolName = it.schoolName,
className = it.className,
classId = it.classId,
scrapperBaseUrl = it.scrapperBaseUrl,
loginType = it.loginType.name,
isCurrent = false,
registrationDate = LocalDateTime.now(),
mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey,
certificateKey = it.certificateKey,
loginMode = it.loginMode.name
),
semesters = it.semesters.mapToEntities(it.studentId)
)
}

View File

@ -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
)

View File

@ -1,20 +1,24 @@
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.reactivex.Single import io.github.wulkanowy.utils.DispatchersProvider
import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AppCreatorRepository @Inject constructor(private val assets: AssetManager) { class AppCreatorRepository @Inject constructor(
fun getAppCreators(): Single<List<Contributor>> { private val assets: AssetManager,
return Single.fromCallable<List<Contributor>> { private val dispatchers: DispatchersProvider
Gson().fromJson( ) {
assets.open("contributors.json").bufferedReader().use { it.readText() },
Array<Contributor>::class.java suspend fun getAppCreators() = withContext(dispatchers.backgroundThread) {
).toList() val moshi = Moshi.Builder().build()
} val type = Types.newParameterizedType(List::class.java, Contributor::class.java)
val adapter = moshi.adapter<List<Contributor>>(type)
adapter.fromJson(assets.open("contributors.json").bufferedReader().use { it.readText() })
} }
} }

View File

@ -3,23 +3,23 @@ package io.github.wulkanowy.data.repositories.attendance
import io.github.wulkanowy.data.db.dao.AttendanceDao import io.github.wulkanowy.data.db.dao.AttendanceDao
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) { class AttendanceLocal @Inject constructor(private val attendanceDb: AttendanceDao) {
fun saveAttendance(attendance: List<Attendance>) { suspend fun saveAttendance(attendance: List<Attendance>) {
attendanceDb.insertAll(attendance) attendanceDb.insertAll(attendance)
} }
fun deleteAttendance(attendance: List<Attendance>) { suspend fun deleteAttendance(attendance: List<Attendance>) {
attendanceDb.deleteAll(attendance) attendanceDb.deleteAll(attendance)
} }
fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Maybe<List<Attendance>> { fun getAttendance(semester: Semester, startDate: LocalDate, endDate: LocalDate): Flow<List<Attendance>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate).filter { it.isNotEmpty() } return attendanceDb.loadAll(semester.diaryId, semester.studentId, startDate, endDate)
} }
} }

View File

@ -6,43 +6,40 @@ import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Absent import io.github.wulkanowy.sdk.pojo.Absent
import io.github.wulkanowy.utils.init import io.github.wulkanowy.utils.init
import io.reactivex.Single import java.time.LocalDate
import org.threeten.bp.LocalDate import java.time.LocalDateTime
import org.threeten.bp.LocalDateTime import java.time.LocalTime
import org.threeten.bp.LocalTime
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceRemote @Inject constructor(private val sdk: Sdk) { class AttendanceRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendance(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<Attendance>> { suspend fun getAttendance(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List<Attendance> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getAttendance(startDate, endDate, semester.semesterId) .getAttendance(startDate, endDate, semester.semesterId)
.map { attendance -> .map {
attendance.map { Attendance(
Attendance( studentId = semester.studentId,
studentId = semester.studentId, diaryId = semester.diaryId,
diaryId = semester.diaryId, date = it.date,
date = it.date, timeId = it.timeId,
timeId = it.timeId, number = it.number,
number = it.number, subject = it.subject,
subject = it.subject, name = it.name,
name = it.name, presence = it.presence,
presence = it.presence, absence = it.absence,
absence = it.absence, exemption = it.exemption,
exemption = it.exemption, lateness = it.lateness,
lateness = it.lateness, excused = it.excused,
excused = it.excused, deleted = it.deleted,
deleted = it.deleted, excusable = it.excusable,
excusable = it.excusable, excuseStatus = it.excuseStatus?.name
excuseStatus = it.excuseStatus?.name )
)
}
} }
} }
fun excuseAbsence(student: Student, semester: Semester, absenceList: List<Attendance>, reason: String?): Single<Boolean> { suspend fun excuseAbsence(student: Student, semester: Semester, absenceList: List<Attendance>, reason: String?): Boolean {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance -> return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear).excuseForAbsence(absenceList.map { attendance ->
Absent( Absent(
date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)), date = LocalDateTime.of(attendance.date, LocalTime.of(0, 0)),

View File

@ -1,45 +1,34 @@
package io.github.wulkanowy.data.repositories.attendance package io.github.wulkanowy.data.repositories.attendance
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.Attendance import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import java.time.LocalDate
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceRepository @Inject constructor( class AttendanceRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: AttendanceLocal, private val local: AttendanceLocal,
private val remote: AttendanceRemote private val remote: AttendanceRemote
) { ) {
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean): Single<List<Attendance>> { fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getAttendance(semester, start.monday, end.sunday).filter { !forceRefresh } shouldFetch = { it.isEmpty() || forceRefresh },
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings).flatMap { query = { local.getAttendance(semester, start.monday, end.sunday) },
if (it) remote.getAttendance(student, semester, start.monday, end.sunday) fetch = { remote.getAttendance(student, semester, start.monday, end.sunday) },
else Single.error(UnknownHostException()) saveFetchResult = { old, new ->
}.flatMap { newAttendance -> local.deleteAttendance(old uniqueSubtract new)
local.getAttendance(semester, start.monday, end.sunday) local.saveAttendance(new uniqueSubtract old)
.toSingle(emptyList()) },
.doOnSuccess { oldAttendance -> filterResult = { it.filter { item -> item.date in start..end } }
local.deleteAttendance(oldAttendance.uniqueSubtract(newAttendance)) )
local.saveAttendance(newAttendance.uniqueSubtract(oldAttendance))
}
}.flatMap {
local.getAttendance(semester, start.monday, end.sunday)
.toSingle(emptyList())
}).map { list -> list.filter { it.date in start..end } }
}
fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null): Single<Boolean> { suspend fun excuseForAbsence(student: Student, semester: Semester, attendanceList: List<Attendance>, reason: String? = null) {
return remote.excuseAbsence(student, semester, attendanceList, reason) remote.excuseAbsence(student, semester, attendanceList, reason)
} }
} }

View File

@ -3,22 +3,22 @@ package io.github.wulkanowy.data.repositories.attendancesummary
import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao import io.github.wulkanowy.data.db.dao.AttendanceSummaryDao
import io.github.wulkanowy.data.db.entities.AttendanceSummary import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) { class AttendanceSummaryLocal @Inject constructor(private val attendanceDb: AttendanceSummaryDao) {
fun saveAttendanceSummary(attendance: List<AttendanceSummary>) { suspend fun saveAttendanceSummary(attendance: List<AttendanceSummary>) {
attendanceDb.insertAll(attendance) attendanceDb.insertAll(attendance)
} }
fun deleteAttendanceSummary(attendance: List<AttendanceSummary>) { suspend fun deleteAttendanceSummary(attendance: List<AttendanceSummary>) {
attendanceDb.deleteAll(attendance) attendanceDb.deleteAll(attendance)
} }
fun getAttendanceSummary(semester: Semester, subjectId: Int): Maybe<List<AttendanceSummary>> { fun getAttendanceSummary(semester: Semester, subjectId: Int): Flow<List<AttendanceSummary>> {
return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId).filter { it.isNotEmpty() } return attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId)
} }
} }

View File

@ -5,32 +5,29 @@ 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.init import io.github.wulkanowy.utils.init
import io.reactivex.Single
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) { class AttendanceSummaryRemote @Inject constructor(private val sdk: Sdk) {
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int): Single<List<AttendanceSummary>> { suspend fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int): List<AttendanceSummary> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getAttendanceSummary(subjectId) .getAttendanceSummary(subjectId)
.map { attendance -> .map {
attendance.map { AttendanceSummary(
AttendanceSummary( studentId = semester.studentId,
studentId = semester.studentId, diaryId = semester.diaryId,
diaryId = semester.diaryId, subjectId = subjectId,
subjectId = subjectId, month = it.month,
month = it.month, presence = it.presence,
presence = it.presence, absence = it.absence,
absence = it.absence, absenceExcused = it.absenceExcused,
absenceExcused = it.absenceExcused, absenceForSchoolReasons = it.absenceForSchoolReasons,
absenceForSchoolReasons = it.absenceForSchoolReasons, lateness = it.lateness,
lateness = it.lateness, latenessExcused = it.latenessExcused,
latenessExcused = it.latenessExcused, exemption = it.exemption
exemption = it.exemption )
)
}
} }
} }
} }

View File

@ -1,35 +1,25 @@
package io.github.wulkanowy.data.repositories.attendancesummary package io.github.wulkanowy.data.repositories.attendancesummary
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class AttendanceSummaryRepository @Inject constructor( class AttendanceSummaryRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: AttendanceSummaryLocal, private val local: AttendanceSummaryLocal,
private val remote: AttendanceSummaryRemote private val remote: AttendanceSummaryRemote
) { ) {
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean = false): Single<List<AttendanceSummary>> { fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
return local.getAttendanceSummary(semester, subjectId).filter { !forceRefresh } shouldFetch = { it.isEmpty() || forceRefresh },
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) query = { local.getAttendanceSummary(semester, subjectId) },
.flatMap { fetch = { remote.getAttendanceSummary(student, semester, subjectId) },
if (it) remote.getAttendanceSummary(student, semester, subjectId) saveFetchResult = { old, new ->
else Single.error(UnknownHostException()) local.deleteAttendanceSummary(old uniqueSubtract new)
}.flatMap { new -> local.saveAttendanceSummary(new uniqueSubtract old)
local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) }
.doOnSuccess { old -> )
local.deleteAttendanceSummary(old.uniqueSubtract(new))
local.saveAttendanceSummary(new.uniqueSubtract(old))
}
}.flatMap { local.getAttendanceSummary(semester, subjectId).toSingle(emptyList()) })
}
} }

View File

@ -3,23 +3,23 @@ package io.github.wulkanowy.data.repositories.completedlessons
import io.github.wulkanowy.data.db.dao.CompletedLessonsDao import io.github.wulkanowy.data.db.dao.CompletedLessonsDao
import io.github.wulkanowy.data.db.entities.CompletedLesson import io.github.wulkanowy.data.db.entities.CompletedLesson
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.reactivex.Maybe import kotlinx.coroutines.flow.Flow
import org.threeten.bp.LocalDate import java.time.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) { class CompletedLessonsLocal @Inject constructor(private val completedLessonsDb: CompletedLessonsDao) {
fun saveCompletedLessons(completedLessons: List<CompletedLesson>) { suspend fun saveCompletedLessons(completedLessons: List<CompletedLesson>) {
completedLessonsDb.insertAll(completedLessons) completedLessonsDb.insertAll(completedLessons)
} }
fun deleteCompleteLessons(completedLessons: List<CompletedLesson>) { suspend fun deleteCompleteLessons(completedLessons: List<CompletedLesson>) {
completedLessonsDb.deleteAll(completedLessons) completedLessonsDb.deleteAll(completedLessons)
} }
fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Maybe<List<CompletedLesson>> { fun getCompletedLessons(semester: Semester, start: LocalDate, end: LocalDate): Flow<List<CompletedLesson>> {
return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end).filter { it.isNotEmpty() } return completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end)
} }
} }

View File

@ -5,34 +5,31 @@ 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.init import io.github.wulkanowy.utils.init
import io.reactivex.Single import java.time.LocalDate
import org.threeten.bp.LocalDate
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) { class CompletedLessonsRemote @Inject constructor(private val sdk: Sdk) {
fun getCompletedLessons(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): Single<List<CompletedLesson>> { suspend fun getCompletedLessons(student: Student, semester: Semester, startDate: LocalDate, endDate: LocalDate): List<CompletedLesson> {
return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear) return sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getCompletedLessons(startDate, endDate) .getCompletedLessons(startDate, endDate)
.map { lessons -> .map {
lessons.map { it.absence
it.absence CompletedLesson(
CompletedLesson( studentId = semester.studentId,
studentId = semester.studentId, diaryId = semester.diaryId,
diaryId = semester.diaryId, date = it.date,
date = it.date, number = it.number,
number = it.number, subject = it.subject,
subject = it.subject, topic = it.topic,
topic = it.topic, teacher = it.teacher,
teacher = it.teacher, teacherSymbol = it.teacherSymbol,
teacherSymbol = it.teacherSymbol, substitution = it.substitution,
substitution = it.substitution, absence = it.absence,
absence = it.absence, resources = it.resources
resources = it.resources )
)
}
} }
} }
} }

View File

@ -1,42 +1,29 @@
package io.github.wulkanowy.data.repositories.completedlessons package io.github.wulkanowy.data.repositories.completedlessons
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.github.pwittchen.reactivenetwork.library.rx2.internet.observing.InternetObservingSettings
import io.github.wulkanowy.data.db.entities.CompletedLesson
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.sunday
import io.github.wulkanowy.utils.monday import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.sunday
import io.github.wulkanowy.utils.uniqueSubtract import io.github.wulkanowy.utils.uniqueSubtract
import io.reactivex.Single import java.time.LocalDate
import org.threeten.bp.LocalDate
import java.net.UnknownHostException
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class CompletedLessonsRepository @Inject constructor( class CompletedLessonsRepository @Inject constructor(
private val settings: InternetObservingSettings,
private val local: CompletedLessonsLocal, private val local: CompletedLessonsLocal,
private val remote: CompletedLessonsRemote private val remote: CompletedLessonsRemote
) { ) {
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean = false): Single<List<CompletedLesson>> { fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
return local.getCompletedLessons(semester, start.monday, end.sunday).filter { !forceRefresh } shouldFetch = { it.isEmpty() || forceRefresh },
.switchIfEmpty(ReactiveNetwork.checkInternetConnectivity(settings) query = { local.getCompletedLessons(semester, start.monday, end.sunday) },
.flatMap { fetch = { remote.getCompletedLessons(student, semester, start.monday, end.sunday) },
if (it) remote.getCompletedLessons(student, semester, start.monday, end.sunday) saveFetchResult = { old, new ->
else Single.error(UnknownHostException()) local.deleteCompleteLessons(old uniqueSubtract new)
}.flatMap { new -> local.saveCompletedLessons(new uniqueSubtract old)
local.getCompletedLessons(semester, start.monday, end.sunday) },
.toSingle(emptyList()) filterResult = { it.filter { item -> item.date in start..end } }
.doOnSuccess { old -> )
local.deleteCompleteLessons(old.uniqueSubtract(new))
local.saveCompletedLessons(new.uniqueSubtract(old))
}
}.flatMap {
local.getCompletedLessons(semester, start.monday, end.sunday)
.toSingle(emptyList())
}).map { list -> list.filter { it.date in start..end } }
}
} }

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