Compare commits

...

140 Commits
2.0.8 ... 2.2.7

Author SHA1 Message Date
a2a7d2ebb2 Merge branch 'release/2.2.7' 2023-12-27 22:21:13 +01:00
a5bc45c5da Version 2.2.7 2023-12-27 22:21:07 +01:00
5646befbd7 Revert "Bump com.google.android.material:material from 1.10.0 to 1.11.0 (#2368)" (#2376)
This reverts commit 7f4539fd27.
2023-12-27 21:52:04 +01:00
75f496b5d2 Bump kotlin_version from 1.9.21 to 1.9.22 (#2371) 2023-12-27 16:11:58 +00:00
23d989d22a Bump hilt_version from 2.49 to 2.50 (#2372) 2023-12-24 07:01:40 +00:00
9e013f7cd9 Bump ru.cian:huawei-publish-gradle-plugin from 1.4.0 to 1.4.2 (#2370) 2023-12-24 07:00:42 +00:00
c63a7c03f1 Bump com.huawei.agconnect:agcp from 1.9.1.301 to 1.9.1.302 (#2373) 2023-12-24 06:54:28 +00:00
5ceee84f0e Bump com.huawei.agconnect:agconnect-crash from 1.9.1.301 to 1.9.1.302 (#2374) 2023-12-24 06:54:09 +00:00
7f4539fd27 Bump com.google.android.material:material from 1.10.0 to 1.11.0 (#2368) 2023-12-22 13:48:13 +00:00
71ebf1260b Bump com.android.tools.build:gradle from 8.1.4 to 8.2.0 (#2361) 2023-12-15 19:44:32 +00:00
784ee58384 Bump work_manager from 2.8.1 to 2.9.0 (#2363) 2023-12-15 16:21:08 +00:00
eceef3f582 Bump com.google.firebase:firebase-bom from 32.6.0 to 32.7.0 (#2366) 2023-12-15 15:52:54 +00:00
0d950fbd86 Bump com.google.android.gms:play-services-ads from 22.5.0 to 22.6.0 (#2367) 2023-12-15 15:52:35 +00:00
003d63b516 Bump room from 2.6.0 to 2.6.1 (#2360) 2023-12-07 22:44:50 +00:00
b4c0440a8e Bump hilt_version from 2.48.1 to 2.49 (#2362) 2023-12-07 22:44:37 +00:00
137c305295 Bump org.jetbrains.kotlinx:kotlinx-serialization-json (#2365) 2023-12-07 22:44:23 +00:00
2c40c221c3 Bump kotlin_version from 1.9.10 to 1.9.21 (#2357) 2023-11-28 22:32:32 +00:00
e82ac78d4a Bump androidx.fragment:fragment-ktx from 1.6.1 to 1.6.2 (#2348) 2023-11-28 22:32:18 +00:00
59d46ce956 Bump com.google.android.gms:play-services-ads from 22.4.0 to 22.5.0 (#2346) 2023-11-28 22:21:19 +00:00
17caa8ecbd Bump com.android.tools:desugar_jdk_libs from 2.0.3 to 2.0.4 (#2345) 2023-11-28 22:21:00 +00:00
e9540b4012 Bump androidx.activity:activity-ktx from 1.8.0 to 1.8.1 (#2354) 2023-11-28 22:20:41 +00:00
643ad60455 Bump com.google.firebase:firebase-bom from 32.5.0 to 32.6.0 (#2355) 2023-11-28 22:20:18 +00:00
01f892ce5c Bump com.android.tools.build:gradle from 8.1.2 to 8.1.4 (#2356) 2023-11-28 22:19:53 +00:00
f61b6a5e78 Bump com.google.android.play:integrity from 1.2.0 to 1.3.0 (#2351) 2023-11-28 22:10:32 +00:00
650cf7484e Bump android_hilt from 1.0.0 to 1.1.0 (#2343) 2023-11-28 22:10:11 +00:00
037cbb0b19 Bump about_libraries from 10.9.1 to 10.9.2 (#2344)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
2023-11-28 23:08:13 +01:00
e49835e89e Merge branch 'release/2.2.6' into develop 2023-11-06 11:22:25 +01:00
c64be2fab0 Merge branch 'release/2.2.6' 2023-11-06 11:22:19 +01:00
ce9cb35172 Version 2.2.6 2023-11-06 11:22:15 +01:00
7fa9219c7b Bump sdk version 2023-11-06 09:40:47 +01:00
1fe1618220 Merge branch 'release/2.2.5' into develop 2023-11-03 23:05:48 +01:00
d9bab2af78 Merge branch 'release/2.2.5' 2023-11-03 23:05:42 +01:00
06b6d88dd1 Version 2.2.5 2023-11-03 23:05:35 +01:00
3bf27baed5 Bump org.robolectric:robolectric from 4.11 to 4.11.1 (#2342) 2023-11-01 17:31:24 +00:00
6802d74002 Bump com.google.firebase:firebase-bom from 32.4.1 to 32.5.0 (#2341) 2023-11-01 17:31:05 +00:00
3fd2683df7 Update dependencies and remove deprecations (#2340) 2023-11-01 16:47:39 +01:00
b708c70ea2 Merge branch 'release/2.2.4' into develop 2023-10-27 14:49:54 +02:00
aba08e6aa9 Merge branch 'release/2.2.4' 2023-10-27 14:49:46 +02:00
124b6dfd79 Version 2.2.4 2023-10-27 14:49:41 +02:00
1dbaa8bfdc Migrate to separate app-update and review artifacts from play:core (#2336) 2023-10-27 14:09:42 +02:00
25ac171298 Merge branch 'release/2.2.3' into develop 2023-10-26 18:32:01 +02:00
387ff1cba7 Merge branch 'release/2.2.3' 2023-10-26 18:31:56 +02:00
eef3464d0b Version 2.2.3 2023-10-26 18:31:51 +02:00
61297a01c7 New Crowdin updates (#2334) 2023-10-26 14:01:45 +02:00
762d4b1393 Timetable timers fixes (#2333) 2023-10-26 10:06:54 +02:00
2e86b67eec Merge branch 'release/2.2.2' into develop 2023-10-23 20:02:32 +02:00
6071b7571b Merge branch 'release/2.2.2' 2023-10-23 20:02:25 +02:00
fcea2218b5 Version 2.2.2 2023-10-23 19:56:46 +02:00
a4a191700e Bump com.google.firebase:firebase-bom from 32.3.1 to 32.4.0 (#2331) 2023-10-23 17:28:38 +00:00
3d76d41b55 Bump com.squareup.okhttp3:logging-interceptor from 4.11.0 to 4.12.0 (#2330) 2023-10-23 16:49:58 +00:00
0e1c20a952 Bump room from 2.5.2 to 2.6.0 (#2329) 2023-10-23 16:48:23 +00:00
5d14ee7f4e Bump androidx.recyclerview:recyclerview from 1.3.1 to 1.3.2 (#2332) 2023-10-23 16:47:03 +00:00
83527d91f3 Allow direct access to weekend from day navigation when there is any lesson during weekend (#2326) 2023-10-23 13:05:46 +02:00
9d62410530 Sort teachers by name in school and teachers tab (#2327) 2023-10-23 13:05:05 +02:00
5dffbdadfa Points statistics improvements (#2328) 2023-10-23 13:04:42 +02:00
516922d5aa Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2324) 2023-10-14 19:18:28 +00:00
9098e74065 Bump com.google.android.material:material from 1.9.0 to 1.10.0 (#2325) 2023-10-14 19:17:56 +00:00
2f5577cc54 Update SDK to 34 (#2322) 2023-10-06 10:07:55 +02:00
3272c38356 Merge branch 'release/2.2.1' into develop 2023-10-03 01:14:25 +02:00
bcd305bef3 Merge branch 'release/2.2.1' 2023-10-03 01:14:17 +02:00
fc5ad16cb7 Version 2.2.1 2023-10-03 01:14:10 +02:00
c8332a0642 Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2321) 2023-10-02 22:49:26 +00:00
3212efe21e Bump com.android.tools.build:gradle from 8.1.1 to 8.1.2 (#2320) 2023-10-02 22:49:05 +00:00
693ce8217d New Crowdin updates (#2313) 2023-10-03 00:48:37 +02:00
2cdd322ed4 Add missing class_id colum in JOIN clause of students with semesters (#2317) 2023-10-02 12:22:02 +02:00
c04b3e40d2 Add negative e-mail validation in school input on support dialog (#2315) 2023-10-02 12:21:04 +02:00
d1d665bbdf Fix student auto selection if there is already active some students logged (#2314) 2023-10-02 12:20:34 +02:00
d70568c446 Merge branch 'release/2.2.0' into develop 2023-09-26 23:13:46 +02:00
1d8378e136 Merge branch 'release/2.2.0' 2023-09-26 23:13:40 +02:00
4a2bf539f0 Version 2.2.0 2023-09-26 23:13:32 +02:00
4d085f8266 New Crowdin updates (#2311) 2023-09-26 21:04:27 +00:00
fca69e7234 Add form dialog to login e-mail support (#2306) 2023-09-26 22:27:08 +02:00
711de0f77f Fix average calculation when there is no real semesters available (#2310) 2023-09-26 22:26:19 +02:00
58d5196ac9 Improve symbol input field (#2312) 2023-09-26 22:25:23 +02:00
26a95ecb99 Auto select students for login (#2307)
* Auto select students for login

* Add sign in icon to sign in button on student select screen
2023-09-26 21:02:36 +02:00
1835446468 Fix password reset related issues (#2308)
* Fix login hint in password reset field

* Don't hide first password reset button

* Change recover button label
2023-09-26 21:01:59 +02:00
4d3b16ec80 Fix password toggle icon tint after clearing error (#2309) 2023-09-26 21:01:25 +02:00
95b4d53fac Add schools API integration (#2302) 2023-09-25 19:44:13 +02:00
0fa197d520 New Crowdin updates (#2303) 2023-09-25 19:43:57 +02:00
646b4a149d Bump about_libraries from 10.8.3 to 10.9.0 (#2304) 2023-09-25 17:43:41 +00:00
afd0c8513a Bump mockk from 1.13.7 to 1.13.8 (#2305) 2023-09-25 17:43:16 +00:00
c4a3da93ca Bump com.huawei.hms:hianalytics from 6.10.0.303 to 6.12.0.300 (#2294) 2023-09-20 21:00:06 +00:00
ff2aa6f195 Bump com.huawei.agconnect:agcp from 1.9.1.300 to 1.9.1.301 (#2297) 2023-09-20 20:59:38 +00:00
1d8d71709f Bump com.huawei.agconnect:agconnect-crash from 1.9.1.300 to 1.9.1.301 (#2298) 2023-09-20 20:49:46 +00:00
aabd7345c1 Bump com.google.firebase:firebase-bom from 32.2.3 to 32.3.1 (#2299) 2023-09-20 20:49:25 +00:00
09d16cf6d8 Bump androidx.annotation:annotation from 1.6.0 to 1.7.0 (#2293) 2023-09-20 20:33:50 +00:00
81d8f7ea48 Bump androidx.lifecycle:lifecycle-livedata-ktx from 2.6.1 to 2.6.2 (#2295) 2023-09-20 20:33:30 +00:00
05a804832b Bump com.google.gms:google-services from 4.3.15 to 4.4.0 (#2300) 2023-09-20 20:33:10 +00:00
db02f0c1e1 Bump com.google.android.gms:play-services-ads from 22.3.0 to 22.4.0 (#2301) 2023-09-20 20:32:56 +00:00
0a40237809 Bump com.github.bastienpaulfr:Treessence from 1.0.5 to 1.1.2 (#2289) 2023-09-04 18:30:02 +00:00
017d46e5db Bump hilt_version from 2.47 to 2.48 (#2290) 2023-09-04 18:13:39 +00:00
8478b8b7ed Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2291) 2023-09-04 18:13:24 +00:00
8cc69728aa Bump kotlin_version from 1.9.0 to 1.9.10 (#2281) 2023-09-01 17:29:02 +00:00
c82e6ae95b New Crowdin updates (#2287) 2023-09-01 19:06:48 +02:00
50a177d18c Bump org.jetbrains.kotlinx:kotlinx-serialization-json (#2282) 2023-09-01 17:05:47 +00:00
a77b3d4cd7 Bump com.google.firebase:firebase-crashlytics-gradle from 2.9.8 to 2.9.9 (#2283) 2023-09-01 17:05:29 +00:00
aff56a8311 Bump com.google.firebase:firebase-bom from 32.2.2 to 32.2.3 (#2284) 2023-09-01 17:05:12 +00:00
5238e4d187 Bump com.google.android.gms:play-services-ads from 22.2.0 to 22.3.0 (#2285) 2023-09-01 17:04:57 +00:00
10f9812495 Bump com.android.tools.build:gradle from 8.1.0 to 8.1.1 (#2286) 2023-09-01 17:04:34 +00:00
ab1de323d4 Merge branch 'release/2.1.0' into develop 2023-08-25 00:01:47 +02:00
af346842a3 Merge branch 'release/2.1.0' 2023-08-25 00:01:42 +02:00
8f78324940 Version 2.1.0 2023-08-25 00:01:36 +02:00
3dfc55c4d1 Add admin messages to login screen (#2280) 2023-08-24 11:33:40 +02:00
fbce9e58d0 New Crowdin updates (#2277) 2023-08-23 19:46:53 +02:00
2e2b13384a Try to switch to next school year before it starts (#2278) 2023-08-23 12:24:17 +02:00
533157709b Add option to show empty tiles in the timetable (#2236)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
2023-08-22 23:47:12 +02:00
024ca89708 Bump mockk from 1.13.5 to 1.13.7 (#2275) 2023-08-22 21:39:04 +00:00
7d5a29d405 Bump org.gradle.toolchains.foojay-resolver-convention (#2276) 2023-08-22 21:20:45 +00:00
8fbe341607 Bump com.huawei.hms:hianalytics from 6.10.0.302 to 6.10.0.303 (#2272) 2023-08-22 21:20:19 +00:00
e21c17ea99 Bump com.google.firebase:firebase-crashlytics-gradle from 2.9.7 to 2.9.8 (#2270) 2023-08-22 21:20:05 +00:00
c4396036ce Bump com.google.firebase:firebase-bom from 32.2.0 to 32.2.2 (#2271) 2023-08-22 21:19:49 +00:00
722b4e5812 Bump androidx.preference:preference-ktx from 1.2.0 to 1.2.1 (#2274) 2023-08-22 21:19:33 +00:00
74820f9571 New Crowdin updates (#2265) 2023-07-31 21:32:07 +02:00
50326c7a48 Bump androidx.recyclerview:recyclerview from 1.3.0 to 1.3.1 (#2268) 2023-07-31 19:00:37 +00:00
0f129109ba Bump com.android.tools.build:gradle from 8.0.2 to 8.1.0 (#2266) 2023-07-31 19:00:17 +00:00
fc2adff997 Bump androidx.fragment:fragment-ktx from 1.6.0 to 1.6.1 (#2269) 2023-07-31 17:35:38 +00:00
7f6a13a9ee Bump coroutines from 1.7.2 to 1.7.3 (#2267) 2023-07-31 17:35:29 +00:00
64cc24ae60 Add incognito mode in messages (#1970)
Co-authored-by: Mikołaj Pich <m.pich@outlook.com>
2023-07-26 22:17:58 +02:00
91d7ee442e New Crowdin updates (#2257) 2023-07-26 19:37:06 +02:00
b296926423 Timetable widget improvements (#2219) 2023-07-25 21:05:14 +00:00
398bc513fb Bump about_libraries from 10.8.0 to 10.8.3 (#2263) 2023-07-25 10:00:31 +00:00
5b2e2ffb34 Remove tests deprecations (#2260) 2023-07-25 11:37:43 +02:00
e79c5d4d2b Bump hilt_version from 2.46.1 to 2.47 (#2261) 2023-07-24 21:36:06 +00:00
c0161f38c6 Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2262) 2023-07-24 21:33:17 +00:00
ef72218906 Bump org.gradle.toolchains.foojay-resolver-convention (#2264) 2023-07-24 21:30:37 +00:00
05741761a2 Bump kotlin_version from 1.8.22 to 1.9.0 (#2255) 2023-07-17 20:08:56 +00:00
86c7de6595 Bump room from 2.5.1 to 2.5.2 (#2250) 2023-07-17 19:15:48 +00:00
88ea753fc6 Bump coroutines from 1.7.1 to 1.7.2 (#2251) 2023-07-17 19:15:34 +00:00
d0819928f3 Bump com.huawei.agconnect:agconnect-crash from 1.9.0.300 to 1.9.1.300 (#2252) 2023-07-17 19:15:21 +00:00
8564e12b01 Bump com.huawei.agconnect:agcp from 1.9.0.300 to 1.9.1.300 (#2254) 2023-07-17 19:15:06 +00:00
29a36aaf6e Bump com.huawei.hms:hianalytics from 6.10.0.301 to 6.10.0.302 (#2253) 2023-07-17 18:11:57 +00:00
dbe608f2dd Bump about_libraries from 10.7.0 to 10.8.0 (#2249) 2023-07-17 17:54:26 +00:00
bb79b33b6d Bump com.google.android.gms:play-services-ads from 22.1.0 to 22.2.0 (#2256) 2023-07-17 17:53:58 +00:00
6e7c12a118 Bump com.google.firebase:firebase-crashlytics-gradle from 2.9.5 to 2.9.7 (#2258) 2023-07-17 17:53:38 +00:00
03cd3aeab7 Bump com.google.firebase:firebase-bom from 32.1.0 to 32.2.0 (#2259) 2023-07-17 17:53:21 +00:00
df8849639b Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2242) 2023-06-14 10:56:32 +00:00
8913b22a20 Bump org.sonarsource.scanner.gradle:sonarqube-gradle-plugin (#2237) 2023-06-08 23:25:46 +00:00
f20ffe44d5 Bump kotlin_version from 1.8.21 to 1.8.22 (#2238) 2023-06-08 23:24:27 +00:00
2f749a690b Bump androidx.fragment:fragment-ktx from 1.5.7 to 1.6.0 (#2239) 2023-06-08 23:18:23 +00:00
ae1951bf58 Merge branch 'release/2.0.8' into develop 2023-06-01 23:43:12 +02:00
201 changed files with 6221 additions and 1154 deletions

View File

@ -13,7 +13,7 @@ jobs:
environment: google-play
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
@ -49,7 +49,7 @@ jobs:
environment: app-gallery
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17

View File

@ -19,7 +19,7 @@ jobs:
environment: app-center
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
@ -89,7 +89,7 @@ jobs:
if: github.event_name != 'pull_request_target'
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17

View File

@ -19,7 +19,7 @@ jobs:
- uses: fkirc/skip-duplicate-actions@master
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
@ -45,7 +45,7 @@ jobs:
- uses: fkirc/skip-duplicate-actions@master
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17
@ -71,7 +71,7 @@ jobs:
- uses: fkirc/skip-duplicate-actions@master
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 17

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

View File

@ -1,8 +1,11 @@
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
import ru.cian.huawei.publish.ReleaseNote
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlinx-serialization'
apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.devtools.ksp'
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
@ -10,37 +13,29 @@ apply plugin: 'com.github.triplet.play'
apply plugin: 'ru.cian.huawei-publish'
apply plugin: 'com.mikepenz.aboutlibraries.plugin'
apply plugin: 'com.huawei.agconnect'
apply plugin: 'kotlin-kapt'
apply from: 'jacoco.gradle'
apply from: 'sonarqube.gradle'
apply from: 'hooks.gradle'
android {
namespace 'io.github.wulkanowy'
compileSdkVersion 33
compileSdk 34
defaultConfig {
applicationId "io.github.wulkanowy"
testApplicationId "io.github.tests.wulkanowy"
minSdkVersion 21
targetSdkVersion 33
versionCode 130
versionName "2.0.8"
targetSdkVersion 34
versionCode 139
versionName "2.2.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "app_name", "Wulkanowy"
manifestPlaceholders = [
firebase_enabled: project.hasProperty("enableFirebase"),
admob_project_id: ""
]
javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.schemaLocation": "$projectDir/schemas".toString(),
"room.incremental" : "true"
]
}
}
buildConfigField "String", "SINGLE_SUPPORT_AD_ID", "null"
buildConfigField "String", "DASHBOARD_TILE_AD_ID", "null"
@ -73,6 +68,7 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
}
debug {
minifyEnabled false
@ -82,10 +78,11 @@ android {
versionNameSuffix "-dev"
ext.enableCrashlytics = project.hasProperty("enableFirebase")
buildConfigField "String", "MESSAGES_BASE_URL", "\"https://messages.wulkanowy.net.pl\""
buildConfigField "String", "SCHOOLS_BASE_URL", '"https://schools.wulkanowy.net.pl"'
}
}
flavorDimensions "platform"
flavorDimensions += "platform"
productFlavors {
hms {
@ -116,6 +113,7 @@ android {
buildFeatures {
viewBinding true
buildConfig true
}
bundle {
@ -124,20 +122,20 @@ android {
}
}
testOptions.unitTests {
includeAndroidResources = true
testOptions {
unitTests.includeAndroidResources = true
// workaround HMS test errors https://github.com/robolectric/robolectric/issues/2750
all { jvmArgs '-noverify' }
unitTests.all { jvmArgs '-noverify' }
}
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "11"
jvmTarget = "17"
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn", "-Xjvm-default=all"]
}
@ -156,17 +154,16 @@ android {
kapt {
correctErrorTypes true
}
kotlin {
jvmToolchain(11)
ksp {
arg("room.schemaLocation", "$projectDir/schemas".toString())
}
play {
defaultToAppBundles = false
track = 'production'
releaseStatus = com.github.triplet.gradle.androidpublisher.ReleaseStatus.IN_PROGRESS
userFraction = 0.25d
updatePriority = 1
releaseStatus = ReleaseStatus.IN_PROGRESS
userFraction = 0.99d
updatePriority = 5
enabled.set(false)
}
@ -177,7 +174,7 @@ huaweiPublish {
buildFormat = "aab"
deployType = "publish"
releaseNotes = [
new ru.cian.huawei.publish.ReleaseNote(
new ReleaseNote(
"pl-PL",
"$projectDir/src/main/play/release-notes/pl-PL/default.txt"
)
@ -187,48 +184,48 @@ huaweiPublish {
}
ext {
work_manager = "2.8.1"
android_hilt = "1.0.0"
room = "2.5.1"
chucker = "3.5.2"
mockk = "1.13.5"
coroutines = "1.7.1"
work_manager = "2.9.0"
android_hilt = "1.1.0"
room = "2.6.1"
chucker = "4.0.0"
mockk = "1.13.8"
coroutines = "1.7.3"
}
dependencies {
implementation 'io.github.wulkanowy:sdk:2.0.8'
implementation 'io.github.wulkanowy:sdk:2.2.7'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
implementation "androidx.core:core-ktx:1.10.1"
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation "androidx.activity:activity-ktx:1.7.2"
implementation "androidx.activity:activity-ktx:1.8.2"
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "androidx.fragment:fragment-ktx:1.5.7"
implementation "androidx.annotation:annotation:1.6.0"
implementation "androidx.fragment:fragment-ktx:1.6.2"
implementation "androidx.annotation:annotation:1.7.1"
implementation "androidx.preference:preference-ktx:1.2.0"
implementation "androidx.recyclerview:recyclerview:1.3.0"
implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.recyclerview:recyclerview:1.3.2"
implementation "androidx.viewpager2:viewpager2:1.1.0-beta02"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation "com.google.android.material:material:1.9.0"
implementation "com.google.android.material:material:1.10.0"
implementation "com.github.wulkanowy:material-chips-input:2.3.1"
implementation "com.github.PhilJay:MPAndroidChart:v3.1.0"
implementation 'com.github.lopspower:CircularImageView:4.3.0'
implementation "androidx.work:work-runtime-ktx:$work_manager"
implementation "androidx.work:work-runtime:$work_manager"
playImplementation "androidx.work:work-gcm:$work_manager"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.2"
implementation "androidx.room:room-runtime:$room"
implementation "androidx.room:room-ktx:$room"
kapt "androidx.room:room-compiler:$room"
ksp "androidx.room:room-compiler:$room"
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
@ -240,33 +237,34 @@ dependencies {
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.11.0"
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
implementation "com.jakewharton.timber:timber:5.0.1"
implementation "at.favre.lib:slf4j-timber:1.0.1"
implementation 'com.github.bastienpaulfr:Treessence:1.0.5'
implementation 'com.github.bastienpaulfr:Treessence:1.1.2'
implementation "com.mikepenz:aboutlibraries-core:$about_libraries"
implementation "io.coil-kt:coil:2.4.0"
implementation 'io.coil-kt:coil:2.5.0'
implementation "io.github.wulkanowy:AppKillerManager:3.0.1"
implementation 'me.xdrop:fuzzywuzzy:1.4.0'
implementation 'com.fredporciuncula:flow-preferences:1.9.1'
implementation 'org.apache.commons:commons-text:1.10.0'
implementation 'org.apache.commons:commons-text:1.11.0'
playImplementation platform('com.google.firebase:firebase-bom:32.1.0')
playImplementation platform('com.google.firebase:firebase-bom:32.7.0')
playImplementation 'com.google.firebase:firebase-analytics-ktx'
playImplementation 'com.google.firebase:firebase-messaging:'
playImplementation 'com.google.firebase:firebase-messaging'
playImplementation 'com.google.firebase:firebase-crashlytics:'
playImplementation 'com.google.firebase:firebase-config-ktx'
playImplementation 'com.google.android.play:core:1.10.3'
playImplementation 'com.google.android.play:core-ktx:1.8.1'
playImplementation 'com.google.android.gms:play-services-ads:22.1.0'
playImplementation 'com.google.android.gms:play-services-ads:22.6.0'
playImplementation "com.google.android.play:integrity:1.3.0"
playImplementation 'com.google.android.play:app-update-ktx:2.1.0'
playImplementation 'com.google.android.play:review-ktx:2.0.1'
hmsImplementation 'com.huawei.hms:hianalytics:6.10.0.301'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.0.300'
hmsImplementation 'com.huawei.hms:hianalytics:6.12.0.300'
hmsImplementation 'com.huawei.agconnect:agconnect-crash:1.9.1.302'
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$chucker"
releaseImplementation "com.github.chuckerteam.chucker:library-no-op:$chucker"
debugImplementation "com.github.ChuckerTeam.Chucker:library:$chucker"
debugImplementation "com.github.chuckerteam.chucker:library:$chucker"
debugImplementation 'com.github.amitshekhariitbhu.Android-Debug-Database:debug-db:1.0.6'
debugImplementation 'com.github.haroldadmin:WhatTheStack:1.0.0-alpha04'
@ -275,7 +273,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation 'org.robolectric:robolectric:4.10.3'
testImplementation 'org.robolectric:robolectric:4.11.1'
testImplementation "androidx.test:runner:1.5.2"
testImplementation "androidx.test.ext:junit:1.1.5"
testImplementation "androidx.test:core:1.5.0"

View File

@ -1,16 +1,16 @@
apply plugin: "jacoco"
jacoco {
toolVersion "0.8.7"
toolVersion "0.8.11"
reportsDirectory.set(file("$buildDir/reports"))
}
tasks.withType(Test) {
tasks.withType(Test).configureEach {
jacoco.includeNoLocationClasses = true
jacoco.excludes = ['jdk.internal.*']
}
task jacocoTestReport(type: JacocoReport) {
tasks.register('jacocoTestReport', JacocoReport) {
group = "Reporting"
description = "Generate Jacoco coverage reports"
@ -33,19 +33,19 @@ task jacocoTestReport(type: JacocoReport) {
'**/*_Factory.*']
classDirectories.setFrom(fileTree(
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/fdroidDebug",
excludes: excludes
dir: "$buildDir/tmp/kotlin-classes/fdroidDebug",
excludes: excludes
))
sourceDirectories.setFrom(files([
"src/main/java",
"src/fdroid/java"
"src/main/java",
"src/fdroid/java"
]))
executionData.setFrom(fileTree(
dir: project.projectDir,
includes: ["**/*.exec", "**/*.ec"]
dir: project.projectDir,
includes: ["**/*.exec", "**/*.ec"]
))
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.utils
import android.view.View
import javax.inject.Inject
class InAppUpdateHelper @Inject constructor() {
lateinit var messageContainer: View
fun checkAndInstallUpdates() {}
fun onResume() {}
}

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.utils
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class IntegrityHelper @Inject constructor() {
@Suppress("UNUSED_PARAMETER")
fun getIntegrityToken(requestId: String): String? = null
}

View File

@ -1,17 +0,0 @@
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

@ -2,8 +2,8 @@ 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 fr.bipi.treessence.base.FormatterPriorityTree
import fr.bipi.treessence.common.StackTraceRecorder
class CrashLogTree : FormatterPriorityTree(Log.VERBOSE) {

View File

@ -0,0 +1,13 @@
package io.github.wulkanowy.utils
import android.view.View
import javax.inject.Inject
class InAppUpdateHelper @Inject constructor() {
lateinit var messageContainer: View
fun checkAndInstallUpdates() {}
fun onResume() {}
}

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.utils
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class IntegrityHelper @Inject constructor() {
@Suppress("UNUSED_PARAMETER")
fun getIntegrityToken(requestId: String): String? = null
}

View File

@ -1,17 +0,0 @@
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

@ -50,5 +50,9 @@
{
"displayName": "Tomasz F.",
"githubUsername": "Pengwius"
},
{
"displayName": "Antoni Paduch",
"githubUsername": "janAte1"
}
]

View File

@ -1,24 +1,30 @@
package io.github.wulkanowy
import android.app.Application
import android.util.Log.*
import android.util.Log.DEBUG
import android.util.Log.INFO
import android.util.Log.VERBOSE
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
import com.yariksoffice.lingver.Lingver
import dagger.hilt.android.HiltAndroidApp
import fr.bipi.tressence.file.FileLoggerTree
import fr.bipi.treessence.file.FileLoggerTree
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.ui.base.ThemeManager
import io.github.wulkanowy.utils.*
import io.github.wulkanowy.utils.ActivityLifecycleLogger
import io.github.wulkanowy.utils.AdsHelper
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.CrashLogExceptionTree
import io.github.wulkanowy.utils.CrashLogTree
import io.github.wulkanowy.utils.DebugLogTree
import io.github.wulkanowy.utils.RemoteConfigHelper
import timber.log.Timber
import javax.inject.Inject
@HiltAndroidApp
class WulkanowyApp : Application(), Configuration.Provider {
@Inject
lateinit var workerFactory: HiltWorkerFactory
@Inject
lateinit var themeManager: ThemeManager
@ -37,6 +43,15 @@ class WulkanowyApp : Application(), Configuration.Provider {
@Inject
lateinit var remoteConfigHelper: RemoteConfigHelper
@Inject
lateinit var workerFactory: HiltWorkerFactory
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
.build()
override fun onCreate() {
super.onCreate()
initializeAppLanguage()
@ -74,9 +89,4 @@ class WulkanowyApp : Application(), Configuration.Provider {
analyticsHelper.logEvent("language", "startup" to preferencesRepository.appLanguage)
}
}
override fun getWorkManagerConfiguration() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.setMinimumLoggingLevel(if (appInfo.isDebug) VERBOSE else INFO)
.build()
}

View File

@ -14,6 +14,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.api.SchoolsService
import io.github.wulkanowy.data.db.AppDatabase
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
@ -82,19 +83,29 @@ internal class DataModule {
@Singleton
@Provides
fun provideRetrofit(
fun provideAdminMessageService(
okHttpClient: OkHttpClient,
json: Json,
appInfo: AppInfo
): Retrofit = Retrofit.Builder()
): AdminMessageService = Retrofit.Builder()
.baseUrl(appInfo.messagesBaseUrl)
.client(okHttpClient)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
.create()
@Singleton
@Provides
fun provideAdminMessageService(retrofit: Retrofit): AdminMessageService = retrofit.create()
fun provideSchoolsService(
okHttpClient: OkHttpClient,
json: Json,
appInfo: AppInfo,
): SchoolsService = Retrofit.Builder()
.baseUrl(appInfo.schoolsBaseUrl)
.client(okHttpClient)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
.create()
@Singleton
@Provides

View File

@ -148,7 +148,7 @@ inline fun <ResultType, RequestType, T> networkBoundResource(
crossinline saveFetchResult: suspend (old: ResultType, new: RequestType) -> Unit,
crossinline onFetchFailed: (Throwable) -> Unit = { },
crossinline shouldFetch: (ResultType) -> Boolean = { true },
crossinline mapResult: (ResultType) -> T
crossinline mapResult: (ResultType) -> T,
) = flow {
emit(Resource.Loading())

View File

@ -0,0 +1,14 @@
package io.github.wulkanowy.data.api
import io.github.wulkanowy.data.pojos.IntegrityRequest
import io.github.wulkanowy.data.pojos.LoginEvent
import retrofit2.http.Body
import retrofit2.http.POST
import javax.inject.Singleton
@Singleton
interface SchoolsService {
@POST("/log/loginEvent")
suspend fun logLoginEvent(@Body request: IntegrityRequest<LoginEvent>)
}

View File

@ -50,6 +50,7 @@ import javax.inject.Singleton
AutoMigration(from = 51, to = 52),
AutoMigration(from = 54, to = 55, spec = Migration55::class),
AutoMigration(from = 55, to = 56),
AutoMigration(from = 56, to = 57, spec = Migration57::class),
],
version = AppDatabase.VERSION_SCHEMA,
exportSchema = true
@ -58,7 +59,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 56
const val VERSION_SCHEMA = 57
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.db
import androidx.room.TypeConverter
import io.github.wulkanowy.data.enums.MessageType
import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.serialization.SerializationException
@ -68,4 +69,9 @@ class Converters {
@TypeConverter
fun stringToDestination(destination: String): Destination = json.decodeFromString(destination)
@TypeConverter
fun messageTypesToString(types: List<MessageType>): String = json.encodeToString(types)
@TypeConverter
fun stringToMessageTypes(text: String): List<MessageType> = json.decodeFromString(text)
}

View File

@ -22,4 +22,4 @@ abstract class AdminMessageDao : BaseDao<AdminMessage> {
deleteAll(oldMessages)
insertAll(newMessages)
}
}
}

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.data.db.dao
import androidx.room.*
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentName
import io.github.wulkanowy.data.db.entities.StudentNickAndAvatar
@ -33,12 +34,12 @@ abstract class StudentDao {
abstract suspend fun loadAll(): List<Student>
@Transaction
@Query("SELECT * FROM Students")
abstract suspend fun loadStudentsWithSemesters(): List<StudentWithSemesters>
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id")
abstract suspend fun loadStudentsWithSemesters(): Map<Student, List<Semester>>
@Transaction
@Query("SELECT * FROM Students WHERE id = :id")
abstract suspend fun loadStudentWithSemestersById(id: Long): StudentWithSemesters?
@Query("SELECT * FROM Students JOIN Semesters ON Students.student_id = Semesters.student_id AND Students.class_id = Semesters.class_id WHERE Students.id = :id")
abstract suspend fun loadStudentWithSemestersById(id: Long): Map<Student, List<Semester>>
@Query("UPDATE Students SET is_current = 1 WHERE id = :id")
abstract suspend fun updateCurrent(id: Long)

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.github.wulkanowy.data.enums.MessageType
import kotlinx.serialization.Serializable
@Serializable
@ -33,7 +34,8 @@ data class AdminMessage(
val priority: String,
val type: String,
@ColumnInfo(name = "types", defaultValue = "[]")
val types: List<MessageType> = emptyList(),
@ColumnInfo(name = "is_dismissible")
val isDismissible: Boolean = false

View File

@ -1,13 +1,8 @@
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

@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration10 : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Grades_Summary RENAME TO GradesSummary")
}
}

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration11 : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Grades_temp (
id INTEGER PRIMARY KEY NOT NULL,
is_read INTEGER NOT NULL,
@ -26,9 +27,10 @@ class Migration11 : Migration(10, 11) {
date INTEGER NOT NULL,
teacher TEXT NOT NULL
)
""")
database.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades")
database.execSQL("DROP TABLE Grades")
database.execSQL("ALTER TABLE Grades_temp RENAME TO Grades")
"""
)
db.execSQL("INSERT INTO Grades_temp SELECT * FROM Grades")
db.execSQL("DROP TABLE Grades")
db.execSQL("ALTER TABLE Grades_temp RENAME TO Grades")
}
}

View File

@ -5,16 +5,17 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration12 : Migration(11, 12) {
override fun migrate(database: SupportSQLiteDatabase) {
createTempStudentsTable(database)
replaceStudentTable(database)
updateStudentsWithClassId(database, getStudentsIds(database))
removeStudentsWithNoClassId(database)
ensureThereIsOnlyOneCurrentStudent(database)
override fun migrate(db: SupportSQLiteDatabase) {
createTempStudentsTable(db)
replaceStudentTable(db)
updateStudentsWithClassId(db, getStudentsIds(db))
removeStudentsWithNoClassId(db)
ensureThereIsOnlyOneCurrentStudent(db)
}
private fun createTempStudentsTable(database: SupportSQLiteDatabase) {
database.execSQL("""
private fun createTempStudentsTable(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Students_tmp (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
endpoint TEXT NOT NULL,
@ -30,15 +31,16 @@ class Migration12 : Migration(11, 12) {
registration_date INTEGER NOT NULL,
class_id INTEGER NOT NULL
)
""")
database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)")
"""
)
db.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students_tmp (email, symbol, student_id, school_id, class_id)")
}
private fun replaceStudentTable(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
database.execSQL("INSERT INTO Students_tmp SELECT * FROM Students")
database.execSQL("DROP TABLE Students")
database.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
private fun replaceStudentTable(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
db.execSQL("INSERT INTO Students_tmp SELECT * FROM Students")
db.execSQL("DROP TABLE Students")
db.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
}
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
@ -54,18 +56,18 @@ class Migration12 : Migration(11, 12) {
return students
}
private fun updateStudentsWithClassId(database: SupportSQLiteDatabase, students: List<Int>) {
private fun updateStudentsWithClassId(db: SupportSQLiteDatabase, students: List<Int>) {
students.forEach {
database.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it")
db.execSQL("UPDATE Students SET class_id = IFNULL((SELECT class_id FROM Semesters WHERE student_id = $it), 0) WHERE student_id = $it")
}
}
private fun removeStudentsWithNoClassId(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Students WHERE class_id = 0")
private fun removeStudentsWithNoClassId(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM Students WHERE class_id = 0")
}
private fun ensureThereIsOnlyOneCurrentStudent(database: SupportSQLiteDatabase) {
database.execSQL("UPDATE Students SET is_current = 0")
database.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)")
private fun ensureThereIsOnlyOneCurrentStudent(db: SupportSQLiteDatabase) {
db.execSQL("UPDATE Students SET is_current = 0")
db.execSQL("UPDATE Students SET is_current = 1 WHERE id = (SELECT MAX(id) FROM Students)")
}
}

View File

@ -5,27 +5,30 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration13 : Migration(12, 13) {
override fun migrate(database: SupportSQLiteDatabase) {
addClassNameToStudents(database, getStudentsIds(database))
updateSemestersTable(database)
markAtLeastAndOnlyOneSemesterAtCurrent(database, getStudentsAndClassIds(database))
clearMessagesTable(database)
override fun migrate(db: SupportSQLiteDatabase) {
addClassNameToStudents(db, getStudentsIds(db))
updateSemestersTable(db)
markAtLeastAndOnlyOneSemesterAtCurrent(db, getStudentsAndClassIds(db))
clearMessagesTable(db)
}
private fun addClassNameToStudents(database: SupportSQLiteDatabase, students: List<Pair<Int, String>>) {
database.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL")
private fun addClassNameToStudents(
db: SupportSQLiteDatabase,
students: List<Pair<Int, String>>
) {
db.execSQL("ALTER TABLE Students ADD COLUMN class_name TEXT DEFAULT \"\" NOT NULL")
students.forEach { (id, name) ->
val schoolName = name.substringAfter(" - ")
val className = name.substringBefore(" - ", "").replace("Klasa ", "")
database.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'")
database.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'")
db.execSQL("UPDATE Students SET class_name = '$className' WHERE id = '$id'")
db.execSQL("UPDATE Students SET school_name = '$schoolName' WHERE id = '$id'")
}
}
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
private fun getStudentsIds(db: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val students = mutableListOf<Pair<Int, String>>()
database.query("SELECT id, school_name FROM Students").use {
db.query("SELECT id, school_name FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(it.getInt(0) to it.getString(1))
@ -36,15 +39,15 @@ class Migration13 : Migration(12, 13) {
return students
}
private fun updateSemestersTable(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL")
database.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL")
database.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL")
private fun updateSemestersTable(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Semesters ADD COLUMN school_year INTEGER DEFAULT 1970 NOT NULL")
db.execSQL("ALTER TABLE Semesters ADD COLUMN start INTEGER DEFAULT 0 NOT NULL")
db.execSQL("ALTER TABLE Semesters ADD COLUMN `end` INTEGER DEFAULT 0 NOT NULL")
}
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
private fun getStudentsAndClassIds(db: SupportSQLiteDatabase): List<Pair<Int, Int>> {
val students = mutableListOf<Pair<Int, Int>>()
database.query("SELECT student_id, class_id FROM Students").use {
db.query("SELECT student_id, class_id FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(it.getInt(0) to it.getInt(1))
@ -55,14 +58,17 @@ class Migration13 : Migration(12, 13) {
return students
}
private fun markAtLeastAndOnlyOneSemesterAtCurrent(database: SupportSQLiteDatabase, students: List<Pair<Int, Int>>) {
private fun markAtLeastAndOnlyOneSemesterAtCurrent(
db: SupportSQLiteDatabase,
students: List<Pair<Int, Int>>
) {
students.forEach { (studentId, classId) ->
database.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'")
database.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)")
db.execSQL("UPDATE Semesters SET is_current = 0 WHERE student_id = '$studentId' AND class_id = '$classId'")
db.execSQL("UPDATE Semesters SET is_current = 1 WHERE id = (SELECT id FROM Semesters WHERE student_id = '$studentId' AND class_id = '$classId' ORDER BY semester_id DESC)")
}
}
private fun clearMessagesTable(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Messages")
private fun clearMessagesTable(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM Messages")
}
}

View File

@ -5,9 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration14 : Migration(13, 14) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS GradesSummary")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS GradesSummary")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS GradesSummary (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
semester_id INTEGER NOT NULL,

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration15 : Migration(14, 15) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS MobileDevices (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
@ -14,6 +15,7 @@ class Migration15 : Migration(14, 15) {
name TEXT NOT NULL,
date INTEGER NOT NULL
)
""")
"""
)
}
}

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration16 : Migration(15, 16) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Teachers (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
@ -15,6 +16,7 @@ class Migration16 : Migration(15, 16) {
name TEXT NOT NULL,
short_name TEXT NOT NULL
)
""")
"""
)
}
}

View File

@ -5,13 +5,14 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration17 : Migration(16, 17) {
override fun migrate(database: SupportSQLiteDatabase) {
createGradesPointsStatisticsTable(database)
truncateSemestersTable(database)
override fun migrate(db: SupportSQLiteDatabase) {
createGradesPointsStatisticsTable(db)
truncateSemestersTable(db)
}
private fun createGradesPointsStatisticsTable(database: SupportSQLiteDatabase) {
database.execSQL("""
private fun createGradesPointsStatisticsTable(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS GradesPointsStatistics(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
@ -20,10 +21,11 @@ class Migration17 : Migration(16, 17) {
others REAL NOT NULL,
student REAL NOT NULL
)
""")
"""
)
}
private fun truncateSemestersTable(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Semesters")
private fun truncateSemestersTable(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM Semesters")
}
}

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration18 : Migration(17, 18) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS School (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -6,16 +6,17 @@ import io.github.wulkanowy.data.db.SharedPrefProvider
class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migration(18, 19) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateMessages(database)
migrateGrades(database)
migrateStudents(database)
override fun migrate(db: SupportSQLiteDatabase) {
migrateMessages(db)
migrateGrades(db)
migrateStudents(db)
migrateSharedPreferences()
}
private fun migrateMessages(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Messages")
database.execSQL("""
private fun migrateMessages(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE Messages")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
is_notified INTEGER NOT NULL,
@ -34,12 +35,14 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio
read_by INTEGER NOT NULL,
removed INTEGER NOT NULL
)
""")
"""
)
}
private fun migrateGrades(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Grades")
database.execSQL("""
private fun migrateGrades(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE Grades")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Grades (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
is_read INTEGER NOT NULL,
@ -59,11 +62,13 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio
date INTEGER NOT NULL,
teacher TEXT NOT NULL
)
""")
"""
)
}
private fun migrateStudents(database: SupportSQLiteDatabase) {
database.execSQL("""
private fun migrateStudents(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Students_tmp (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
scrapper_base_url TEXT NOT NULL,
@ -86,26 +91,29 @@ class Migration19(private val sharedPrefProvider: SharedPrefProvider) : Migratio
is_current INTEGER NOT NULL,
registration_date INTEGER NOT NULL
)
""")
"""
)
database.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;")
database.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";")
database.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;")
db.execSQL("ALTER TABLE Students ADD COLUMN scrapperBaseUrl TEXT NOT NULL DEFAULT \"\";")
db.execSQL("ALTER TABLE Students ADD COLUMN apiBaseUrl TEXT NOT NULL DEFAULT \"\";")
db.execSQL("ALTER TABLE Students ADD COLUMN is_parent INT NOT NULL DEFAULT 0;")
db.execSQL("ALTER TABLE Students ADD COLUMN loginMode TEXT NOT NULL DEFAULT \"\";")
db.execSQL("ALTER TABLE Students ADD COLUMN certificateKey TEXT NOT NULL DEFAULT \"\";")
db.execSQL("ALTER TABLE Students ADD COLUMN privateKey TEXT NOT NULL DEFAULT \"\";")
db.execSQL("ALTER TABLE Students ADD COLUMN user_login_id INTEGER NOT NULL DEFAULT 0;")
database.execSQL("""
db.execSQL(
"""
INSERT INTO Students_tmp(
id, scrapper_base_url, mobile_base_url, is_parent, login_type, login_mode, certificate_key, private_key, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date)
SELECT
id, endpoint, apiBaseUrl, is_parent, loginType, "SCRAPPER", certificateKey, privateKey, email, password, symbol, student_id, user_login_id, student_name, school_id, school_name, school_id, school_name, class_name, class_id, is_current, registration_date
FROM Students
""")
database.execSQL("DROP TABLE Students")
database.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
database.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)")
"""
)
db.execSQL("DROP TABLE Students")
db.execSQL("ALTER TABLE Students_tmp RENAME TO Students")
db.execSQL("CREATE UNIQUE INDEX index_Students_email_symbol_student_id_school_id_class_id ON Students (email, symbol, student_id, school_id, class_id)")
}
private fun migrateSharedPreferences() {

View File

@ -5,14 +5,16 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS LuckyNumbers (
id INTEGER PRIMARY KEY NOT NULL,
is_notified INTEGER NOT NULL,
student_id INTEGER NOT NULL,
date INTEGER NOT NULL,
lucky_number INTEGER NOT NULL)
""")
"""
)
}
}

View File

@ -5,14 +5,15 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration20 : Migration(19, 20) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateTimetable(database)
truncateSubjects(database)
override fun migrate(db: SupportSQLiteDatabase) {
migrateTimetable(db)
truncateSubjects(db)
}
private fun migrateTimetable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE Timetable")
database.execSQL("""
private fun migrateTimetable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE Timetable")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Timetable` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`student_id` INTEGER NOT NULL,
@ -33,10 +34,11 @@ class Migration20 : Migration(19, 20) {
`changes` INTEGER NOT NULL,
`canceled` INTEGER NOT NULL
)
""")
"""
)
}
private fun truncateSubjects(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Subjects")
private fun truncateSubjects(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM Subjects")
}
}

View File

@ -5,11 +5,11 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration21 : Migration(20, 21) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Attendance ADD COLUMN excusable INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Attendance ADD COLUMN time_id INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Attendance ADD COLUMN excuse_status TEXT DEFAULT NULL")
database.execSQL("DELETE FROM Semesters")
db.execSQL("DELETE FROM Semesters")
}
}

View File

@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration22 : Migration(21, 22) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN school_short TEXT NOT NULL DEFAULT ''")
}
}

View File

@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration23 : Migration(22, 23) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''")
database.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Notes ADD COLUMN teacher_symbol TEXT NOT NULL DEFAULT ''")
db.execSQL("ALTER TABLE Notes ADD COLUMN category_type INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Notes ADD COLUMN is_points_show INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Notes ADD COLUMN points INTEGER NOT NULL DEFAULT 0")
}
}

View File

@ -5,9 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration24 : Migration(23, 24) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Messages ADD COLUMN has_attachments INTEGER NOT NULL DEFAULT 0")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS MessageAttachments (
real_id INTEGER NOT NULL,
message_id INTEGER NOT NULL,
@ -16,6 +17,7 @@ class Migration24 : Migration(23, 24) {
filename TEXT NOT NULL,
PRIMARY KEY(real_id)
)
""")
"""
)
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration25 : Migration(24, 25) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Homework ADD COLUMN is_done INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Homework ADD COLUMN attachments TEXT NOT NULL DEFAULT \"[]\"")
}
}

View File

@ -5,10 +5,10 @@ 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")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_predicted_grade_notified INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE GradesSummary ADD COLUMN is_final_grade_notified INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE GradesSummary ADD COLUMN predicted_grade_last_change INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE GradesSummary ADD COLUMN final_grade_last_change INTEGER NOT NULL DEFAULT 0")
}
}

View File

@ -5,24 +5,25 @@ 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 \"\"")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN user_name TEXT NOT NULL DEFAULT \"\"")
val students = getStudentsIdsAndNames(database)
val units = getReportingUnits(database)
val students = getStudentsIdsAndNames(db)
val units = getReportingUnits(db)
students.forEach { (id, userLoginId, studentName) ->
val userNameFromUnits = units.singleOrNull { (senderId, _) -> senderId == userLoginId }?.second
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'")
db.execSQL("UPDATE Students SET user_name = '$userName' WHERE id = '$id'")
}
}
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
private fun getStudentsIdsAndNames(db: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
val students = mutableListOf<Triple<Long, Int, String>>()
database.query("SELECT id, user_login_id, student_name FROM Students").use {
db.query("SELECT id, user_login_id, student_name FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
@ -33,9 +34,9 @@ class Migration27 : Migration(26, 27) {
return students
}
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
private fun getReportingUnits(db: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val units = mutableListOf<Pair<Int, String>>()
database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
db.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
if (it.moveToFirst()) {
do {
units.add(it.getInt(0) to it.getString(1))

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration28 : Migration(27, 28) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Conferences (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -5,9 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration29 : Migration(28, 29) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS GradesStatistics")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS GradesStatistics")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS GradeSemesterStatistics (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
@ -16,8 +17,10 @@ class Migration29 : Migration(28, 29) {
amounts TEXT NOT NULL,
student_grade INTEGER NOT NULL
)
""")
database.execSQL("""
"""
)
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS GradePartialStatistics (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration3 : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS CompletedLesson (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration30 : Migration(29, 30) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE TimetableAdditional (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,
@ -16,6 +17,7 @@ class Migration30 : Migration(29, 30) {
date INTEGER NOT NULL,
subject TEXT NOT NULL
)
""")
"""
)
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration31 : Migration(30, 31) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""CREATE TABLE IF NOT EXISTS StudentInfo (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration32 : Migration(31, 32) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN nick TEXT NOT NULL DEFAULT \"\"")
}
}

View File

@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration33 : Migration(32, 33) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS StudentInfo")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS StudentInfo")
database.execSQL(
db.execSQL(
"""CREATE TABLE IF NOT EXISTS StudentInfo (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration34 : Migration(33, 34) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM ReportingUnits")
database.execSQL("DELETE FROM Recipients")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM ReportingUnits")
db.execSQL("DELETE FROM Recipients")
}
}

View File

@ -7,13 +7,13 @@ import io.github.wulkanowy.utils.AppInfo
class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
database.query("SELECT * FROM Students").use {
db.query("SELECT * FROM Students").use {
while (it.moveToNext()) {
val studentId = it.getLongOrNull(0)
database.execSQL(
db.execSQL(
"""
UPDATE Students
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration36 : Migration(35, 36) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Exams ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE Homework ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration37 : Migration(36, 37) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS TimetableHeaders (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration38 : Migration(37, 38) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`student_id` INTEGER NOT NULL,
@ -14,6 +15,7 @@ class Migration38 : Migration(37, 38) {
`subject` TEXT NOT NULL,
`content` TEXT NOT NULL
)
""")
"""
)
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration39 : Migration(38, 39) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Conferences ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE SchoolAnnouncements ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Conferences ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE SchoolAnnouncements ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
}
}
}

View File

@ -5,9 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration4 : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Messages")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER PRIMARY KEY NOT NULL,
is_notified INTEGER NOT NULL,

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration40 : Migration(39, 40) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Notifications` (
`student_id` INTEGER NOT NULL,
@ -20,4 +20,4 @@ class Migration40 : Migration(39, 40) {
"""
)
}
}
}

View File

@ -7,9 +7,9 @@ import io.github.wulkanowy.data.enums.GradeExpandMode
class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migration(40, 41) {
override fun migrate(database: SupportSQLiteDatabase) {
override fun migrate(db: SupportSQLiteDatabase) {
migrateSharedPreferences()
database.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0")
db.execSQL("ALTER TABLE Homework ADD COLUMN is_added_by_user INTEGER NOT NULL DEFAULT 0")
}
private fun migrateSharedPreferences() {
@ -18,4 +18,4 @@ class Migration41(private val sharedPrefProvider: SharedPrefProvider) : Migratio
}
sharedPrefProvider.delete("pref_key_expand_grade")
}
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration42 : Migration(41, 42) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""CREATE TABLE IF NOT EXISTS `AdminMessages` (
`id` INTEGER NOT NULL,
`title` TEXT NOT NULL,
@ -21,4 +21,4 @@ class Migration42 : Migration(41, 42) {
PRIMARY KEY(`id`))"""
)
}
}
}

View File

@ -5,8 +5,8 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration43 : Migration(42, 43) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
database.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Timetable ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
db.execSQL("ALTER TABLE Attendance ADD COLUMN is_notified INTEGER NOT NULL DEFAULT 1")
}
}

View File

@ -5,7 +5,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration44 : Migration(43, 44) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE AdminMessages ADD COLUMN is_dismissible INTEGER NOT NULL DEFAULT 0")
}
}

View File

@ -8,65 +8,65 @@ import java.time.ZoneOffset
class Migration46 : Migration(45, 46) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateConferences(database)
migrateMessages(database)
migrateMobileDevices(database)
migrateNotifications(database)
migrateTimetable(database)
migrateTimetableAdditional(database)
override fun migrate(db: SupportSQLiteDatabase) {
migrateConferences(db)
migrateMessages(db)
migrateMobileDevices(db)
migrateNotifications(db)
migrateTimetable(db)
migrateTimetableAdditional(db)
}
private fun migrateConferences(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Conferences").use {
private fun migrateConferences(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM Conferences").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
db.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMessages(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Messages").use {
private fun migrateMessages(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM Messages").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
db.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMobileDevices(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM MobileDevices").use {
private fun migrateMobileDevices(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM MobileDevices").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
db.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateNotifications(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Notifications").use {
private fun migrateNotifications(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM Notifications").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
db.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateTimetable(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Timetable").use {
private fun migrateTimetable(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM Timetable").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
@ -74,13 +74,13 @@ class Migration46 : Migration(45, 46) {
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
db.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}
private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM TimetableAdditional").use {
private fun migrateTimetableAdditional(db: SupportSQLiteDatabase) {
db.query("SELECT * FROM TimetableAdditional").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
@ -88,7 +88,7 @@ class Migration46 : Migration(45, 46) {
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
db.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}

View File

@ -5,10 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration49 : Migration(48, 49) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS SchoolAnnouncements")
database.execSQL(
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `SchoolAnnouncements` (
`user_login_id` INTEGER NOT NULL,

View File

@ -7,11 +7,16 @@ import java.time.ZoneOffset
class Migration5 : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL")
database.execSQL("UPDATE Students SET registration_date = '${now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()}'")
database.execSQL("DROP TABLE IF EXISTS Notes")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Students ADD COLUMN registration_date INTEGER DEFAULT 0 NOT NULL")
db.execSQL(
"UPDATE Students SET registration_date = '${
now().atZone(ZoneOffset.UTC).toInstant().toEpochMilli()
}'"
)
db.execSQL("DROP TABLE IF EXISTS Notes")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Notes (
id INTEGER PRIMARY KEY NOT NULL,
is_read INTEGER NOT NULL,
@ -21,6 +26,7 @@ class Migration5 : Migration(4, 5) {
teacher TEXT NOT NULL,
category TEXT NOT NULL,
content TEXT NOT NULL)
""")
"""
)
}
}

View File

@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration50 : Migration(49, 50) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS MobileDevices")
database.execSQL(
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS MobileDevices")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `MobileDevices` (
`user_login_id` INTEGER NOT NULL,

View File

@ -5,17 +5,17 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration51 : Migration(50, 51) {
override fun migrate(database: SupportSQLiteDatabase) {
createMailboxTable(database)
recreateMessagesTable(database)
recreateMessageAttachmentsTable(database)
recreateRecipientsTable(database)
deleteReportingUnitTable(database)
override fun migrate(db: SupportSQLiteDatabase) {
createMailboxTable(db)
recreateMessagesTable(db)
recreateMessageAttachmentsTable(db)
recreateRecipientsTable(db)
deleteReportingUnitTable(db)
}
private fun createMailboxTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Mailboxes")
database.execSQL(
private fun createMailboxTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Mailboxes")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Mailboxes` (
`globalKey` TEXT NOT NULL,
@ -30,9 +30,9 @@ class Migration51 : Migration(50, 51) {
)
}
private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL(
private fun recreateMessagesTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Messages")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Messages` (
`message_global_key` TEXT NOT NULL,
@ -52,9 +52,9 @@ class Migration51 : Migration(50, 51) {
)
}
private fun recreateMessageAttachmentsTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS MessageAttachments")
database.execSQL(
private fun recreateMessageAttachmentsTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS MessageAttachments")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `MessageAttachments` (
`real_id` INTEGER NOT NULL,
@ -66,9 +66,9 @@ class Migration51 : Migration(50, 51) {
)
}
private fun recreateRecipientsTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Recipients")
database.execSQL(
private fun recreateRecipientsTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Recipients")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Recipients` (
`mailboxGlobalKey` TEXT NOT NULL,
@ -82,7 +82,7 @@ class Migration51 : Migration(50, 51) {
)
}
private fun deleteReportingUnitTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS ReportingUnits")
private fun deleteReportingUnitTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS ReportingUnits")
}
}

View File

@ -5,14 +5,14 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration53 : Migration(52, 53) {
override fun migrate(database: SupportSQLiteDatabase) {
createMailboxTable(database)
recreateMessagesTable(database)
override fun migrate(db: SupportSQLiteDatabase) {
createMailboxTable(db)
recreateMessagesTable(db)
}
private fun createMailboxTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Mailboxes")
database.execSQL(
private fun createMailboxTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Mailboxes")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Mailboxes` (
`globalKey` TEXT NOT NULL,
@ -29,9 +29,9 @@ class Migration53 : Migration(52, 53) {
)
}
private fun recreateMessagesTable(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL(
private fun recreateMessagesTable(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Messages")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS `Messages` (
`email` TEXT NOT NULL,

View File

@ -5,22 +5,24 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration54 : Migration(53, 54) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateResman(database)
removeTomaszowMazowieckiStudents(database)
override fun migrate(db: SupportSQLiteDatabase) {
migrateResman(db)
removeTomaszowMazowieckiStudents(db)
}
private fun migrateResman(database: SupportSQLiteDatabase) {
database.execSQL("""
private fun migrateResman(db: SupportSQLiteDatabase) {
db.execSQL(
"""
UPDATE Students SET
scrapper_base_url = 'https://vulcan.net.pl',
login_type = 'ADFSLightScoped',
symbol = 'rzeszowprojekt'
WHERE scrapper_base_url = 'https://resman.pl'
""".trimIndent())
""".trimIndent()
)
}
private fun removeTomaszowMazowieckiStudents(database: SupportSQLiteDatabase) {
database.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'")
private fun removeTomaszowMazowieckiStudents(db: SupportSQLiteDatabase) {
db.execSQL("DELETE FROM Students WHERE symbol = 'tomaszowmazowiecki'")
}
}

View File

@ -0,0 +1,10 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.DeleteColumn
import androidx.room.migration.AutoMigrationSpec
@DeleteColumn(
tableName = "AdminMessages",
columnName = "type",
)
class Migration57 : AutoMigrationSpec

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration6 : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS ReportingUnits (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,
@ -15,9 +16,11 @@ class Migration6 : Migration(5, 6) {
sender_id INTEGER NOT NULL,
sender_name TEXT NOT NULL,
roles TEXT NOT NULL)
""")
"""
)
database.execSQL("""
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Recipients (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,
@ -28,10 +31,11 @@ class Migration6 : Migration(5, 6) {
unit_id INTEGER NOT NULL,
role INTEGER NOT NULL,
hash TEXT NOT NULL)
""")
"""
)
database.execSQL("DELETE FROM Semesters WHERE 1")
database.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
database.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL")
db.execSQL("DELETE FROM Semesters WHERE 1")
db.execSQL("ALTER TABLE Semesters ADD COLUMN class_id INTEGER DEFAULT 0 NOT NULL")
db.execSQL("ALTER TABLE Semesters ADD COLUMN unit_id INTEGER DEFAULT 0 NOT NULL")
}
}

View File

@ -5,8 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration7 : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS GradesStatistics (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,
@ -15,6 +16,7 @@ class Migration7 : Migration(6, 7) {
grade INTEGER NOT NULL,
amount INTEGER NOT NULL,
is_semester INTEGER NOT NULL)
""")
"""
)
}
}

View File

@ -5,9 +5,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration8 : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL")
database.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL")
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE Timetable ADD COLUMN subjectOld TEXT DEFAULT \"\" NOT NULL")
db.execSQL("ALTER TABLE Timetable ADD COLUMN roomOld TEXT DEFAULT \"\" NOT NULL")
db.execSQL("ALTER TABLE Timetable ADD COLUMN teacherOld TEXT DEFAULT \"\" NOT NULL")
}
}

View File

@ -5,9 +5,10 @@ import androidx.sqlite.db.SupportSQLiteDatabase
class Migration9 : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("DROP TABLE IF EXISTS Messages")
database.execSQL("""
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("DROP TABLE IF EXISTS Messages")
db.execSQL(
"""
CREATE TABLE IF NOT EXISTS Messages (
id INTEGER PRIMARY KEY NOT NULL,
student_id INTEGER NOT NULL,

View File

@ -0,0 +1,9 @@
package io.github.wulkanowy.data.enums
enum class MessageType {
GENERAL_MESSAGE,
DASHBOARD_MESSAGE,
LOGIN_MESSAGE,
PASS_RESET_MESSAGE,
ERROR_OVERRIDE,
}

View File

@ -0,0 +1,11 @@
package io.github.wulkanowy.data.enums
enum class TimetableGapsMode(val value: String) {
NO_GAPS("no_gaps"),
BETWEEN_LESSONS("between"),
BETWEEN_AND_BEFORE_LESSONS("before_and_between");
companion object {
fun getByValue(value: String) = entries.find { it.value == value } ?: BETWEEN_LESSONS
}
}

View File

@ -0,0 +1,21 @@
package io.github.wulkanowy.data.pojos
import kotlinx.serialization.Serializable
@Serializable
data class LoginEvent(
val uuid: String,
val schoolName: String,
val schoolShort: String,
val schoolAddress: String,
val scraperBaseUrl: String,
val symbol: String,
val schoolId: String,
val loginType: String,
)
@Serializable
data class IntegrityRequest<T>(
val tokenString: String,
val data: T,
)

View File

@ -1,10 +1,11 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.api.AdminMessageService
import io.github.wulkanowy.data.db.dao.AdminMessageDao
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.utils.AppInfo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject
import javax.inject.Singleton
@ -13,34 +14,20 @@ import javax.inject.Singleton
class AdminMessageRepository @Inject constructor(
private val adminMessageService: AdminMessageService,
private val adminMessageDao: AdminMessageDao,
private val appInfo: AppInfo
) {
private val saveFetchResultMutex = Mutex()
suspend fun getAdminMessages(student: Student) = networkBoundResource(
mutex = saveFetchResultMutex,
isResultEmpty = { it == null },
query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() },
shouldFetch = { true },
saveFetchResult = { oldItems, newItems ->
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
},
showSavedOnLoading = false,
mapResult = { adminMessages ->
adminMessages.filter { adminMessage ->
val isCorrectRegister = adminMessage.targetRegisterHost?.let {
student.scrapperBaseUrl.contains(it, true)
} ?: true
val isCorrectFlavor =
adminMessage.targetFlavor?.equals(appInfo.buildFlavor, true) ?: true
val isCorrectMaxVersion =
adminMessage.versionMax?.let { it >= appInfo.versionCode } ?: true
val isCorrectMinVersion =
adminMessage.versionMin?.let { it <= appInfo.versionCode } ?: true
isCorrectRegister && isCorrectFlavor && isCorrectMaxVersion && isCorrectMinVersion
}.maxByOrNull { it.id }
}
)
fun getAdminMessages(): Flow<Resource<List<AdminMessage>>> =
networkBoundResource(
mutex = saveFetchResultMutex,
isResultEmpty = { false },
query = { adminMessageDao.loadAll() },
fetch = { adminMessageService.getAdminMessages() },
shouldFetch = { true },
saveFetchResult = { oldItems, newItems ->
adminMessageDao.removeOldAndSaveNew(oldItems, newItems)
},
showSavedOnLoading = false,
)
}

View File

@ -3,18 +3,26 @@ package io.github.wulkanowy.data.repositories
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.db.dao.MailboxDao
import io.github.wulkanowy.data.db.dao.MessageAttachmentDao
import io.github.wulkanowy.data.db.dao.MessagesDao
import io.github.wulkanowy.data.db.entities.*
import io.github.wulkanowy.data.db.entities.Mailbox
import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.enums.MessageFolder.TRASHED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.networkBoundResource
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.pojos.MessageDraft
import io.github.wulkanowy.data.waitForResult
import io.github.wulkanowy.domain.messages.GetMailboxByStudentUseCase
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
@ -25,7 +33,6 @@ import io.github.wulkanowy.utils.uniqueSubtract
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.sync.Mutex
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import timber.log.Timber
@ -97,7 +104,7 @@ class MessageRepository @Inject constructor(
shouldFetch = {
checkNotNull(it) { "This message no longer exist!" }
Timber.d("Message content in db empty: ${it.message.content.isBlank()}")
it.message.unread || it.message.content.isBlank()
(it.message.unread && markAsRead) || it.message.content.isBlank()
},
query = {
messagesDb.loadMessageWithAttachment(message.messageGlobalKey)
@ -113,7 +120,10 @@ class MessageRepository @Inject constructor(
messagesDb.updateAll(
listOf(old.message.apply {
id = message.id
unread = !markAsRead
unread = when {
markAsRead -> false
else -> unread
}
sender = new.sender
recipients = new.recipients.singleOrNull() ?: "Wielu adresatów"
content = content.ifBlank { new.content }
@ -123,7 +133,7 @@ class MessageRepository @Inject constructor(
items = new.attachments.mapToEntities(message.messageGlobalKey),
)
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read")
Timber.d("Message ${message.messageId} with blank content: ${old.message.content.isBlank()}, marked as read: $markAsRead")
}
)

View File

@ -15,7 +15,6 @@ import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import io.github.wulkanowy.ui.modules.settings.appearance.menuorder.AppMenuItem
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
@ -195,10 +194,12 @@ class PreferencesRepository @Inject constructor(
)
)
val showTimetableTimers: Boolean
get() = getBoolean(
R.string.pref_key_timetable_show_timers,
R.bool.pref_default_timetable_show_timers
val showTimetableGaps: TimetableGapsMode
get() = TimetableGapsMode.getByValue(
getString(
R.string.pref_key_timetable_show_gaps,
R.string.pref_default_timetable_show_gaps
)
)
val showSubjectsWithoutGrades: Boolean
@ -343,6 +344,12 @@ class PreferencesRepository @Inject constructor(
)
}
var isIncognitoMode: Boolean
get() = getBoolean(R.string.pref_key_incognito_moge, R.bool.pref_default_incognito_mode)
set(value) = sharedPref.edit {
putBoolean(context.getString(R.string.pref_key_incognito_moge), value)
}
var installationId: String
get() = sharedPref.getString(PREF_KEY_INSTALLATION_ID, null).orEmpty()
private set(value) = sharedPref.edit { putString(PREF_KEY_INSTALLATION_ID, value) }

View File

@ -0,0 +1,68 @@
package io.github.wulkanowy.data.repositories
import io.github.wulkanowy.data.api.SchoolsService
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.pojos.IntegrityRequest
import io.github.wulkanowy.data.pojos.LoginEvent
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.utils.IntegrityHelper
import io.github.wulkanowy.utils.getCurrentOrLast
import io.github.wulkanowy.utils.init
import kotlinx.coroutines.withTimeout
import timber.log.Timber
import java.util.UUID
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration.Companion.seconds
@Singleton
class SchoolsRepository @Inject constructor(
private val integrityHelper: IntegrityHelper,
private val schoolsService: SchoolsService,
private val sdk: Sdk,
) {
suspend fun logSchoolLogin(loginData: LoginData, students: List<StudentWithSemesters>) {
students.forEach {
runCatching {
withTimeout(10.seconds) {
logLogin(loginData, it.student, it.semesters.getCurrentOrLast())
}
}
.onFailure { Timber.e(it) }
}
}
private suspend fun logLogin(loginData: LoginData, student: Student, semester: Semester) {
val requestId = UUID.randomUUID().toString()
val token = integrityHelper.getIntegrityToken(requestId) ?: return
val schoolInfo = sdk
.init(student.copy(password = loginData.password))
.switchDiary(
diaryId = semester.diaryId,
kindergartenDiaryId = semester.kindergartenDiaryId,
schoolYear = semester.schoolYear
)
.getSchool()
schoolsService.logLoginEvent(
IntegrityRequest(
tokenString = token,
data = LoginEvent(
uuid = requestId,
schoolAddress = schoolInfo.address,
schoolName = schoolInfo.name,
schoolShort = student.schoolShortName,
scraperBaseUrl = student.scrapperBaseUrl,
loginType = student.loginType,
symbol = student.symbol,
schoolId = student.schoolSymbol,
)
)
)
}
}

View File

@ -41,7 +41,7 @@ class SemesterRepository @Inject constructor(
val isRefreshOnModeChangeRequired = when {
Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE -> {
semesters.firstOrNull { it.isCurrent }?.let {
semesters.firstOrNull { it.isCurrent() }?.let {
0 == it.diaryId && 0 == it.kindergartenDiaryId
} == true
}
@ -49,7 +49,7 @@ class SemesterRepository @Inject constructor(
}
val isRefreshOnNoCurrentAppropriate =
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent }
refreshOnNoCurrent && !semesters.any { semester -> semester.isCurrent() }
return forceRefresh || isNoSemesters || isRefreshOnModeChangeRequired || isRefreshOnNoCurrentAppropriate
}

View File

@ -62,20 +62,28 @@ class StudentRepository @Inject constructor(
.getStudentsHybrid(email, password, scrapperBaseUrl, "", symbol)
.mapToPojo(password)
suspend fun getSavedStudents(decryptPass: Boolean = true) =
studentDb.loadStudentsWithSemesters()
.map {
it.apply {
suspend fun getSavedStudents(decryptPass: Boolean = true): List<StudentWithSemesters> {
return studentDb.loadStudentsWithSemesters().map { (student, semesters) ->
StudentWithSemesters(
student = student.apply {
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)
}
}
}
}
},
semesters = semesters,
)
}
}
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true) =
studentDb.loadStudentWithSemestersById(id)?.apply {
suspend fun getSavedStudentById(id: Long, decryptPass: Boolean = true): StudentWithSemesters? =
studentDb.loadStudentWithSemestersById(id).let { res ->
StudentWithSemesters(
student = res.keys.firstOrNull() ?: return null,
semesters = res.values.first(),
)
}.apply {
if (decryptPass && Sdk.Mode.valueOf(student.loginMode) != Sdk.Mode.HEBE) {
student.password = withContext(dispatchers.io) {
decrypt(student.password)

View File

@ -0,0 +1,64 @@
package io.github.wulkanowy.domain.adminmessage
import io.github.wulkanowy.data.Resource
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageType
import io.github.wulkanowy.data.mapResourceData
import io.github.wulkanowy.data.repositories.AdminMessageRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.utils.AppInfo
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
class GetAppropriateAdminMessageUseCase @Inject constructor(
private val adminMessageRepository: AdminMessageRepository,
private val preferencesRepository: PreferencesRepository,
private val appInfo: AppInfo
) {
operator fun invoke(student: Student, type: MessageType): Flow<Resource<AdminMessage?>> {
return invoke(student.scrapperBaseUrl, type)
}
operator fun invoke(scrapperBaseUrl: String, type: MessageType): Flow<Resource<AdminMessage?>> {
return adminMessageRepository.getAdminMessages().mapResourceData { adminMessages ->
adminMessages
.asSequence()
.filter { it.isNotDismissed() }
.filter { it.isVersionMatch() }
.filter { it.isRegisterHostMatch(scrapperBaseUrl) }
.filter { it.isFlavorMatch() }
.filter { it.isTypeMatch(type) }
.maxByOrNull { it.id }
}
}
private fun AdminMessage.isNotDismissed(): Boolean {
return id !in preferencesRepository.dismissedAdminMessageIds
}
private fun AdminMessage.isRegisterHostMatch(scrapperBaseUrl: String): Boolean {
return targetRegisterHost?.let {
scrapperBaseUrl.contains(it, true)
} ?: true
}
private fun AdminMessage.isFlavorMatch(): Boolean {
return targetFlavor?.equals(appInfo.buildFlavor, true) ?: true
}
private fun AdminMessage.isVersionMatch(): Boolean {
val isCorrectMaxVersion = versionMax?.let { it >= appInfo.versionCode } ?: true
val isCorrectMinVersion = versionMin?.let { it <= appInfo.versionCode } ?: true
return isCorrectMaxVersion && isCorrectMinVersion
}
private fun AdminMessage.isTypeMatch(messageType: MessageType): Boolean {
if (messageType in types) return true
if (MessageType.GENERAL_MESSAGE in types) return true
return false
}
}

View File

@ -4,6 +4,7 @@ import android.content.Intent
import android.widget.RemoteViewsService
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.data.db.SharedPrefProvider
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
@ -26,10 +27,19 @@ class TimetableWidgetService : RemoteViewsService() {
@Inject
lateinit var sharedPref: SharedPrefProvider
@Inject
lateinit var prefRepository: PreferencesRepository
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
Timber.d("TimetableWidgetFactory created")
return TimetableWidgetFactory(
timetableRepo, studentRepo, semesterRepo, sharedPref, applicationContext, intent
timetableRepository = timetableRepo,
studentRepository = studentRepo,
semesterRepository = semesterRepo,
sharedPref = sharedPref,
prefRepository = prefRepository,
context = applicationContext,
intent = intent,
)
}
}

View File

@ -148,6 +148,10 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
binding.attendanceNavDate.text = date
}
override fun showNavigation(show: Boolean) {
binding.attendanceNavContainer.isVisible = show
}
override fun clearData() {
with(attendanceAdapter) {
items = emptyList()
@ -281,7 +285,9 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(SAVED_DATE_KEY, presenter.currentDate.toEpochDay())
presenter.currentDate?.let {
outState.putLong(SAVED_DATE_KEY, it.toEpochDay())
}
}
override fun onDestroyView() {

View File

@ -3,10 +3,14 @@ package io.github.wulkanowy.ui.modules.attendance
import android.annotation.SuppressLint
import io.github.wulkanowy.data.*
import io.github.wulkanowy.data.db.entities.Attendance
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.repositories.AttendanceRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.repositories.TimetableRepository
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.*
@ -14,6 +18,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDate.now
import java.time.LocalDate.ofEpochDay
@ -28,9 +33,10 @@ class AttendancePresenter @Inject constructor(
private val analytics: AnalyticsHelper
) : BasePresenter<AttendanceView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().previousOrSameSchoolDay
private var initialDate: LocalDate? = null
private var isWeekendHasLessons: Boolean = false
lateinit var currentDate: LocalDate
var currentDate: LocalDate? = null
private set
private lateinit var lastError: Throwable
@ -44,27 +50,34 @@ class AttendancePresenter @Inject constructor(
view.initView()
Timber.i("Attendance view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
currentDate = date?.let(::ofEpochDay)
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
}
fun onPreviousDay() {
val date = if (isWeekendHasLessons) {
currentDate?.minusDays(1)
} else currentDate?.previousSchoolDay
view?.finishActionMode()
attendanceToExcuseList.clear()
reloadView(currentDate.previousSchoolDay)
reloadView(date ?: return)
loadData()
}
fun onNextDay() {
val date = if (isWeekendHasLessons) {
currentDate?.plusDays(1)
} else currentDate?.nextSchoolDay
view?.finishActionMode()
attendanceToExcuseList.clear()
reloadView(currentDate.nextSchoolDay)
reloadView(date ?: return)
loadData()
}
fun onPickDate() {
view?.showDatePickerDialog(currentDate)
view?.showDatePickerDialog(currentDate ?: return)
}
fun onDateSet(year: Int, month: Int, day: Int) {
@ -93,10 +106,8 @@ class AttendancePresenter @Inject constructor(
Timber.i("Attendance view is reselected")
view?.let { view ->
if (view.currentStackSize == 1) {
baseDate = now().previousOrSameSchoolDay
if (currentDate != baseDate) {
reloadView(baseDate)
if (currentDate != initialDate) {
reloadView(initialDate ?: return)
loadData()
} else if (!view.isViewEmpty) {
view.resetView()
@ -188,19 +199,6 @@ class AttendancePresenter @Inject constructor(
return true
}
private fun setBaseDateOnHolidays() {
flow {
val student = studentRepository.getCurrentStudent()
emit(semesterRepository.getCurrentSemester(student))
}.catch {
Timber.i("Loading semester result: An exception occurred")
}.onEach {
baseDate = baseDate.getLastSchoolDayIfHoliday(it.schoolYear)
currentDate = baseDate
reloadNavigation()
}.launch("holidays")
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading attendance data started")
@ -211,11 +209,13 @@ class AttendancePresenter @Inject constructor(
isParent = student.isParent
val semester = semesterRepository.getCurrentSemester(student)
checkInitialAndCurrentDate(student, semester)
attendanceRepository.getAttendance(
student = student,
semester = semester,
start = currentDate,
end = currentDate,
start = currentDate ?: now(),
end = currentDate ?: now(),
forceRefresh = forceRefresh
)
}
@ -231,6 +231,8 @@ class AttendancePresenter @Inject constructor(
}.sortedBy { item -> item.number }
}
.onResourceData {
isWeekendHasLessons = isWeekendHasLessons || isWeekendHasLessons(it)
view?.run {
enableSwipe(true)
showProgress(false)
@ -238,6 +240,7 @@ class AttendancePresenter @Inject constructor(
showEmpty(it.isEmpty())
showContent(it.isNotEmpty())
updateData(it)
reloadNavigation()
}
}
.onResourceIntermediate { view?.showRefresh(true) }
@ -263,6 +266,43 @@ class AttendancePresenter @Inject constructor(
.launch()
}
private suspend fun checkInitialAndCurrentDate(student: Student, semester: Semester) {
if (initialDate == null) {
val lessons = attendanceRepository.getAttendance(
student = student,
semester = semester,
start = now().monday,
end = now().sunday,
forceRefresh = false,
).toFirstResult().dataOrNull.orEmpty()
isWeekendHasLessons = isWeekendHasLessons(lessons)
initialDate = getInitialDate(semester)
}
if (currentDate == null) {
currentDate = initialDate
}
}
private fun isWeekendHasLessons(
lessons: List<Attendance>,
): Boolean = lessons.any {
it.date.dayOfWeek in listOf(
DayOfWeek.SATURDAY,
DayOfWeek.SUNDAY,
)
}
private fun getInitialDate(semester: Semester): LocalDate {
val now = now()
return when {
now.isHolidays -> now.getLastSchoolDayIfHoliday(semester.schoolYear)
isWeekendHasLessons -> now
else -> now.previousOrSameSchoolDay
}
}
private fun excuseAbsence(reason: String?, toExcuseList: List<Attendance>) {
resourceFlow {
val student = studentRepository.getCurrentStudent()
@ -311,7 +351,7 @@ class AttendancePresenter @Inject constructor(
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}")
Timber.i("Reload attendance view with the date ${currentDate?.toFormattedString()}")
view?.apply {
showProgress(true)
enableSwipe(false)
@ -326,10 +366,13 @@ class AttendancePresenter @Inject constructor(
@SuppressLint("DefaultLocale")
private fun reloadNavigation() {
val currentDate = currentDate ?: return
view?.apply {
showPreButton(!currentDate.minusDays(1).isHolidays)
showNextButton(!currentDate.plusDays(1).isHolidays)
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
showNavigation(true)
}
}
}

View File

@ -40,6 +40,8 @@ interface AttendanceView : BaseView {
fun showContent(show: Boolean)
fun showNavigation(show: Boolean)
fun showPreButton(show: Boolean)
fun showNextButton(show: Boolean)

View File

@ -3,6 +3,7 @@ package io.github.wulkanowy.ui.modules.dashboard
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.ui.modules.dashboard.adapters.DashboardAdapter
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
import java.util.*
class DashboardItemMoveCallback(
@ -55,5 +56,5 @@ class DashboardItemMoveCallback(
}
private val RecyclerView.ViewHolder.isAdminMessageOrAccountItem: Boolean
get() = this is DashboardAdapter.AdminMessageViewHolder || this is DashboardAdapter.AccountViewHolder
get() = this is AdminMessageViewHolder || this is DashboardAdapter.AccountViewHolder
}

View File

@ -5,7 +5,9 @@ import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.db.entities.LuckyNumber
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageType
import io.github.wulkanowy.data.repositories.*
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.base.ErrorHandler
import io.github.wulkanowy.utils.AdsHelper
@ -32,7 +34,7 @@ class DashboardPresenter @Inject constructor(
private val conferenceRepository: ConferenceRepository,
private val preferencesRepository: PreferencesRepository,
private val schoolAnnouncementRepository: SchoolAnnouncementRepository,
private val adminMessageRepository: AdminMessageRepository,
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase,
private val adsHelper: AdsHelper
) : BasePresenter<DashboardView>(errorHandler, studentRepository) {
@ -159,19 +161,23 @@ class DashboardPresenter @Inject constructor(
DashboardItem.Type.ACCOUNT -> {
updateData(DashboardItem.Account(student), forceRefresh)
}
DashboardItem.Type.HORIZONTAL_GROUP -> {
loadHorizontalGroup(student, forceRefresh)
}
DashboardItem.Type.LESSONS -> loadLessons(student, forceRefresh)
DashboardItem.Type.GRADES -> loadGrades(student, forceRefresh)
DashboardItem.Type.HOMEWORK -> loadHomework(student, forceRefresh)
DashboardItem.Type.ANNOUNCEMENTS -> {
loadSchoolAnnouncements(student, forceRefresh)
}
DashboardItem.Type.EXAMS -> loadExams(student, forceRefresh)
DashboardItem.Type.CONFERENCES -> {
loadConferences(student, forceRefresh)
}
DashboardItem.Type.ADS -> loadAds(forceRefresh)
DashboardItem.Type.ADMIN_MESSAGE -> loadAdminMessage(student, forceRefresh)
}
@ -355,6 +361,7 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.GRADES
}
}
is Resource.Success -> {
Timber.i("Loading dashboard grades result: Success")
updateData(
@ -365,6 +372,7 @@ class DashboardPresenter @Inject constructor(
forceRefresh
)
}
is Resource.Error -> {
Timber.i("Loading dashboard grades result: An exception occurred")
errorHandler.dispatch(it.error)
@ -378,7 +386,7 @@ class DashboardPresenter @Inject constructor(
private fun loadLessons(student: Student, forceRefresh: Boolean) {
flatResourceFlow {
val semester = semesterRepository.getCurrentSemester(student)
val date = LocalDate.now().nextOrSameSchoolDay
val date = LocalDate.now()
timetableRepository.getTimetable(
student = student,
@ -402,12 +410,14 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.LESSONS
}
}
is Resource.Success -> {
Timber.i("Loading dashboard lessons result: Success")
updateData(
DashboardItem.Lessons(it.data), forceRefresh
)
}
is Resource.Error -> {
Timber.i("Loading dashboard lessons result: An exception occurred")
errorHandler.dispatch(it.error)
@ -457,10 +467,12 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.HOMEWORK
}
}
is Resource.Success -> {
Timber.i("Loading dashboard homework result: Success")
updateData(DashboardItem.Homework(it.data), forceRefresh)
}
is Resource.Error -> {
Timber.i("Loading dashboard homework result: An exception occurred")
errorHandler.dispatch(it.error)
@ -489,10 +501,12 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.ANNOUNCEMENTS
}
}
is Resource.Success -> {
Timber.i("Loading dashboard announcements result: Success")
updateData(DashboardItem.Announcements(it.data), forceRefresh)
}
is Resource.Error -> {
Timber.i("Loading dashboard announcements result: An exception occurred")
errorHandler.dispatch(it.error)
@ -530,10 +544,12 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.EXAMS
}
}
is Resource.Success -> {
Timber.i("Loading dashboard exams result: Success")
updateData(DashboardItem.Exams(it.data), forceRefresh)
}
is Resource.Error -> {
Timber.i("Loading dashboard exams result: An exception occurred")
errorHandler.dispatch(it.error)
@ -569,10 +585,12 @@ class DashboardPresenter @Inject constructor(
firstLoadedItemList += DashboardItem.Type.CONFERENCES
}
}
is Resource.Success -> {
Timber.i("Loading dashboard conferences result: Success")
updateData(DashboardItem.Conferences(it.data), forceRefresh)
}
is Resource.Error -> {
Timber.i("Loading dashboard conferences result: An exception occurred")
errorHandler.dispatch(it.error)
@ -584,12 +602,12 @@ class DashboardPresenter @Inject constructor(
}
private fun loadAdminMessage(student: Student, forceRefresh: Boolean) {
flatResourceFlow { adminMessageRepository.getAdminMessages(student) }
.filter {
val data = it.dataOrNull ?: return@filter true
val isDismissed = data.id in preferencesRepository.dismissedAdminMessageIds
!isDismissed
}
flatResourceFlow {
getAppropriateAdminMessageUseCase(
student = student,
type = MessageType.DASHBOARD_MESSAGE,
)
}
.onEach {
when (it) {
is Resource.Loading -> {
@ -597,6 +615,7 @@ class DashboardPresenter @Inject constructor(
if (forceRefresh) return@onEach
updateData(DashboardItem.AdminMessages(), forceRefresh)
}
is Resource.Success -> {
Timber.i("Loading dashboard admin message result: Success")
updateData(
@ -604,6 +623,7 @@ class DashboardPresenter @Inject constructor(
forceRefresh = forceRefresh
)
}
is Resource.Error -> {
Timber.i("Loading dashboard admin message result: An exception occurred")
Timber.e(it.error)

View File

@ -1,8 +1,6 @@
package io.github.wulkanowy.ui.modules.dashboard.adapters
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.Typeface
import android.os.Handler
import android.os.Looper
@ -24,6 +22,7 @@ import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.databinding.*
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
import io.github.wulkanowy.utils.*
import timber.log.Timber
import java.time.Duration
@ -109,7 +108,9 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
ItemDashboardConferencesBinding.inflate(inflater, parent, false)
)
DashboardItem.Type.ADMIN_MESSAGE.ordinal -> AdminMessageViewHolder(
ItemDashboardAdminMessageBinding.inflate(inflater, parent, false)
ItemDashboardAdminMessageBinding.inflate(inflater, parent, false),
onAdminMessageDismissClickListener = onAdminMessageDismissClickListener,
onAdminMessageClickListener = onAdminMessageClickListener,
)
DashboardItem.Type.ADS.ordinal -> AdsViewHolder(
ItemDashboardAdsBinding.inflate(inflater, parent, false)
@ -128,7 +129,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
is AnnouncementsViewHolder -> bindAnnouncementsViewHolder(holder, position)
is ExamsViewHolder -> bindExamsViewHolder(holder, position)
is ConferencesViewHolder -> bindConferencesViewHolder(holder, position)
is AdminMessageViewHolder -> bindAdminMessage(holder, position)
is AdminMessageViewHolder -> holder.bind((items[position] as DashboardItem.AdminMessages).adminMessage)
is AdsViewHolder -> bindAdsViewHolder(holder, position)
}
}
@ -733,39 +734,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
}
private fun bindAdminMessage(adminMessageViewHolder: AdminMessageViewHolder, position: Int) {
val item = (items[position] as DashboardItem.AdminMessages).adminMessage ?: return
val context = adminMessageViewHolder.binding.root.context
val (backgroundColor, textColor) = when (item.priority) {
"HIGH" -> {
context.getThemeAttrColor(R.attr.colorMessageHigh) to
context.getThemeAttrColor(R.attr.colorOnMessageHigh)
}
"MEDIUM" -> {
context.getThemeAttrColor(R.attr.colorMessageMedium) to Color.BLACK
}
else -> null to context.getThemeAttrColor(R.attr.colorOnSurface)
}
with(adminMessageViewHolder.binding) {
dashboardAdminMessageItemTitle.text = item.title
dashboardAdminMessageItemTitle.setTextColor(textColor)
dashboardAdminMessageItemDescription.text = item.content
dashboardAdminMessageItemDescription.setTextColor(textColor)
dashboardAdminMessageItemIcon.setColorFilter(textColor)
dashboardAdminMessageItemDismiss.isVisible = item.isDismissible
dashboardAdminMessageItemDismiss.setTextColor(textColor)
dashboardAdminMessageItemDismiss.setOnClickListener {
onAdminMessageDismissClickListener(item)
}
root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) })
item.destinationUrl?.let { url ->
root.setOnClickListener { onAdminMessageClickListener(url) }
}
}
}
private fun bindAdsViewHolder(adsViewHolder: AdsViewHolder, position: Int) {
val item = (items[position] as DashboardItem.Ads).adBanner ?: return
val binding = adsViewHolder.binding
@ -819,9 +787,6 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
val adapter by lazy { DashboardConferencesAdapter() }
}
class AdminMessageViewHolder(val binding: ItemDashboardAdminMessageBinding) :
RecyclerView.ViewHolder(binding.root)
class AdsViewHolder(val binding: ItemDashboardAdsBinding) :
RecyclerView.ViewHolder(binding.root)

View File

@ -0,0 +1,52 @@
package io.github.wulkanowy.ui.modules.dashboard.viewholders
import android.content.res.ColorStateList
import android.graphics.Color
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.databinding.ItemDashboardAdminMessageBinding
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.utils.getThemeAttrColor
class AdminMessageViewHolder(
private val binding: ItemDashboardAdminMessageBinding,
private val onAdminMessageDismissClickListener: (AdminMessage) -> Unit,
private val onAdminMessageClickListener: (String?) -> Unit,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: AdminMessage?) {
item ?: return
val context = binding.root.context
val (backgroundColor, textColor) = when (item.priority) {
"HIGH" -> {
context.getThemeAttrColor(R.attr.colorMessageHigh) to
context.getThemeAttrColor(R.attr.colorOnMessageHigh)
}
"MEDIUM" -> {
context.getThemeAttrColor(R.attr.colorMessageMedium) to Color.BLACK
}
else -> null to context.getThemeAttrColor(R.attr.colorOnSurface)
}
with(binding) {
dashboardAdminMessageItemTitle.text = item.title
dashboardAdminMessageItemTitle.setTextColor(textColor)
dashboardAdminMessageItemDescription.text = item.content
dashboardAdminMessageItemDescription.setTextColor(textColor)
dashboardAdminMessageItemIcon.setColorFilter(textColor)
dashboardAdminMessageItemDismiss.isVisible = item.isDismissible
dashboardAdminMessageItemDismiss.setTextColor(textColor)
dashboardAdminMessageItemDismiss.setOnClickListener {
onAdminMessageDismissClickListener(item)
}
root.setCardBackgroundColor(backgroundColor?.let { ColorStateList.valueOf(it) })
item.destinationUrl?.let { url ->
root.setOnClickListener { onAdminMessageClickListener(url) }
}
}
}
}

View File

@ -58,7 +58,7 @@ class GradeAverageProvider @Inject constructor(
when (params.gradeAverageMode) {
ONE_SEMESTER -> getGradeSubjects(
student = student,
semester = semesters.single { it.semesterId == semesterId },
semester = semesters.first { it.semesterId == semesterId },
forceRefresh = forceRefresh,
params = params,
)

View File

@ -22,6 +22,8 @@ import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.roundToInt
class GradeStatisticsAdapter @Inject constructor() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@ -269,7 +271,7 @@ class GradeStatisticsAdapter @Inject constructor() :
valueTextSize = 12f
valueTextColor = binding.root.context.getThemeAttrColor(android.R.attr.textColorPrimary)
valueFormatter = object : ValueFormatter() {
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}%"
override fun getBarLabel(barEntry: BarEntry) = "${barEntry.y}"
}
colors = gradePointsColors
}
@ -304,15 +306,20 @@ class GradeStatisticsAdapter @Inject constructor() :
}
xAxis.setDrawLabels(false)
xAxis.setDrawGridLines(false)
val yMaxFromValues = (max(points.others, points.student)).roundToInt() + 30f
val yMaxFromValuesWithMargin = ((yMaxFromValues / 10.0).roundToInt() * 10).toFloat()
val yMax = yMaxFromValuesWithMargin.coerceAtLeast(100f)
val yLabelCount = (yMax / 10).toInt() + 1
with(axisLeft) {
axisMinimum = 0f
axisMaximum = 100f
labelCount = 11
axisMaximum = yMax
labelCount = yLabelCount
}
with(axisRight) {
axisMinimum = 0f
axisMaximum = 100f
labelCount = 11
axisMaximum = yMax
labelCount = yLabelCount
}
invalidate()
}

View File

@ -42,7 +42,7 @@ class GradeSummaryPresenter @Inject constructor(
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}
.logResourceStatus("load grade summary", showData = true)
.logResourceStatus("load grade summary")
.mapResourceData { createGradeSummaryItems(it) }
.onResourceData {
view?.run {

View File

@ -23,7 +23,7 @@ import io.github.wulkanowy.ui.modules.login.symbol.LoginSymbolFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.notifications.NotificationsFragment
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.UpdateHelper
import io.github.wulkanowy.utils.InAppUpdateHelper
import javax.inject.Inject
@AndroidEntryPoint
@ -33,7 +33,7 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
override lateinit var presenter: LoginPresenter
@Inject
lateinit var updateHelper: UpdateHelper
lateinit var inAppUpdateHelper: InAppUpdateHelper
@Inject
lateinit var appInfo: AppInfo
@ -47,10 +47,10 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
setContentView(ActivityLoginBinding.inflate(layoutInflater).apply { binding = this }.root)
setSupportActionBar(binding.loginToolbar)
messageContainer = binding.loginContainer
updateHelper.messageContainer = binding.loginContainer
inAppUpdateHelper.messageContainer = binding.loginContainer
presenter.onAttachView(this)
updateHelper.checkAndInstallUpdates(this)
inAppUpdateHelper.checkAndInstallUpdates()
if (savedInstanceState == null) {
openFragment(LoginFormFragment.newInstance(), clearBackStack = true)
@ -117,14 +117,6 @@ class LoginActivity : BaseActivity<LoginPresenter, ActivityLoginBinding>(), Logi
override fun onResume() {
super.onResume()
updateHelper.onResume(this)
}
//https://developer.android.com/guide/playcore/in-app-updates#status_callback
@Deprecated("Deprecated in Java")
@Suppress("DEPRECATION")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
updateHelper.onActivityResult(requestCode, resultCode)
inAppUpdateHelper.onResume()
}
}

View File

@ -9,12 +9,16 @@ import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import dagger.hilt.android.AndroidEntryPoint
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.databinding.FragmentLoginFormBinding
import io.github.wulkanowy.ui.base.BaseFragment
import io.github.wulkanowy.ui.modules.dashboard.viewholders.AdminMessageViewHolder
import io.github.wulkanowy.ui.modules.login.LoginActivity
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.support.LoginSupportDialog
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
import io.github.wulkanowy.utils.*
import javax.inject.Inject
@ -182,7 +186,9 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
override fun clearPassError() {
binding.loginFormPassLayout.error = null
binding.loginFormPassLayout.setEndIconTintList(null)
binding.loginFormPassLayout.setEndIconTintList(
requireContext().getAttrColorStateList(R.attr.colorOnSurface)
)
binding.loginFormErrorBox.isVisible = false
}
@ -207,6 +213,19 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
binding.loginFormContainer.visibility = if (show) VISIBLE else GONE
}
override fun showAdminMessage(message: AdminMessage?) {
AdminMessageViewHolder(
binding = binding.loginFormMessage,
onAdminMessageDismissClickListener = presenter::onAdminMessageDismissed,
onAdminMessageClickListener = presenter::onAdminMessageSelected,
).bind(message)
binding.loginFormMessage.root.isVisible = message != null
}
override fun openInternetBrowser(url: String) {
requireContext().openInternetBrowser(url)
}
override fun showDomainSuffixInput(show: Boolean) {
binding.loginFormDomainSuffixLayout.isVisible = show
}
@ -221,8 +240,7 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
}
override fun showContact(show: Boolean) {
binding.loginFormContact.visibility = if (show) VISIBLE else GONE
binding.loginFormRecoverLink.visibility = if (show) GONE else VISIBLE
binding.loginFormContact.isVisible = show
}
override fun openPrivacyPolicyPage() {
@ -266,20 +284,7 @@ class LoginFormFragment : BaseFragment<FragmentLoginFormBinding>(R.layout.fragme
presenter.updateCustomDomainSuffixVisibility()
}
override fun openEmail(lastError: String) {
context?.openEmailClient(
chooserTitle = requireContext().getString(R.string.login_email_intent_title),
email = "wulkanowyinc@gmail.com",
subject = requireContext().getString(R.string.login_email_subject),
body = requireContext().getString(
R.string.login_email_text,
"${appInfo.systemManufacturer} ${appInfo.systemModel}",
appInfo.systemVersion.toString(),
"${appInfo.versionName}-${appInfo.buildFlavor}",
"$formHostValue/$formHostSymbol",
preferencesRepository.installationId,
lastError
)
)
override fun openEmail(supportInfo: LoginSupportInfo) {
LoginSupportDialog.newInstance(supportInfo).show(childFragmentManager, "support_dialog")
}
}

View File

@ -1,16 +1,23 @@
package io.github.wulkanowy.ui.modules.login.form
import androidx.core.net.toUri
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.enums.MessageType
import io.github.wulkanowy.data.flatResourceFlow
import io.github.wulkanowy.data.logResourceStatus
import io.github.wulkanowy.data.onResourceData
import io.github.wulkanowy.data.onResourceError
import io.github.wulkanowy.data.onResourceLoading
import io.github.wulkanowy.data.onResourceNotLoading
import io.github.wulkanowy.data.onResourceSuccess
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.data.resourceFlow
import io.github.wulkanowy.domain.adminmessage.GetAppropriateAdminMessageUseCase
import io.github.wulkanowy.ui.base.BasePresenter
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.ifNullOrBlank
@ -22,7 +29,9 @@ class LoginFormPresenter @Inject constructor(
studentRepository: StudentRepository,
private val loginErrorHandler: LoginErrorHandler,
private val appInfo: AppInfo,
private val analytics: AnalyticsHelper
private val analytics: AnalyticsHelper,
private val getAppropriateAdminMessageUseCase: GetAppropriateAdminMessageUseCase,
private val preferencesRepository: PreferencesRepository,
) : BasePresenter<LoginFormView>(loginErrorHandler, studentRepository) {
private var lastError: Throwable? = null
@ -41,6 +50,31 @@ class LoginFormPresenter @Inject constructor(
Timber.i("Entered wrong username or password")
}
}
reloadAdminMessage()
}
private fun reloadAdminMessage() {
flatResourceFlow {
getAppropriateAdminMessageUseCase(
scrapperBaseUrl = view?.formHostValue.orEmpty(),
type = MessageType.LOGIN_MESSAGE,
)
}
.logResourceStatus("load login admin message")
.onResourceData { view?.showAdminMessage(it) }
.onResourceError { view?.showAdminMessage(null) }
.launch()
}
fun onAdminMessageSelected(url: String?) {
url?.let { view?.openInternetBrowser(it) }
}
fun onAdminMessageDismissed(adminMessage: AdminMessage) {
preferencesRepository.dismissedAdminMessageIds += adminMessage.id
view?.showAdminMessage(null)
}
fun onPrivacyLinkClick() {
@ -63,6 +97,7 @@ class LoginFormPresenter @Inject constructor(
}
updateCustomDomainSuffixVisibility()
updateUsernameLabel()
reloadAdminMessage()
}
}
@ -99,22 +134,36 @@ class LoginFormPresenter @Inject constructor(
}
}
fun onSignInClick() {
private fun getLoginData(): LoginData {
val email = view?.formUsernameValue.orEmpty().trim()
val password = view?.formPassValue.orEmpty().trim()
val host = view?.formHostValue.orEmpty().trim()
val domainSuffix = view?.formDomainSuffix.orEmpty().trim()
val domainSuffix = view?.formDomainSuffix.orEmpty().trim().takeIf {
"customSuffix" in host
}.orEmpty()
val symbol = view?.formHostSymbol.orEmpty().trim()
if (!validateCredentials(email, password, host)) return
return LoginData(
login = email,
password = password,
baseUrl = host,
domainSuffix = domainSuffix,
symbol = symbol
)
}
fun onSignInClick() {
val loginData = getLoginData()
if (!validateCredentials(loginData.login, loginData.password, loginData.baseUrl)) return
resourceFlow {
studentRepository.getUserSubjectsFromScrapper(
email = email,
password = password,
scrapperBaseUrl = host,
domainSuffix = domainSuffix,
symbol = symbol
email = loginData.login,
password = loginData.password,
scrapperBaseUrl = loginData.baseUrl,
domainSuffix = loginData.domainSuffix,
symbol = loginData.symbol.orEmpty(),
)
}
.logResourceStatus("login")
@ -126,7 +175,6 @@ class LoginFormPresenter @Inject constructor(
}
}
.onResourceSuccess {
val loginData = LoginData(email, password, host, domainSuffix, symbol)
when (it.symbols.size) {
0 -> view?.navigateToSymbol(loginData)
else -> view?.navigateToStudentSelect(loginData, it)
@ -134,7 +182,7 @@ class LoginFormPresenter @Inject constructor(
analytics.logEvent(
"registration_form",
"success" to true,
"scrapperBaseUrl" to host,
"scrapperBaseUrl" to loginData.baseUrl,
"error" to "No error"
)
}
@ -151,7 +199,7 @@ class LoginFormPresenter @Inject constructor(
analytics.logEvent(
"registration_form",
"success" to false,
"scrapperBaseUrl" to host,
"scrapperBaseUrl" to loginData.baseUrl,
"error" to it.message.ifNullOrBlank { "No message" }
)
}
@ -163,7 +211,14 @@ class LoginFormPresenter @Inject constructor(
}
fun onEmailClick() {
view?.openEmail(lastError?.message.ifNullOrBlank { "none" })
view?.openEmail(
LoginSupportInfo(
loginData = getLoginData(),
lastErrorMessage = lastError?.message,
registerUser = null,
enteredSymbol = null,
)
)
}
fun onRecoverClick() {

View File

@ -1,8 +1,10 @@
package io.github.wulkanowy.ui.modules.login.form
import io.github.wulkanowy.data.db.entities.AdminMessage
import io.github.wulkanowy.data.pojos.RegisterUser
import io.github.wulkanowy.ui.base.BaseView
import io.github.wulkanowy.ui.modules.login.LoginData
import io.github.wulkanowy.ui.modules.login.support.LoginSupportInfo
interface LoginFormView : BaseView {
@ -58,6 +60,10 @@ interface LoginFormView : BaseView {
fun showContent(show: Boolean)
fun showAdminMessage(message: AdminMessage?)
fun openInternetBrowser(url: String)
fun showDomainSuffixInput(show: Boolean)
fun showOtherOptionsButton(show: Boolean)
@ -74,7 +80,7 @@ interface LoginFormView : BaseView {
fun openFaqPage()
fun openEmail(lastError: String)
fun openEmail(supportInfo: LoginSupportInfo)
fun openAdvancedLogin()

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